source: trunk/autoquest-core-usability/src/main/java/de/ugoe/cs/autoquest/usability/DefaultCursorPositioningRule.java @ 1918

Last change on this file since 1918 was 1918, checked in by pharms, 9 years ago
  • extension with further smell detections
  • may not fully work. But Hudson is more happy because compile errors should be gone
File size: 8.5 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.usability;
16
17import java.text.DecimalFormat;
18import java.util.HashMap;
19import java.util.List;
20import java.util.Map;
21
22import org.apache.commons.math3.stat.inference.ChiSquareTest;
23
24import de.ugoe.cs.autoquest.eventcore.Event;
25import de.ugoe.cs.autoquest.eventcore.IEventTarget;
26import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonDown;
27import de.ugoe.cs.autoquest.eventcore.gui.MouseButtonInteraction;
28import de.ugoe.cs.autoquest.eventcore.gui.MouseClick;
29import de.ugoe.cs.autoquest.eventcore.gui.Scroll;
30import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIElement;
31import de.ugoe.cs.autoquest.eventcore.guimodel.IGUIView;
32import de.ugoe.cs.autoquest.eventcore.guimodel.ITextArea;
33import de.ugoe.cs.autoquest.eventcore.guimodel.ITextField;
34import de.ugoe.cs.autoquest.tasktrees.treeifc.DefaultTaskInstanceTraversingVisitor;
35import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTask;
36import de.ugoe.cs.autoquest.tasktrees.treeifc.IEventTaskInstance;
37import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskInstance;
38import de.ugoe.cs.autoquest.tasktrees.treeifc.ITaskModel;
39import de.ugoe.cs.autoquest.tasktrees.treeifc.IUserSession;
40
41/**
42 * TODO comment
43 *
44 * @version $Revision: $ $Date: 16.07.2012$
45 * @author 2012, last modified by $Author: pharms$
46 */
47public class DefaultCursorPositioningRule implements UsabilityEvaluationRule {
48
49    /*
50     * (non-Javadoc)
51     *
52     * @see de.ugoe.cs.usability.UsabilityEvaluationRule#evaluate(TaskTree)
53     */
54    @Override
55    public UsabilityEvaluationResult evaluate(ITaskModel taskModel) {
56        FirstViewActionStatistics statistics = new FirstViewActionStatistics();
57        calculateStatistics(taskModel.getUserSessions(), statistics);
58
59        UsabilityEvaluationResult results = new UsabilityEvaluationResult(taskModel);
60        analyzeStatistics(statistics, results);
61
62        return results;
63    }
64
65    /**
66     *
67     */
68    private void analyzeStatistics(FirstViewActionStatistics statistics,
69                                   UsabilityEvaluationResult results)
70    {
71        for (Map.Entry<IGUIView, Map<IEventTask, Integer>> firstActions :
72                statistics.getFirstViewActions().entrySet())
73        {
74            long[] observed = new long[firstActions.getValue().size()];
75            long allObservedInView = 0;
76            long maxObserved = 0;
77            int i = 0;
78            IEventTask mostOftenDoneFirst = null;
79           
80            for (Map.Entry<IEventTask, Integer> firstAction : firstActions.getValue().entrySet()) {
81                observed[i++] = firstAction.getValue();
82                allObservedInView += firstAction.getValue();
83               
84                if (maxObserved < firstAction.getValue()) {
85                    maxObserved = firstAction.getValue();
86                    mostOftenDoneFirst = firstAction.getKey();
87                }
88            }
89           
90            double[] expected = new double[firstActions.getValue().size()];
91            double expectedFrequency =
92                ((double) statistics.getViewOpenedCount(firstActions.getKey())) / expected.length;
93           
94            for (i = 0; i < expected.length; i++) {
95                expected[i] = expectedFrequency;
96            }
97           
98            if ((allObservedInView > 1) &&
99                ((expected.length == 1) ||
100                 (new ChiSquareTest().chiSquareTest(expected, observed, 0.05))))
101            {
102                // values are not equally distributed.
103                   
104                UsabilitySmellIntensity intensity = UsabilitySmellIntensity.getIntensity
105                    ((int) (1000 * maxObserved / allObservedInView), (int) allObservedInView, -1);
106                       
107                if ((intensity != null) && isCursorPositioningAction(mostOftenDoneFirst)) {
108                    Map<String, Object> parameters = new HashMap<String, Object>();
109                    parameters.put("view", firstActions.getKey());
110                    parameters.put("textfield", getTarget(mostOftenDoneFirst));
111                    parameters.put("ratio", new DecimalFormat("#.##").format
112                                       (100.0 * maxObserved / allObservedInView));
113
114                    results.addSmell(intensity, UsabilitySmellDescription.MOST_OFTEN_DONE_FIRST,
115                                     parameters);
116                }
117            }
118        }
119    }
120
121
122    /**
123     *
124     */
125    private boolean isCursorPositioningAction(IEventTask task) {
126        Event event = ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent();
127        return ((event.getType() instanceof MouseClick) ||
128                (event.getType() instanceof MouseButtonDown)) &&
129               (((MouseButtonInteraction) event.getType()).getButton() ==
130                   MouseButtonInteraction.Button.LEFT) &&
131               ((event.getTarget() instanceof ITextField) ||
132                (event.getTarget() instanceof ITextArea));
133    }
134
135    /**
136     *
137     */
138    private IEventTarget getTarget(IEventTask task) {
139        return ((IEventTaskInstance) task.getInstances().iterator().next()).getEvent().getTarget();
140    }
141
142    /**
143     *
144     */
145    private void calculateStatistics(List<IUserSession>              sessions,
146                                     final FirstViewActionStatistics statistics)
147    {
148        final IGUIView[] currentView = new IGUIView[1];
149       
150        for (IUserSession session : sessions) {
151            for (final ITaskInstance currentRoot : session) {
152                currentRoot.accept(new DefaultTaskInstanceTraversingVisitor() {
153                    @Override
154                    public void visit(IEventTaskInstance eventTaskInstance) {
155                        // ignore scrolling to get something semantically helpful
156                        if ((eventTaskInstance.getEvent().getTarget() instanceof IGUIElement) &&
157                            (!(eventTaskInstance.getEvent().getType() instanceof Scroll)))
158                        {
159                            IGUIView view =
160                                ((IGUIElement) eventTaskInstance.getEvent().getTarget()).getView();
161                           
162                            if (((currentView[0] == null) && (view != null)) ||
163                                ((currentView[0] != null) && (!currentView[0].equals(view))))
164                            {
165                                currentView[0] = view;
166                                statistics.addFirstViewAction
167                                    (view, eventTaskInstance.getEventTask());
168                            }
169                        }
170                    }
171                });
172            }
173        }
174    }
175
176    /**
177     *
178     */
179    private class FirstViewActionStatistics {
180       
181        /** */
182        private Map<IGUIView, Map<IEventTask, Integer>> firstViewActions = new HashMap<>();
183       
184        /**
185         *
186         */
187        private void addFirstViewAction(IGUIView view, IEventTask action) {
188            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
189           
190            if (counterMap == null) {
191                counterMap = new HashMap<>();
192                firstViewActions.put(view, counterMap);
193            }
194           
195            Integer counter = counterMap.get(action);
196           
197            if (counter == null) {
198                counterMap.put(action, 1);
199            }
200            else {
201                counterMap.put(action, counter + 1);
202            }
203        }
204
205        /**
206         *
207         */
208        private Map<IGUIView, Map<IEventTask, Integer>> getFirstViewActions() {
209            return firstViewActions;
210        }
211
212        /**
213         *
214         */
215        private int getViewOpenedCount(IGUIView view) {
216            Map<IEventTask, Integer> counterMap = firstViewActions.get(view);
217            int count = 0;
218           
219            for (Integer counter : counterMap.values()) {
220                count += counter;
221            }
222           
223            return count;
224        }
225    }
226}
Note: See TracBrowser for help on using the repository browser.