remove packages

pull/184/head
Simon Let 4 years ago committed by Simon Let
parent 6e08ff08cf
commit d7f03ec8fa
  1. 246
      pkg/histanal/histeval.go
  2. 180
      pkg/histanal/histload.go
  3. 47
      pkg/strat/directory-sensitive.go
  4. 29
      pkg/strat/dummy.go
  5. 91
      pkg/strat/dynamic-record-distance.go
  6. 53
      pkg/strat/frequent.go
  7. 97
      pkg/strat/markov-chain-cmd.go
  8. 76
      pkg/strat/markov-chain.go
  9. 57
      pkg/strat/random.go
  10. 56
      pkg/strat/recent-bash.go
  11. 37
      pkg/strat/recent.go
  12. 70
      pkg/strat/record-distance.go
  13. 46
      pkg/strat/strat.go

@ -1,246 +0,0 @@
package histanal
import (
"bytes"
"encoding/json"
"fmt"
"log"
"math/rand"
"os"
"os/exec"
"github.com/curusarn/resh/pkg/records"
"github.com/curusarn/resh/pkg/strat"
"github.com/jpillora/longestcommon"
"github.com/schollz/progressbar"
)
type matchJSON struct {
Match bool
Distance int
CharsRecalled int
}
type multiMatchItemJSON struct {
Distance int
CharsRecalled int
}
type multiMatchJSON struct {
Match bool
Entries []multiMatchItemJSON
}
type strategyJSON struct {
Title string
Description string
Matches []matchJSON
PrefixMatches []multiMatchJSON
}
// HistEval evaluates history
type HistEval struct {
HistLoad
BatchMode bool
maxCandidates int
Strategies []strategyJSON
}
// NewHistEval constructs new HistEval
func NewHistEval(inputPath string,
maxCandidates int, skipFailedCmds bool,
debugRecords float64, sanitizedInput bool) HistEval {
e := HistEval{
HistLoad: HistLoad{
skipFailedCmds: skipFailedCmds,
debugRecords: debugRecords,
sanitizedInput: sanitizedInput,
},
maxCandidates: maxCandidates,
BatchMode: false,
}
records := e.loadHistoryRecords(inputPath)
device := deviceRecords{Records: records}
user := userRecords{}
user.Devices = append(user.Devices, device)
e.UsersRecords = append(e.UsersRecords, user)
e.preprocessRecords()
return e
}
// NewHistEvalBatchMode constructs new HistEval in batch mode
func NewHistEvalBatchMode(input string, inputDataRoot string,
maxCandidates int, skipFailedCmds bool,
debugRecords float64, sanitizedInput bool) HistEval {
e := HistEval{
HistLoad: HistLoad{
skipFailedCmds: skipFailedCmds,
debugRecords: debugRecords,
sanitizedInput: sanitizedInput,
},
maxCandidates: maxCandidates,
BatchMode: false,
}
e.UsersRecords = e.loadHistoryRecordsBatchMode(input, inputDataRoot)
e.preprocessRecords()
return e
}
func (e *HistEval) preprocessDeviceRecords(device deviceRecords) deviceRecords {
sessionIDs := map[string]uint64{}
var nextID uint64
nextID = 1 // start with 1 because 0 won't get saved to json
for k, record := range device.Records {
id, found := sessionIDs[record.SessionID]
if found == false {
id = nextID
sessionIDs[record.SessionID] = id
nextID++
}
device.Records[k].SeqSessionID = id
// assert
if record.Sanitized != e.sanitizedInput {
if e.sanitizedInput {
log.Fatal("ASSERT failed: '--sanitized-input' is present but data is not sanitized")
}
log.Fatal("ASSERT failed: data is sanitized but '--sanitized-input' is not present")
}
device.Records[k].SeqSessionID = id
if e.debugRecords > 0 && rand.Float64() < e.debugRecords {
device.Records[k].DebugThisRecord = true
}
}
// sort.SliceStable(device.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
// })
// iterate from back and mark last record of each session
sessionIDSet := map[string]bool{}
for i := len(device.Records) - 1; i >= 0; i-- {
var record *records.EnrichedRecord
record = &device.Records[i]
if sessionIDSet[record.SessionID] {
continue
}
sessionIDSet[record.SessionID] = true
record.LastRecordOfSession = true
}
return device
}
// enrich records and add sequential session ID
func (e *HistEval) preprocessRecords() {
for i := range e.UsersRecords {
for j := range e.UsersRecords[i].Devices {
e.UsersRecords[i].Devices[j] = e.preprocessDeviceRecords(e.UsersRecords[i].Devices[j])
}
}
}
// Evaluate a given strategy
func (e *HistEval) Evaluate(strategy strat.IStrategy) error {
title, description := strategy.GetTitleAndDescription()
log.Println("Evaluating strategy:", title, "-", description)
strategyData := strategyJSON{Title: title, Description: description}
for i := range e.UsersRecords {
for j := range e.UsersRecords[i].Devices {
bar := progressbar.New(len(e.UsersRecords[i].Devices[j].Records))
var prevRecord records.EnrichedRecord
for _, record := range e.UsersRecords[i].Devices[j].Records {
if e.skipFailedCmds && record.ExitCode != 0 {
continue
}
candidates := strategy.GetCandidates(records.Stripped(record))
if record.DebugThisRecord {
log.Println()
log.Println("===================================================")
log.Println("STRATEGY:", title, "-", description)
log.Println("===================================================")
log.Println("Previous record:")
if prevRecord.RealtimeBefore == 0 {
log.Println("== NIL")
} else {
rec, _ := prevRecord.ToString()
log.Println(rec)
}
log.Println("---------------------------------------------------")
log.Println("Recommendations for:")
rec, _ := record.ToString()
log.Println(rec)
log.Println("---------------------------------------------------")
for i, candidate := range candidates {
if i > 10 {
break
}
log.Println(string(candidate))
}
log.Println("===================================================")
}
matchFound := false
longestPrefixMatchLength := 0
multiMatch := multiMatchJSON{}
for i, candidate := range candidates {
// make an option (--calculate-total) to turn this on/off ?
// if i >= e.maxCandidates {
// break
// }
commonPrefixLength := len(longestcommon.Prefix([]string{candidate, record.CmdLine}))
if commonPrefixLength > longestPrefixMatchLength {
longestPrefixMatchLength = commonPrefixLength
prefixMatch := multiMatchItemJSON{Distance: i + 1, CharsRecalled: commonPrefixLength}
multiMatch.Match = true
multiMatch.Entries = append(multiMatch.Entries, prefixMatch)
}
if candidate == record.CmdLine {
match := matchJSON{Match: true, Distance: i + 1, CharsRecalled: record.CmdLength}
matchFound = true
strategyData.Matches = append(strategyData.Matches, match)
strategyData.PrefixMatches = append(strategyData.PrefixMatches, multiMatch)
break
}
}
if matchFound == false {
strategyData.Matches = append(strategyData.Matches, matchJSON{})
strategyData.PrefixMatches = append(strategyData.PrefixMatches, multiMatch)
}
err := strategy.AddHistoryRecord(&record)
if err != nil {
log.Println("Error while evauating", err)
return err
}
bar.Add(1)
prevRecord = record
}
strategy.ResetHistory()
fmt.Println()
}
}
e.Strategies = append(e.Strategies, strategyData)
return nil
}
// CalculateStatsAndPlot results
func (e *HistEval) CalculateStatsAndPlot(scriptName string) {
evalJSON, err := json.Marshal(e)
if err != nil {
log.Fatal("json marshal error", err)
}
buffer := bytes.Buffer{}
buffer.Write(evalJSON)
// run python script to stat and plot/
cmd := exec.Command(scriptName)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = &buffer
err = cmd.Run()
if err != nil {
log.Printf("Command finished with error: %v", err)
}
}

@ -1,180 +0,0 @@
package histanal
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"path/filepath"
"github.com/curusarn/resh/pkg/records"
)
type deviceRecords struct {
Name string
Records []records.EnrichedRecord
}
type userRecords struct {
Name string
Devices []deviceRecords
}
// HistLoad loads history
type HistLoad struct {
UsersRecords []userRecords
skipFailedCmds bool
sanitizedInput bool
debugRecords float64
}
func (e *HistLoad) preprocessDeviceRecords(device deviceRecords) deviceRecords {
sessionIDs := map[string]uint64{}
var nextID uint64
nextID = 1 // start with 1 because 0 won't get saved to json
for k, record := range device.Records {
id, found := sessionIDs[record.SessionID]
if found == false {
id = nextID
sessionIDs[record.SessionID] = id
nextID++
}
device.Records[k].SeqSessionID = id
// assert
if record.Sanitized != e.sanitizedInput {
if e.sanitizedInput {
log.Fatal("ASSERT failed: '--sanitized-input' is present but data is not sanitized")
}
log.Fatal("ASSERT failed: data is sanitized but '--sanitized-input' is not present")
}
device.Records[k].SeqSessionID = id
if e.debugRecords > 0 && rand.Float64() < e.debugRecords {
device.Records[k].DebugThisRecord = true
}
}
// sort.SliceStable(device.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
// })
// iterate from back and mark last record of each session
sessionIDSet := map[string]bool{}
for i := len(device.Records) - 1; i >= 0; i-- {
var record *records.EnrichedRecord
record = &device.Records[i]
if sessionIDSet[record.SessionID] {
continue
}
sessionIDSet[record.SessionID] = true
record.LastRecordOfSession = true
}
return device
}
// enrich records and add sequential session ID
func (e *HistLoad) preprocessRecords() {
for i := range e.UsersRecords {
for j := range e.UsersRecords[i].Devices {
e.UsersRecords[i].Devices[j] = e.preprocessDeviceRecords(e.UsersRecords[i].Devices[j])
}
}
}
func (e *HistLoad) loadHistoryRecordsBatchMode(fname string, dataRootPath string) []userRecords {
var records []userRecords
info, err := os.Stat(dataRootPath)
if err != nil {
log.Fatal("Error: Directory", dataRootPath, "does not exist - exiting! (", err, ")")
}
if info.IsDir() == false {
log.Fatal("Error:", dataRootPath, "is not a directory - exiting!")
}
users, err := ioutil.ReadDir(dataRootPath)
if err != nil {
log.Fatal("Could not read directory:", dataRootPath)
}
fmt.Println("Listing users in <", dataRootPath, ">...")
for _, user := range users {
userRecords := userRecords{Name: user.Name()}
userFullPath := filepath.Join(dataRootPath, user.Name())
if user.IsDir() == false {
log.Println("Warn: Unexpected file (not a directory) <", userFullPath, "> - skipping.")
continue
}
fmt.Println()
fmt.Printf("*- %s\n", user.Name())
devices, err := ioutil.ReadDir(userFullPath)
if err != nil {
log.Fatal("Could not read directory:", userFullPath)
}
for _, device := range devices {
deviceRecords := deviceRecords{Name: device.Name()}
deviceFullPath := filepath.Join(userFullPath, device.Name())
if device.IsDir() == false {
log.Println("Warn: Unexpected file (not a directory) <", deviceFullPath, "> - skipping.")
continue
}
fmt.Printf(" \\- %s\n", device.Name())
files, err := ioutil.ReadDir(deviceFullPath)
if err != nil {
log.Fatal("Could not read directory:", deviceFullPath)
}
for _, file := range files {
fileFullPath := filepath.Join(deviceFullPath, file.Name())
if file.Name() == fname {
fmt.Printf(" \\- %s - loading ...", file.Name())
// load the data
deviceRecords.Records = e.loadHistoryRecords(fileFullPath)
fmt.Println(" OK ✓")
} else {
fmt.Printf(" \\- %s - skipped\n", file.Name())
}
}
userRecords.Devices = append(userRecords.Devices, deviceRecords)
}
records = append(records, userRecords)
}
return records
}
func (e *HistLoad) loadHistoryRecords(fname string) []records.EnrichedRecord {
file, err := os.Open(fname)
if err != nil {
log.Fatal("Open() resh history file error:", err)
}
defer file.Close()
var recs []records.EnrichedRecord
scanner := bufio.NewScanner(file)
for scanner.Scan() {
record := records.Record{}
fallbackRecord := records.FallbackRecord{}
line := scanner.Text()
err = json.Unmarshal([]byte(line), &record)
if err != nil {
err = json.Unmarshal([]byte(line), &fallbackRecord)
if err != nil {
log.Println("Line:", line)
log.Fatal("Decoding error:", err)
}
record = records.Convert(&fallbackRecord)
}
if e.sanitizedInput == false {
if record.CmdLength != 0 {
log.Fatal("Assert failed - 'cmdLength' is set in raw data. Maybe you want to use '--sanitized-input' option?")
}
record.CmdLength = len(record.CmdLine)
} else if record.CmdLength == 0 {
log.Fatal("Assert failed - 'cmdLength' is unset in the data. This should not happen.")
}
if !e.skipFailedCmds || record.ExitCode == 0 {
recs = append(recs, records.Enriched(record))
}
}
return recs
}

@ -1,47 +0,0 @@
package strat
import "github.com/curusarn/resh/pkg/records"
// DirectorySensitive prediction/recommendation strategy
type DirectorySensitive struct {
history map[string][]string
lastPwd string
}
// Init see name
func (s *DirectorySensitive) Init() {
s.history = map[string][]string{}
}
// GetTitleAndDescription see name
func (s *DirectorySensitive) GetTitleAndDescription() (string, string) {
return "directory sensitive (recent)", "Use recent commands executed is the same directory"
}
// GetCandidates see name
func (s *DirectorySensitive) GetCandidates() []string {
return s.history[s.lastPwd]
}
// AddHistoryRecord see name
func (s *DirectorySensitive) AddHistoryRecord(record *records.EnrichedRecord) 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
}
// ResetHistory see name
func (s *DirectorySensitive) ResetHistory() error {
s.Init()
s.history = map[string][]string{}
return nil
}

@ -1,29 +0,0 @@
package strat
import "github.com/curusarn/resh/pkg/records"
// Dummy prediction/recommendation strategy
type Dummy struct {
history []string
}
// GetTitleAndDescription see name
func (s *Dummy) GetTitleAndDescription() (string, string) {
return "dummy", "Return empty candidate list"
}
// GetCandidates see name
func (s *Dummy) GetCandidates() []string {
return nil
}
// AddHistoryRecord see name
func (s *Dummy) AddHistoryRecord(record *records.EnrichedRecord) error {
s.history = append(s.history, record.CmdLine)
return nil
}
// ResetHistory see name
func (s *Dummy) ResetHistory() error {
return nil
}

@ -1,91 +0,0 @@
package strat
import (
"math"
"sort"
"strconv"
"github.com/curusarn/resh/pkg/records"
)
// DynamicRecordDistance prediction/recommendation strategy
type DynamicRecordDistance struct {
history []records.EnrichedRecord
DistParams records.DistParams
pwdHistogram map[string]int
realPwdHistogram map[string]int
gitOriginHistogram map[string]int
MaxDepth int
Label string
}
type strDynDistEntry struct {
cmdLine string
distance float64
}
// Init see name
func (s *DynamicRecordDistance) Init() {
s.history = nil
s.pwdHistogram = map[string]int{}
s.realPwdHistogram = map[string]int{}
s.gitOriginHistogram = map[string]int{}
}
// GetTitleAndDescription see name
func (s *DynamicRecordDistance) GetTitleAndDescription() (string, string) {
return "dynamic record distance (depth:" + strconv.Itoa(s.MaxDepth) + ";" + s.Label + ")", "Use TF-IDF record distance to recommend commands"
}
func (s *DynamicRecordDistance) idf(count int) float64 {
return math.Log(float64(len(s.history)) / float64(count))
}
// GetCandidates see name
func (s *DynamicRecordDistance) GetCandidates(strippedRecord records.EnrichedRecord) []string {
if len(s.history) == 0 {
return nil
}
var mapItems []strDynDistEntry
for i, record := range s.history {
if s.MaxDepth != 0 && i > s.MaxDepth {
break
}
distParams := records.DistParams{
Pwd: s.DistParams.Pwd * s.idf(s.pwdHistogram[strippedRecord.PwdAfter]),
RealPwd: s.DistParams.RealPwd * s.idf(s.realPwdHistogram[strippedRecord.RealPwdAfter]),
Git: s.DistParams.Git * s.idf(s.gitOriginHistogram[strippedRecord.GitOriginRemote]),
Time: s.DistParams.Time,
SessionID: s.DistParams.SessionID,
}
distance := record.DistanceTo(strippedRecord, distParams)
mapItems = append(mapItems, strDynDistEntry{record.CmdLine, distance})
}
sort.SliceStable(mapItems, func(i int, j int) bool { return mapItems[i].distance < mapItems[j].distance })
var hist []string
histSet := map[string]bool{}
for _, item := range mapItems {
if histSet[item.cmdLine] {
continue
}
histSet[item.cmdLine] = true
hist = append(hist, item.cmdLine)
}
return hist
}
// AddHistoryRecord see name
func (s *DynamicRecordDistance) AddHistoryRecord(record *records.EnrichedRecord) error {
// append record to front
s.history = append([]records.EnrichedRecord{*record}, s.history...)
s.pwdHistogram[record.Pwd]++
s.realPwdHistogram[record.RealPwd]++
s.gitOriginHistogram[record.GitOriginRemote]++
return nil
}
// ResetHistory see name
func (s *DynamicRecordDistance) ResetHistory() error {
s.Init()
return nil
}

@ -1,53 +0,0 @@
package strat
import (
"sort"
"github.com/curusarn/resh/pkg/records"
)
// Frequent prediction/recommendation strategy
type Frequent struct {
history map[string]int
}
type strFrqEntry struct {
cmdLine string
count int
}
// Init see name
func (s *Frequent) Init() {
s.history = map[string]int{}
}
// GetTitleAndDescription see name
func (s *Frequent) GetTitleAndDescription() (string, string) {
return "frequent", "Use frequent commands"
}
// GetCandidates see name
func (s *Frequent) 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
}
// AddHistoryRecord see name
func (s *Frequent) AddHistoryRecord(record *records.EnrichedRecord) error {
s.history[record.CmdLine]++
return nil
}
// ResetHistory see name
func (s *Frequent) ResetHistory() error {
s.Init()
return nil
}

@ -1,97 +0,0 @@
package strat
import (
"sort"
"strconv"
"github.com/curusarn/resh/pkg/records"
"github.com/mb-14/gomarkov"
)
// MarkovChainCmd prediction/recommendation strategy
type MarkovChainCmd struct {
Order int
history []strMarkCmdHistoryEntry
historyCmds []string
}
type strMarkCmdHistoryEntry struct {
cmd string
cmdLine string
}
type strMarkCmdEntry struct {
cmd string
transProb float64
}
// Init see name
func (s *MarkovChainCmd) Init() {
s.history = nil
s.historyCmds = nil
}
// GetTitleAndDescription see name
func (s *MarkovChainCmd) GetTitleAndDescription() (string, string) {
return "command-based markov chain (order " + strconv.Itoa(s.Order) + ")", "Use command-based markov chain to recommend commands"
}
// GetCandidates see name
func (s *MarkovChainCmd) GetCandidates() []string {
if len(s.history) < s.Order {
var hist []string
for _, item := range s.history {
hist = append(hist, item.cmdLine)
}
return hist
}
chain := gomarkov.NewChain(s.Order)
chain.Add(s.historyCmds)
cmdsSet := map[string]bool{}
var entries []strMarkCmdEntry
for _, cmd := range s.historyCmds {
if cmdsSet[cmd] {
continue
}
cmdsSet[cmd] = true
prob, _ := chain.TransitionProbability(cmd, s.historyCmds[len(s.historyCmds)-s.Order:])
entries = append(entries, strMarkCmdEntry{cmd: cmd, transProb: prob})
}
sort.Slice(entries, func(i int, j int) bool { return entries[i].transProb > entries[j].transProb })
var hist []string
histSet := map[string]bool{}
for i := len(s.history) - 1; i >= 0; i-- {
if histSet[s.history[i].cmdLine] {
continue
}
histSet[s.history[i].cmdLine] = true
if s.history[i].cmd == entries[0].cmd {
hist = append(hist, s.history[i].cmdLine)
}
}
// log.Println("################")
// log.Println(s.history[len(s.history)-s.order:])
// log.Println(" -> ")
// x := math.Min(float64(len(hist)), 3)
// log.Println(entries[:int(x)])
// x = math.Min(float64(len(hist)), 5)
// log.Println(hist[:int(x)])
// log.Println("################")
return hist
}
// AddHistoryRecord see name
func (s *MarkovChainCmd) AddHistoryRecord(record *records.EnrichedRecord) error {
s.history = append(s.history, strMarkCmdHistoryEntry{cmdLine: record.CmdLine, cmd: record.Command})
s.historyCmds = append(s.historyCmds, record.Command)
// s.historySet[record.CmdLine] = true
return nil
}
// ResetHistory see name
func (s *MarkovChainCmd) ResetHistory() error {
s.Init()
return nil
}

@ -1,76 +0,0 @@
package strat
import (
"sort"
"strconv"
"github.com/curusarn/resh/pkg/records"
"github.com/mb-14/gomarkov"
)
// MarkovChain prediction/recommendation strategy
type MarkovChain struct {
Order int
history []string
}
type strMarkEntry struct {
cmdLine string
transProb float64
}
// Init see name
func (s *MarkovChain) Init() {
s.history = nil
}
// GetTitleAndDescription see name
func (s *MarkovChain) GetTitleAndDescription() (string, string) {
return "markov chain (order " + strconv.Itoa(s.Order) + ")", "Use markov chain to recommend commands"
}
// GetCandidates see name
func (s *MarkovChain) GetCandidates() []string {
if len(s.history) < s.Order {
return s.history
}
chain := gomarkov.NewChain(s.Order)
chain.Add(s.history)
cmdLinesSet := map[string]bool{}
var entries []strMarkEntry
for _, cmdLine := range s.history {
if cmdLinesSet[cmdLine] {
continue
}
cmdLinesSet[cmdLine] = true
prob, _ := chain.TransitionProbability(cmdLine, s.history[len(s.history)-s.Order:])
entries = append(entries, strMarkEntry{cmdLine: cmdLine, transProb: prob})
}
sort.Slice(entries, func(i int, j int) bool { return entries[i].transProb > entries[j].transProb })
var hist []string
for _, item := range entries {
hist = append(hist, item.cmdLine)
}
// log.Println("################")
// log.Println(s.history[len(s.history)-s.order:])
// log.Println(" -> ")
// x := math.Min(float64(len(hist)), 5)
// log.Println(hist[:int(x)])
// log.Println("################")
return hist
}
// AddHistoryRecord see name
func (s *MarkovChain) AddHistoryRecord(record *records.EnrichedRecord) error {
s.history = append(s.history, record.CmdLine)
// s.historySet[record.CmdLine] = true
return nil
}
// ResetHistory see name
func (s *MarkovChain) ResetHistory() error {
s.Init()
return nil
}

@ -1,57 +0,0 @@
package strat
import (
"math/rand"
"time"
"github.com/curusarn/resh/pkg/records"
)
// Random prediction/recommendation strategy
type Random struct {
CandidatesSize int
history []string
historySet map[string]bool
}
// Init see name
func (s *Random) Init() {
s.history = nil
s.historySet = map[string]bool{}
}
// GetTitleAndDescription see name
func (s *Random) GetTitleAndDescription() (string, string) {
return "random", "Use random commands"
}
// GetCandidates see name
func (s *Random) GetCandidates() []string {
seed := time.Now().UnixNano()
rand.Seed(seed)
var candidates []string
candidateSet := map[string]bool{}
for len(candidates) < s.CandidatesSize && len(candidates)*2 < len(s.historySet) {
x := rand.Intn(len(s.history))
candidate := s.history[x]
if candidateSet[candidate] == false {
candidateSet[candidate] = true
candidates = append(candidates, candidate)
continue
}
}
return candidates
}
// AddHistoryRecord see name
func (s *Random) AddHistoryRecord(record *records.EnrichedRecord) error {
s.history = append([]string{record.CmdLine}, s.history...)
s.historySet[record.CmdLine] = true
return nil
}
// ResetHistory see name
func (s *Random) ResetHistory() error {
s.Init()
return nil
}

@ -1,56 +0,0 @@
package strat
import "github.com/curusarn/resh/pkg/records"
// RecentBash prediction/recommendation strategy
type RecentBash struct {
histfile []string
histfileSnapshot map[string][]string
history map[string][]string
}
// Init see name
func (s *RecentBash) Init() {
s.histfileSnapshot = map[string][]string{}
s.history = map[string][]string{}
}
// GetTitleAndDescription see name
func (s *RecentBash) GetTitleAndDescription() (string, string) {
return "recent (bash-like)", "Behave like bash"
}
// GetCandidates see name
func (s *RecentBash) GetCandidates(strippedRecord records.EnrichedRecord) []string {
// populate the local history from histfile
if s.histfileSnapshot[strippedRecord.SessionID] == nil {
s.histfileSnapshot[strippedRecord.SessionID] = s.histfile
}
return append(s.history[strippedRecord.SessionID], s.histfileSnapshot[strippedRecord.SessionID]...)
}
// AddHistoryRecord see name
func (s *RecentBash) AddHistoryRecord(record *records.EnrichedRecord) error {
// remove previous occurance of record
for i, cmd := range s.history[record.SessionID] {
if cmd == record.CmdLine {
s.history[record.SessionID] = append(s.history[record.SessionID][:i], s.history[record.SessionID][i+1:]...)
}
}
// append new record
s.history[record.SessionID] = append([]string{record.CmdLine}, s.history[record.SessionID]...)
if record.LastRecordOfSession {
// append history of the session to histfile and clear session history
s.histfile = append(s.history[record.SessionID], s.histfile...)
s.histfileSnapshot[record.SessionID] = nil
s.history[record.SessionID] = nil
}
return nil
}
// ResetHistory see name
func (s *RecentBash) ResetHistory() error {
s.Init()
return nil
}

@ -1,37 +0,0 @@
package strat
import "github.com/curusarn/resh/pkg/records"
// Recent prediction/recommendation strategy
type Recent struct {
history []string
}
// GetTitleAndDescription see name
func (s *Recent) GetTitleAndDescription() (string, string) {
return "recent", "Use recent commands"
}
// GetCandidates see name
func (s *Recent) GetCandidates() []string {
return s.history
}
// AddHistoryRecord see name
func (s *Recent) AddHistoryRecord(record *records.EnrichedRecord) error {
// remove previous occurance of record
for i, cmd := range s.history {
if cmd == record.CmdLine {
s.history = append(s.history[:i], s.history[i+1:]...)
}
}
// append new record
s.history = append([]string{record.CmdLine}, s.history...)
return nil
}
// ResetHistory see name
func (s *Recent) ResetHistory() error {
s.history = nil
return nil
}

@ -1,70 +0,0 @@
package strat
import (
"sort"
"strconv"
"github.com/curusarn/resh/pkg/records"
)
// RecordDistance prediction/recommendation strategy
type RecordDistance struct {
history []records.EnrichedRecord
DistParams records.DistParams
MaxDepth int
Label string
}
type strDistEntry struct {
cmdLine string
distance float64
}
// Init see name
func (s *RecordDistance) Init() {
s.history = nil
}
// GetTitleAndDescription see name
func (s *RecordDistance) GetTitleAndDescription() (string, string) {
return "record distance (depth:" + strconv.Itoa(s.MaxDepth) + ";" + s.Label + ")", "Use record distance to recommend commands"
}
// GetCandidates see name
func (s *RecordDistance) GetCandidates(strippedRecord records.EnrichedRecord) []string {
if len(s.history) == 0 {
return nil
}
var mapItems []strDistEntry
for i, record := range s.history {
if s.MaxDepth != 0 && i > s.MaxDepth {
break
}
distance := record.DistanceTo(strippedRecord, s.DistParams)
mapItems = append(mapItems, strDistEntry{record.CmdLine, distance})
}
sort.SliceStable(mapItems, func(i int, j int) bool { return mapItems[i].distance < mapItems[j].distance })
var hist []string
histSet := map[string]bool{}
for _, item := range mapItems {
if histSet[item.cmdLine] {
continue
}
histSet[item.cmdLine] = true
hist = append(hist, item.cmdLine)
}
return hist
}
// AddHistoryRecord see name
func (s *RecordDistance) AddHistoryRecord(record *records.EnrichedRecord) error {
// append record to front
s.history = append([]records.EnrichedRecord{*record}, s.history...)
return nil
}
// ResetHistory see name
func (s *RecordDistance) ResetHistory() error {
s.Init()
return nil
}

@ -1,46 +0,0 @@
package strat
import (
"github.com/curusarn/resh/pkg/records"
)
// ISimpleStrategy interface
type ISimpleStrategy interface {
GetTitleAndDescription() (string, string)
GetCandidates() []string
AddHistoryRecord(record *records.EnrichedRecord) error
ResetHistory() error
}
// IStrategy interface
type IStrategy interface {
GetTitleAndDescription() (string, string)
GetCandidates(r records.EnrichedRecord) []string
AddHistoryRecord(record *records.EnrichedRecord) error
ResetHistory() error
}
type simpleStrategyWrapper struct {
strategy ISimpleStrategy
}
// NewSimpleStrategyWrapper returns IStrategy created by wrapping given ISimpleStrategy
func NewSimpleStrategyWrapper(strategy ISimpleStrategy) *simpleStrategyWrapper {
return &simpleStrategyWrapper{strategy: strategy}
}
func (s *simpleStrategyWrapper) GetTitleAndDescription() (string, string) {
return s.strategy.GetTitleAndDescription()
}
func (s *simpleStrategyWrapper) GetCandidates(r records.EnrichedRecord) []string {
return s.strategy.GetCandidates()
}
func (s *simpleStrategyWrapper) AddHistoryRecord(r *records.EnrichedRecord) error {
return s.strategy.AddHistoryRecord(r)
}
func (s *simpleStrategyWrapper) ResetHistory() error {
return s.strategy.ResetHistory()
}
Loading…
Cancel
Save