Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add history function for the past page view. #53

Merged
merged 6 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 112 additions & 1 deletion cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cache

import (
"archive/zip"
"bufio"
"fmt"
"io"
"io/ioutil"
Expand All @@ -12,13 +13,15 @@ import (
"os/user"
"path"
"path/filepath"
"strconv"
"strings"
"time"
)

const (
indexJSON = "index.json"
pagesDirectory = "pages"
historyPath = "/history"
pageSuffix = ".md"
zipPath = "/tldr.zip"
)
Expand All @@ -32,6 +35,16 @@ type Repository struct {
ttl time.Duration
}

// HistoryRecord represent the search history of certain page
type HistoryRecord struct {
page string
count int
}

func (h HistoryRecord) String() string {
return fmt.Sprintf("%s %d", h.page, h.count)
}

// NewRepository returns a new cache repository. The data is loaded from the
// remote if missing or stale.
func NewRepository(remote string, ttl time.Duration) (*Repository, error) {
Expand Down Expand Up @@ -204,7 +217,21 @@ func (r *Repository) loadFromRemote() error {
}

func (r *Repository) makeCacheDir() error {
return os.MkdirAll(r.directory, 0755)
if err := os.MkdirAll(r.directory, 0755); err != nil {
return fmt.Errorf("ERROR: creating directory %s: %s", r.directory, err)
}
// touching the history file.
historyFile := path.Join(r.directory, historyPath)
return touchFile(historyFile)
}

func touchFile(fileName string) error {
file, err := os.Create(fileName)
if err != nil {
return fmt.Errorf("ERROR: creating file %s: %s", fileName, err)
}
defer file.Close()
return nil
}

func (r *Repository) unzip() error {
Expand Down Expand Up @@ -259,3 +286,87 @@ func (r Repository) isReachable() bool {
_, err = net.DialTimeout("tcp", u.Hostname()+":"+u.Port(), timeout)
return err == nil
}

func (r Repository) RecordHistory(page string) error {
records, err := r.LoadHistory()
if err != nil {
return fmt.Errorf("ERROR: loading history failed %s", err)
}

newRecord := HistoryRecord{
page: page,
count: 1,
}

foundIdx := -1
for idx, r := range *records {
if r.page == page {
newRecord.count = r.count + 1
foundIdx = idx
break
}
}

if foundIdx != -1 { //found in history, we want to put the last search at the end of the history.
newRecords := append((*records)[:foundIdx], (*records)[foundIdx+1:]...)
records = &newRecords
}

newRecords := append(*records, newRecord)
return r.saveHistory(&newRecords)
}

func (r Repository) saveHistory(history *[]HistoryRecord) error {
hisFile := path.Join(r.directory, historyPath)
inFile, err := os.Create(hisFile)
if err != nil {
return fmt.Errorf("ERROR: opening history file %s", hisFile)
}
defer inFile.Close()

for _, his := range *history {
fmt.Fprintln(inFile, fmt.Sprintf("%s,%d", his.page, his.count))
}
return nil
}

func (r Repository) LoadHistory() (*[]HistoryRecord, error) {
// read the history file line by line, into a map.
history := path.Join(r.directory, historyPath)
//if it is not exist, touch it.
_, err := os.Stat(history)

if os.IsNotExist(err) {
if err := touchFile(history); err != nil {
return nil, fmt.Errorf("ERROR: cannot create the history file %s, %s", history, err)
}
}

inFile, err := os.Open(history)
if err != nil {
return nil, fmt.Errorf("ERROR: opening history file %s", history)

}

defer inFile.Close()

scanner := bufio.NewScanner(inFile)
historyRecords := make([]HistoryRecord, 0, 10)

for scanner.Scan() {
line := scanner.Text()
lineParts := strings.Split(line, ",")
count, err := strconv.Atoi(lineParts[1])

if err != nil {
return nil, err
}

historyRecords = append(historyRecords, HistoryRecord{
page: lineParts[0],
count: count,
})
}

return &historyRecords, nil
}
68 changes: 64 additions & 4 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,13 @@ func TestMarkdown(t *testing.T) {
_, err := r.Markdown("linux", "hostname")

if err != nil {
t.Error("Exptected to successfully pull a page from the cache")
t.Error("Expected to successfully pull a page from the cache")
}

_, err = r.Markdown("linux", "hostnamee")

if err == nil {
t.Error("Exptected to return an error for non existing pages")
t.Error("Expected to return an error for non existing pages")
}
}

Expand All @@ -127,10 +127,70 @@ func TestPages(t *testing.T) {
pages, err := r.Pages()

if err != nil {
t.Error("Exptected to successfully retrieve all pages.")
t.Error("Expected to successfully retrieve all pages.")
}

if len(pages) == 0 {
t.Error("Exptected to find some pages")
t.Error("Expected to find some pages")
}
}

func TestHistory(t *testing.T) {

repo := Repository{
directory: "/tmp/.cache/tldr",
remote: "https://tldr.sh/assets/tldr.zip",
ttl: time.Hour * 24 * 7,
}

repo.makeCacheDir()

if err2 := repo.RecordHistory("git-pull"); err2 != nil {
t.Error("Expected to record history successful.")
}

repo.RecordHistory("git-pull")
repo.RecordHistory("git-pull")
repo.RecordHistory("git-push")
repo.RecordHistory("git-push")
repo.RecordHistory("git-fetch")
repo.RecordHistory("git-pull")

history, err := repo.LoadHistory()
if err != nil {
t.Error("Expected to load history successful.")
}

length := len(*history)
if length != 3 {
t.Error("Expected to have 3 history records.")
}

rec1 := HistoryRecord{
page: "git-push",
count: 2,
}

if (*history)[0] != rec1 {
t.Errorf("Expected first record to be %+v", rec1)
}

rec2 := HistoryRecord{
page: "git-fetch",
count: 1,
}

if (*history)[1] != rec2 {
t.Errorf("Expected second record to be %+v", rec2)
}

rec3 := HistoryRecord{
page: "git-pull",
count: 4,
}

if (*history)[2] != rec3 {
t.Errorf("Expected third record to be %+v", rec3)
}

}
35 changes: 35 additions & 0 deletions cmd/tldr/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"fmt"
"log"
"math"
"math/rand"
"os"
"runtime"
Expand All @@ -22,6 +23,7 @@ const (
updateUsage = "update local database"
versionUsage = "print version and exit"
randomUsage = "prints a random page"
historyUsage = "show the latest search history"
)

const (
Expand Down Expand Up @@ -106,6 +108,11 @@ func printPage(page string) {
if err != nil {
log.Fatalf("ERROR: writing markdown: %s", err)
}

err2 := repository.RecordHistory(page)
if err2 != nil {
log.Fatalf("ERROR: saving history: %s", err2)
}
}

func printPageForPlatform(page string, platform string) {
Expand Down Expand Up @@ -156,6 +163,29 @@ func updatePages() {
}
}

func printHistory() {
repository, err := cache.NewRepository(remoteURL, ttl)
if err != nil {
log.Fatalf("ERROR: creating cache repository: %s", err)
}

history, err := repository.LoadHistory()
if err != nil {
log.Fatalf("ERROR: error loading history: %s", err)
}

hisLen := len(*history)
if hisLen == 0 {
fmt.Println("No history is available yet")
} else { //default print last 10.
size := int(math.Min(10, float64(hisLen)))
for i := 1; i <= size; i++ {
record := (*history)[hisLen-i]
fmt.Printf("%s\n", record)
}
}
}

func main() {
version := flag.Bool("version", false, versionUsage)
flag.BoolVar(version, "v", false, versionUsage)
Expand All @@ -176,6 +206,9 @@ func main() {
random := flag.Bool("random", false, randomUsage)
flag.BoolVar(random, "r", false, randomUsage)

history := flag.Bool("history", false, historyUsage)
flag.BoolVar(history, "t", false, historyUsage)

flag.Parse()

if *version {
Expand All @@ -191,6 +224,8 @@ func main() {
printPageForPlatform(page, *platform)
} else if *random {
printRandomPage()
} else if *history {
printHistory()
} else {
page := flag.Arg(0)
printPage(page)
Expand Down