source: trunk/src/org/expeditee/stats/SessionStats.java@ 919

Last change on this file since 919 was 919, checked in by jts21, 10 years ago

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

File size: 14.5 KB
Line 
1/**
2 * SessionStats.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.stats;
20
21import java.awt.event.KeyEvent;
22import java.awt.event.MouseEvent;
23import java.sql.Time;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.Date;
27import java.util.List;
28
29import org.expeditee.gui.DisplayIO;
30import org.expeditee.gui.Frame;
31import org.expeditee.gui.FrameMouseActions;
32import org.expeditee.gui.FrameUtils;
33import org.expeditee.gui.TimeKeeper;
34import org.expeditee.items.Dot;
35import org.expeditee.items.Item;
36import org.expeditee.items.Line;
37import org.expeditee.items.Picture;
38import org.expeditee.items.Text;
39
40public class SessionStats extends Stats {
41
42 public enum ItemType {
43 Text, Dot, Line, Picture, Total;
44 }
45
46 public enum StatType {
47 Created, Copied, Moved, Deleted;
48 }
49
50 private static final int STAT_TYPES = StatType.values().length;
51
52 private static final int ITEM_TYPES = ItemType.values().length;
53
54 protected static int[][] _ItemStats = new int[ITEM_TYPES][STAT_TYPES];
55
56 private static List<String> _FrameEvents = new ArrayList<String>();
57
58 // statistics counters
59 private static final int MOUSE_BUTTONS = 4;
60
61 private static StringBuffer _FramesEdited = new StringBuffer();
62
63 protected static int[] _MouseCounters = new int[MOUSE_BUTTONS];
64
65 private static int _AccessedFrames = 0;
66
67 private static int _SavedFrames = 0;
68
69 // the number of frames created via TDFC
70 protected static int _CreatedFrames = 0;
71
72 // character counts
73 private static int[] _CharCounts = new int[255];
74
75 private static long _LastEvent = new Date().getTime();
76
77 private static final long DARK_TIME_THRESHOLD = 60000;
78
79 protected static int _EscapeCount = 0;
80
81 private static boolean _StatsEnabled = true;
82
83 private static final int[] EVENT_INTERVALS = new int[] { 1, 2, 3, 4, 5, 10,
84 20, 60, 300, 600, Integer.MAX_VALUE };
85
86 private static int[] _EventTimes = new int[EVENT_INTERVALS.length];
87
88 protected static int _BackspaceCount;
89
90 public static String getCurrentStats() {
91
92 StringBuffer stats = getLength();
93
94 long elapsedTime = (new Date()).getTime() - _StartTime.getTime();
95 //Add darkTime to the nearest minute
96 stats.append("DarkTime: ").append(
97 (_DarkTime.getTime() + MILLISECONDS_PER_MINUTE / 2) / MILLISECONDS_PER_MINUTE).append(
98 "-->" + _DarkTime.getTime() * 100 / elapsedTime + "%\n");
99
100 stats.append(getResponseStats()).append("\n");
101 stats.append(getFrameStats());
102
103 stats.append(getCharacterStats());
104
105 stats.append("Version: ").append(DisplayIO.TITLE);
106
107 return stats.toString();
108 }
109
110 private static String getTimeElapsed() {
111 Date currentTime = new Date();
112 long elapsedTime = currentTime.getTime() - _StartTime.getTime();
113 String time = ""
114 + Math.round((float) elapsedTime / MILLISECONDS_PER_MINUTE);
115 return time;
116 }
117
118 private static String getResponseStats() {
119 String stats = "ResponseTime: "
120 + TimeKeeper.Formatter.format(FrameUtils.getLastResponseTime())
121 + "sec, ";
122
123 if (_AccessedFrames > 0)
124 stats += TimeKeeper.Formatter.format(FrameUtils
125 .getResponseTimeTotal()
126 / _AccessedFrames)
127 + "avg";
128 else
129 stats += "--avg";
130
131 return stats;
132 }
133
134 private static String getFrameStats() {
135 return getFrameStats(true);
136 }
137
138 private static String getFrameStats(boolean newline) {
139 StringBuffer stats = new StringBuffer();
140 appendStat(stats, "FramesAccessed", _AccessedFrames, newline, false,
141 DEFAULT_VALUE_WIDTH, DEFAULT_RATE_WIDTH);
142 appendStat(stats, "FramesEdited", _SavedFrames, newline, false,
143 DEFAULT_VALUE_WIDTH, DEFAULT_RATE_WIDTH);
144 return stats.toString();
145 }
146
147 private static String getCharacterStats() {
148 int[] counts = _CharCounts;
149
150 int chars = 0;
151
152 for (int i = 'A'; i <= 'Z'; i++)
153 chars += counts[i];
154
155 for (int i = 'a'; i <= 'z'; i++)
156 chars += counts[i];
157
158 for (int i = '0'; i <= '9'; i++)
159 chars += counts[i];
160
161 chars -= counts[KeyEvent.VK_BACK_SPACE];
162 chars -= counts[KeyEvent.VK_DELETE];
163
164 int EOS = counts['.'] + counts[','] + counts['!'] + counts['?'];
165
166 int punct = counts[' '] + counts[';'] + counts[':'];
167 chars += counts['('] + counts[')'] + counts['\''] + counts['"']
168 + counts['+'] + counts['='] + counts['-'];
169
170 StringBuffer stats = new StringBuffer();
171 appendStat(stats, "Chars", chars + punct + EOS);
172 appendStat(stats, "Words", punct + EOS);
173 appendStat(stats, "Sentences", EOS);
174 appendStat(stats, "TextItems",
175 _ItemStats[ItemType.Text.ordinal()][StatType.Created.ordinal()]);
176 appendStat(stats, "Frames", _CreatedFrames);
177 appendStat(stats, "Escape", _EscapeCount);
178 appendStat(stats, "Backspace", _BackspaceCount);
179 appendStat(stats, "Left", _MouseCounters[MouseEvent.BUTTON1]);
180 appendStat(stats, "Middle", _MouseCounters[MouseEvent.BUTTON2]);
181 appendStat(stats, "Right", _MouseCounters[MouseEvent.BUTTON3]);
182
183 return stats.toString();
184 }
185
186 public static void resetStats() {
187 _StartTime = new Date();
188 _AccessedFrames = 0;
189 _SavedFrames = 0;
190 _CreatedFrames = 0;
191 _CharCounts = new int[255];
192 _EscapeCount = 0;
193 _BackspaceCount = 0;
194 _DarkTime.setTime(0);
195 _MouseCounters = new int[MOUSE_BUTTONS];
196
197 for (int i = 0; i < ITEM_TYPES; i++) {
198 for (int j = 0; j < STAT_TYPES; j++) {
199 _ItemStats[i][j] = 0;
200 }
201 }
202 }
203
204 /**
205 * Called signal that a frame has been accessed.
206 *
207 */
208 public static void AccessedFrame() {
209 if (_StatsEnabled) {
210 _AccessedFrames++;
211 }
212 }
213
214 public static void SavedFrame(String frameName) {
215 FrameEdited(frameName);
216 _SavedFrames++;
217 }
218
219 public static void Escape() {
220 _EscapeCount++;
221 }
222
223 public static void CreatedFrame() {
224 _CreatedFrames++;
225 }
226
227 /**
228 * Increments the count for a character that is typed.
229 *
230 * @param ch
231 * ascii value for the typed character
232 */
233 public static void TypedChar(int ch) {
234 if (ch == KeyEvent.VK_BACK_SPACE || ch == KeyEvent.VK_DELETE)
235 _BackspaceCount++;
236 UserEvent();
237 _CharCounts[ch]++;
238 }
239
240 private static void UserEvent() {
241 long thisEvent = new Date().getTime();
242 long elapsedTime = thisEvent - _LastEvent;
243 addEventTime(elapsedTime);
244 if (elapsedTime > DARK_TIME_THRESHOLD) {
245 _DarkTime.setTime(_DarkTime.getTime() + elapsedTime);
246 }
247 _LastEvent = thisEvent;
248 }
249
250 public static void AddFrameEvent(String description) {
251 Date elapsedTime = getFrameTotalTime();
252
253 _FrameEvents.add(Formatter.getTimeWithMillis(elapsedTime) + " "
254 + DisplayIO.getMouseX() + " " + FrameMouseActions.getY() + " "
255 + description);
256 }
257
258 public static String getFrameEventList(Frame currentFrame) {
259 StringBuilder eventList = new StringBuilder();
260 // First put on the session and darkTime
261 Time darkTime = currentFrame == null ? getFrameDarkTime()
262 : currentFrame.getDarkTime();
263 Time activeTime = currentFrame == null ? getFrameActiveTime()
264 : currentFrame.getActiveTime();
265 eventList.append(ACTIVE_TIME_ATTRIBUTE).append(
266 Formatter.getTimePeriod(activeTime)).append('\n');
267 eventList.append(DARK_TIME_ATTRIBUTE).append(
268 Formatter.getTimePeriod(darkTime)).append('\n');
269 for (String s : _FrameEvents)
270 eventList.append(s).append('\n');
271 if (eventList.length() > 0)
272 eventList.deleteCharAt(eventList.length() - 1);
273 return eventList.toString();
274 }
275
276 public static String getFrameEventList() {
277 return getFrameEventList(null);
278 }
279
280 public static void MouseClicked(int button) {
281 UserEvent();
282 _MouseCounters[button]++;
283 }
284
285 public static void setEnabled(boolean value) {
286 _StatsEnabled = true;
287 }
288
289 private static void FrameEdited(String name) {
290 _FramesEdited.append(Formatter.getLongDateTime()).append("[").append(
291 name).append("]\n");
292 }
293
294 public static String getFramesEdited() {
295 return _FramesEdited.toString();
296 }
297
298 public static StringBuffer getShortStats() {
299 StringBuffer sb = new StringBuffer();
300 sb.append("FramesA:").append(_AccessedFrames);
301 sb.append(", FramesE:").append(_SavedFrames);
302 sb.append(", ").append(getResponseStats());
303 return sb;
304 }
305
306 private static void ItemStats(Collection<Item> items, StatType stat) {
307 if (items == null)
308 return;
309 for (Item i : items) {
310 if (i instanceof Text)
311 _ItemStats[ItemType.Text.ordinal()][stat.ordinal()]++;
312 else if (i instanceof Dot)
313 _ItemStats[ItemType.Dot.ordinal()][stat.ordinal()]++;
314 else if (i instanceof Picture)
315 _ItemStats[ItemType.Picture.ordinal()][stat.ordinal()]++;
316 else if (i instanceof Line)
317 _ItemStats[ItemType.Line.ordinal()][stat.ordinal()]++;
318 }
319 }
320
321 public static void CreatedItems(Collection<Item> items) {
322 ItemStats(items, StatType.Created);
323 }
324
325 public static void MovedItems(Collection<Item> items) {
326 ItemStats(items, StatType.Moved);
327 }
328
329 public static void CopiedItems(Collection<Item> items) {
330 ItemStats(items, StatType.Copied);
331 }
332
333 public static void DeletedItems(Collection<Item> items) {
334 ItemStats(items, StatType.Deleted);
335 }
336
337 public static void CreatedText() {
338 _ItemStats[ItemType.Text.ordinal()][StatType.Created.ordinal()]++;
339 }
340
341 public static String getItemStats() {
342
343 StringBuffer sb = getLength();
344
345 int max = 0;
346 final int TOTAL_INDEX = ITEM_TYPES - 1;
347 for (int i = 0; i < STAT_TYPES; i++) {
348 _ItemStats[TOTAL_INDEX][i] = 0;
349 for (int j = 0; j < TOTAL_INDEX; j++) {
350 _ItemStats[TOTAL_INDEX][i] += _ItemStats[j][i];
351 }
352 }
353 for (int i = 0; i < STAT_TYPES; i++) {
354 max = Math.max(_ItemStats[TOTAL_INDEX][i], max);
355 }
356 int maxWidthValue = ("" + max).length();
357 int maxWidthRate = (getRate(max)).length();
358
359 int maxNameWidth = 0;
360 int maxColumnWidth = 0;
361 ItemType[] itemTypes = ItemType.values();
362 StatType[] statTypes = StatType.values();
363 // Get the width of the longest itemType
364 for (int i = 0; i < ITEM_TYPES; i++) {
365 maxNameWidth = Math.max(maxNameWidth, itemTypes[i].toString()
366 .length());
367 }
368
369 for (int i = 0; i < STAT_TYPES; i++) {
370 maxNameWidth = Math.max(maxColumnWidth, statTypes[i].toString()
371 .length());
372 }
373 maxColumnWidth = Math.max(maxWidthRate + maxWidthValue + 3,
374 maxNameWidth);
375
376 sb.append(getBufferedString("", maxNameWidth)).append(COLUMN_SEPARATOR);
377
378 StringBuffer lineSeparator = getBufferedString("", maxNameWidth,
379 ROW_SEPARATOR_CHAR);
380 lineSeparator.append(ROW_COLUMN_SEPARATOR);
381
382 for (int i = 0; i < STAT_TYPES; i++) {
383 sb.append(
384 getBufferedString(statTypes[i].toString(), maxColumnWidth))
385 .append(COLUMN_SEPARATOR);
386 lineSeparator.append(getBufferedString("", maxColumnWidth,
387 ROW_SEPARATOR_CHAR).append(ROW_COLUMN_SEPARATOR));
388 }
389 // Remove the last column separator
390 lineSeparator.delete(lineSeparator.length()
391 - ROW_COLUMN_SEPARATOR.length(), lineSeparator.length() - 1);
392 sb.delete(sb.length() - COLUMN_SEPARATOR.length(), sb.length() - 1);
393 sb.append('\n');
394 sb.append(lineSeparator).append('\n');
395
396 for (int j = 0; j < ITEM_TYPES; j++) {
397 sb.append(getBufferedString(itemTypes[j].toString(), maxNameWidth))
398 .append(COLUMN_SEPARATOR);
399 for (int i = 0; i < STAT_TYPES; i++) {
400 int total = 0;
401 int nonZeroItems = 0;
402 String statName = StatType.values()[i].toString();
403
404 int value = _ItemStats[j][i];
405 if (value > 0)
406 nonZeroItems++;
407 total += value;
408 sb.append(getCompactStat(ItemType.values()[j].toString()
409 + statName, value, maxWidthValue, maxColumnWidth));
410 }
411 sb.delete(sb.length() - COLUMN_SEPARATOR.length(), sb.length() - 1);
412 sb.append('\n');
413 }
414 sb.append(lineSeparator.toString().replace(ROW_COLUMN_SEPARATOR_CHAR,
415 BOTTOM_COLUMN_SEPARATOR_CHAR));
416 return sb.toString();
417 }
418
419 public static String getEventStats() {
420 StringBuffer stats = getLength();
421 int max = getMax(_EventTimes);
422 int maxWidthEvents = ("" + max).length();
423 int maxWidthRate = (getRate(max)).length();
424 for (int i = 0; i < _EventTimes.length - 1; i++) {
425 String upperBound = getFormattedTime(EVENT_INTERVALS[i]);
426 appendStat(stats, "<" + upperBound, _EventTimes[i], true, false,
427 maxWidthEvents, maxWidthRate);
428 }
429 int lastIndex = EVENT_INTERVALS.length - 1;
430 appendStat(stats, "+"
431 + getFormattedTime(EVENT_INTERVALS[lastIndex - 1]),
432 _EventTimes[lastIndex], false, false, maxWidthEvents,
433 maxWidthRate);
434 return stats.toString();
435 }
436
437 /**
438 * Gets the highest number in the list of numbers.
439 *
440 * @param nums
441 * list of numbers
442 * @return highest number in the list
443 */
444 private static int getMax(int[] nums) {
445 int max = 0;
446 for (int i = 0; i < nums.length; i++)
447 if (nums[i] > max)
448 max = nums[i];
449 return max;
450 }
451
452 private static String getFormattedTime(int secs) {
453 String time;
454 if (secs < 60) {
455 time = secs + "s";
456 } else if (secs < 60 * 60) {
457 time = (secs / 60) + "m";
458 } else {
459 time = (secs / 60 / 60) + "h";
460 }
461 if (time.length() == 2)
462 time = " " + time;
463 return time;
464 }
465
466 /**
467 * @return
468 */
469 public static StringBuffer getLength() {
470 StringBuffer stats = getDate();
471 stats.append("Start: ").append(Formatter.getDateTime(_StartTime))
472 .append("\n");
473 stats.append("SessionTime: ").append(getTimeElapsed()).append("\n");
474 return stats;
475 }
476
477 private static void addEventTime(long millis) {
478
479 for (int i = 0; i < _EventTimes.length - 1; i++) {
480 if (millis < EVENT_INTERVALS[i] * 1000) {
481 _EventTimes[i]++;
482 return;
483 }
484 }
485 _EventTimes[_EventTimes.length - 1]++;
486 }
487
488 public static void DeletedItem(Item toDelete) {
489 List<Item> items = new ArrayList<Item>();
490 items.add(toDelete);
491 DeletedItems(items);
492 }
493
494 /**
495 * Reset the stats ready to keep track of the stats for this current session
496 * of editting a newly displayed frame. This method is called everytime the
497 * user navigates to a new frame.
498 *
499 * The frame access time is used when the frame is saved to see how long the
500 * user was editting the frame. The frameAccessDark time keeps track of the
501 * total session dark time when this frame was accessed. If the frame is
502 * editted and saved, its the difference in the current session dark time
503 * and the session dark time when the frame was accessed, is added to the
504 * frames dark time.
505 *
506 */
507 public static void NewFrameSession() {
508 _FrameEvents.clear();
509 _FrameAccessTime = new Date();
510 _FrameAccessDarkTime = (Time) _DarkTime.clone();
511 }
512}
Note: See TracBrowser for help on using the repository browser.