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

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

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