diff --git a/common/resh-common.go b/common/resh-common.go index 69fb7cf..1499595 100644 --- a/common/resh-common.go +++ b/common/resh-common.go @@ -74,8 +74,9 @@ type Record struct { CmdLength int `json:"cmdLength"` // enriching fields - added "later" - FirstWord string `json:"firstWord"` - Invalid bool `json:"invalid"` + FirstWord string `json:"firstWord"` + Invalid bool `json:"invalid"` + SeqSessionID uint64 `json:"seqSessionID"` } // FallbackRecord when record is too old and can't be parsed into regular Record diff --git a/evaluate/resh-evaluate-plot.py b/evaluate/resh-evaluate-plot.py index 5ec215b..9f27347 100755 --- a/evaluate/resh-evaluate-plot.py +++ b/evaluate/resh-evaluate-plot.py @@ -26,7 +26,7 @@ for user in data["UsersRecords"]: continue DATA_records.append(record) - DATA_records_by_session[record["sessionPid"]].append(record) + DATA_records_by_session[record["sessionId"]].append(record) DATA_records = list(sorted(DATA_records, key=lambda x: x["realtimeBeforeLocal"])) @@ -265,6 +265,9 @@ def plot_strategies_matches(plot_size=50, selected_strategies=[]): plt.ylabel('%' + " of matches") plt.xlabel("Distance") legend = [] + x_values = range(1, plot_size+1) + saved_matches_total = None + saved_dataPoint_count = None for strategy in data["Strategies"]: strategy_title = strategy["Title"] # strategy_description = strategy["Description"] @@ -295,24 +298,34 @@ def plot_strategies_matches(plot_size=50, selected_strategies=[]): matches[dist-1] += 1 charsRecalled[dist-1] += chars + # recent is very simple strategy so we will believe + # that there is no bug in it and we can use it to determine total + if strategy_title == "recent": + saved_matches_total = matches_total + saved_dataPoint_count = dataPoint_count + + if len(selected_strategies) and strategy_title not in selected_strategies: + continue acc = 0 matches_cumulative = [] for x in matches: acc += x matches_cumulative.append(acc) - matches_cumulative.append(matches_total) + # matches_cumulative.append(matches_total) matches_percent = list(map(lambda x: 100 * x / dataPoint_count, matches_cumulative)) - x_values = range(1, plot_size+2) plt.plot(x_values, matches_percent, 'o-') legend.append(strategy_title) + assert(saved_matches_total is not None) + assert(saved_dataPoint_count is not None) + max_values = [100 * saved_matches_total / saved_dataPoint_count] * len(x_values) + plt.plot(x_values, max_values, 'r-') + legend.append("maximum possible") x_ticks = list(range(1, plot_size+1, 2)) x_labels = x_ticks[:] - x_ticks.append(plot_size+1) - x_labels.append("total") plt.xticks(x_ticks, x_labels) plt.legend(legend, loc="best") plt.show() @@ -324,14 +337,14 @@ def plot_strategies_charsRecalled(plot_size=50, selected_strategies=[]): plt.title("Average characters recalled at distance") plt.ylabel("Average characters recalled") plt.xlabel("Distance") + x_values = range(1, plot_size+1) legend = [] + saved_charsRecalled_total = None + saved_dataPoint_count = None for strategy in data["Strategies"]: strategy_title = strategy["Title"] # strategy_description = strategy["Description"] - if len(selected_strategies) and strategy_title not in selected_strategies: - continue - dataPoint_count = 0 matches = [0] * plot_size matches_total = 0 @@ -355,38 +368,47 @@ def plot_strategies_charsRecalled(plot_size=50, selected_strategies=[]): matches[dist-1] += 1 charsRecalled[dist-1] += chars + # recent is very simple strategy so we will believe + # that there is no bug in it and we can use it to determine total + if strategy_title == "recent": + saved_charsRecalled_total = charsRecalled_total + saved_dataPoint_count = dataPoint_count + + if len(selected_strategies) and strategy_title not in selected_strategies: + continue acc = 0 charsRecalled_cumulative = [] for x in charsRecalled: acc += x charsRecalled_cumulative.append(acc) - charsRecalled_cumulative.append(charsRecalled_total) charsRecalled_average = list(map(lambda x: x / dataPoint_count, charsRecalled_cumulative)) - x_values = range(1, plot_size+2) plt.plot(x_values, charsRecalled_average, 'o-') legend.append(strategy_title) + assert(saved_charsRecalled_total is not None) + assert(saved_dataPoint_count is not None) + max_values = [saved_charsRecalled_total / saved_dataPoint_count] * len(x_values) + plt.plot(x_values, max_values, 'r-') + legend.append("maximum possible") x_ticks = list(range(1, plot_size+1, 2)) x_labels = x_ticks[:] - x_ticks.append(plot_size+1) - x_labels.append("total") plt.xticks(x_ticks, x_labels) plt.legend(legend, loc="best") plt.show() -graph_cmdSequences() -graph_cmdSequences(node_count=28, edge_minValue=0.06) - -plot_cmdLineFrq_rank() -# plot_cmdFrq_rank() - -plot_cmdLineVocabularySize_cmdLinesEntered() -# plot_cmdVocabularySize_cmdLinesEntered() +# graph_cmdSequences() +# graph_cmdSequences(node_count=28, edge_minValue=0.06) +# +# plot_cmdLineFrq_rank() +# # plot_cmdFrq_rank() +# +# plot_cmdLineVocabularySize_cmdLinesEntered() +# # plot_cmdVocabularySize_cmdLinesEntered() plot_strategies_matches() plot_strategies_charsRecalled() diff --git a/evaluate/resh-evaluate.go b/evaluate/resh-evaluate.go index 07bb34c..bef0b24 100644 --- a/evaluate/resh-evaluate.go +++ b/evaluate/resh-evaluate.go @@ -12,6 +12,7 @@ import ( "os/exec" "os/user" "path/filepath" + "sort" "github.com/curusarn/resh/common" ) @@ -89,7 +90,12 @@ func main() { // strategies = append(strategies, &dummy) recent := strategyRecent{} - strategies = append(strategies, &recent) + frequent := strategyFrequent{} + frequent.init() + directory := strategyDirectorySensitive{} + directory.init() + + strategies = append(strategies, &recent, &frequent, &directory) for _, strat := range strategies { err := evaluator.evaluate(strat) @@ -175,8 +181,18 @@ func (e *evaluator) calculateStatsAndPlot(scriptName string) { // enrich records and add them to serializable structure func (e *evaluator) processRecords() { for i := range e.UsersRecords { - for j := range e.UsersRecords[i].Devices { + for j, device := range e.UsersRecords[i].Devices { + sessionIDs := map[string]uint64{} + var nextID uint64 + nextID = 0 for k, record := range e.UsersRecords[i].Devices[j].Records { + id, found := sessionIDs[record.SessionId] + if found == false { + id = nextID + sessionIDs[record.SessionId] = id + nextID++ + } + record.SeqSessionID = id // assert if record.Sanitized != e.sanitizedInput { if e.sanitizedInput { @@ -188,6 +204,12 @@ func (e *evaluator) processRecords() { e.UsersRecords[i].Devices[j].Records[k].Enrich() // device.Records = append(device.Records, record) } + sort.SliceStable(e.UsersRecords[i].Devices[j].Records, func(x, y int) bool { + if device.Records[x].SeqSessionID == device.Records[y].SeqSessionID { + return device.Records[x].RealtimeAfterLocal < device.Records[y].RealtimeAfterLocal + } + return device.Records[x].SeqSessionID < device.Records[y].SeqSessionID + }) } } } diff --git a/evaluate/strategy-directory-sensitive.go b/evaluate/strategy-directory-sensitive.go new file mode 100644 index 0000000..0c00bc4 --- /dev/null +++ b/evaluate/strategy-directory-sensitive.go @@ -0,0 +1,42 @@ +package main + +import ( + "github.com/curusarn/resh/common" +) + +type strategyDirectorySensitive struct { + history map[string][]string + lastPwd string +} + +func (s *strategyDirectorySensitive) init() { + s.history = map[string][]string{} +} + +func (s *strategyDirectorySensitive) GetTitleAndDescription() (string, string) { + return "directory sensitive (recent)", "Use recent commands executed is the same directory" +} + +func (s *strategyDirectorySensitive) GetCandidates() []string { + return s.history[s.lastPwd] +} + +func (s *strategyDirectorySensitive) AddHistoryRecord(record *common.Record) error { + // work on history for PWD + pwd := record.Pwd + // remove previous occurance of record + for i, cmd := range s.history[pwd] { + if cmd == record.CmdLine { + s.history[pwd] = append(s.history[pwd][:i], s.history[pwd][i+1:]...) + } + } + // append new record + s.history[pwd] = append([]string{record.CmdLine}, s.history[pwd]...) + s.lastPwd = record.PwdAfter + return nil +} + +func (s *strategyDirectorySensitive) ResetHistory() error { + s.history = map[string][]string{} + return nil +} diff --git a/evaluate/strategy-frequent.go b/evaluate/strategy-frequent.go new file mode 100644 index 0000000..c41f852 --- /dev/null +++ b/evaluate/strategy-frequent.go @@ -0,0 +1,47 @@ +package main + +import ( + "sort" + + "github.com/curusarn/resh/common" +) + +type strategyFrequent struct { + history map[string]int +} + +type strFrqEntry struct { + cmdLine string + count int +} + +func (s *strategyFrequent) init() { + s.history = map[string]int{} +} + +func (s *strategyFrequent) GetTitleAndDescription() (string, string) { + return "frequent", "Use frequent commands" +} + +func (s *strategyFrequent) GetCandidates() []string { + var mapItems []strFrqEntry + for cmdLine, count := range s.history { + mapItems = append(mapItems, strFrqEntry{cmdLine, count}) + } + sort.Slice(mapItems, func(i int, j int) bool { return mapItems[i].count > mapItems[j].count }) + var hist []string + for _, item := range mapItems { + hist = append(hist, item.cmdLine) + } + return hist +} + +func (s *strategyFrequent) AddHistoryRecord(record *common.Record) error { + s.history[record.CmdLine]++ + return nil +} + +func (s *strategyFrequent) ResetHistory() error { + s.history = map[string]int{} + return nil +} diff --git a/evaluate/strategy-recent.go b/evaluate/strategy-recent.go index b75adc2..7d24d23 100644 --- a/evaluate/strategy-recent.go +++ b/evaluate/strategy-recent.go @@ -27,5 +27,6 @@ func (s *strategyRecent) AddHistoryRecord(record *common.Record) error { } func (s *strategyRecent) ResetHistory() error { + s.history = nil return nil }