database: Fetch and index fork and stars for GitHub and BitBucket.
Store and show if a package is a fork, how many imports and stars
(followers in BitBucket) for each package in the search result list.
Currently we have over 90% packages coming from these two VCS's.
Corresponding UI change is made to show these additional information
under each package's import path.
Change-Id: I669755d4b905f360918d38e8600534a61a449ba4
Reviewed-on: https://go-review.googlesource.com/24173
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/database/database.go b/database/database.go
index 914ec97..2f952ac 100644
--- a/database/database.go
+++ b/database/database.go
@@ -60,8 +60,11 @@
}
type Package struct {
- Path string `json:"path"`
- Synopsis string `json:"synopsis,omitempty"`
+ Path string `json:"path"`
+ ImportCount int `json:"import_count`
+ Synopsis string `json:"synopsis,omitempty"`
+ Fork bool `json:"fork,omitempty"`
+ Stars int `json:"stars,omitempty"`
}
type byPath []Package
diff --git a/database/indexae.go b/database/indexae.go
index 8000761..877e6ea 100644
--- a/database/indexae.go
+++ b/database/indexae.go
@@ -27,6 +27,8 @@
Synopsis string
Score float64
ImportCount float64
+ Stars float64
+ Fork search.Atom
}
// PutIndex creates or updates a package entry in the search index. id identifies the document in the index.
@@ -55,9 +57,15 @@
// Update document information accordingly.
if pdoc != nil {
- pkg.Name = search.Atom(pdoc.ProjectName)
+ pkg.Name = search.Atom(pdoc.Name)
pkg.Path = pdoc.ImportPath
pkg.Synopsis = pdoc.Synopsis
+ pkg.Stars = float64(pdoc.Stars)
+ var fork string
+ if forkAvailable(pdoc.ImportPath) {
+ fork = fmt.Sprint(pdoc.Fork) // "true" or "false"
+ }
+ pkg.Fork = search.Atom(fork)
}
if score >= 0 {
pkg.Score = score
@@ -70,6 +78,10 @@
return nil
}
+func forkAvailable(p string) bool {
+ return strings.HasPrefix(p, "github.com") || strings.HasPrefix(p, "bitbucket.org")
+}
+
// Search searches the packages index for a given query. A path-like query string
// will be passed in unchanged, whereas single words will be stemmed.
func Search(c context.Context, q string) ([]Package, error) {
@@ -86,15 +98,26 @@
},
}
for it := index.Search(c, parseQuery2(q), opt); ; {
- var pkg PackageDocument
- _, err := it.Next(&pkg)
+ var pd PackageDocument
+ _, err := it.Next(&pd)
if err == search.Done {
break
}
if err != nil {
return nil, err
}
- pkgs = append(pkgs, Package{pkg.Path, pkg.Synopsis})
+ pkg := Package{
+ Path: pd.Path,
+ ImportCount: int(pd.ImportCount),
+ Synopsis: pd.Synopsis,
+ }
+ if pd.Fork == "true" {
+ pkg.Fork = true
+ }
+ if pd.Stars > 0 {
+ pkg.Stars = int(pd.Stars)
+ }
+ pkgs = append(pkgs, pkg)
}
return pkgs, nil
}
diff --git a/doc/builder.go b/doc/builder.go
index 4b20837..a4614f0 100644
--- a/doc/builder.go
+++ b/doc/builder.go
@@ -400,6 +400,13 @@
// Version control: belongs to a dead end fork
DeadEndFork bool
+ // Whether the package is a fork of another one.
+ Fork bool
+
+ // How many stars (for a GitHub project) or followers (for a BitBucket
+ // project) the repository of this package has.
+ Stars int
+
// The time this object was created.
Updated time.Time
@@ -498,6 +505,8 @@
VCS: dir.VCS,
DeadEndFork: dir.DeadEndFork,
Subdirectories: dir.Subdirectories,
+ Fork: dir.Fork,
+ Stars: dir.Stars,
}
var b builder
diff --git a/gddo-server/assets/site.css b/gddo-server/assets/site.css
index cf11b78..52789c2 100644
--- a/gddo-server/assets/site.css
+++ b/gddo-server/assets/site.css
@@ -126,3 +126,14 @@
font-size:16px;
}
}
+
+.synopsis {
+ opacity: 0.87;
+}
+
+.additional-info {
+ display: block;
+ opacity: 0.54;
+ text-transform: uppercase;
+ font-size: 0.75em;
+}
diff --git a/gddo-server/assets/templates/common.html b/gddo-server/assets/templates/common.html
index dc3d85b..2754b88 100644
--- a/gddo-server/assets/templates/common.html
+++ b/gddo-server/assets/templates/common.html
@@ -37,11 +37,30 @@
</div>{{end}}
{{define "Pkgs"}}
- <table class="table table-condensed">
+ <table class="table table-condensed">
+ <thead><tr><th>Path</th><th>Synopsis</th></tr></thead>
+ <tbody>{{range .}}<tr><td>{{if .Path|isValidImportPath}}<a href="/{{.Path}}">{{.Path|importPath}}</a>{{else}}{{.Path|importPath}}{{end}}</td><td>{{.Synopsis|importPath}}</td></tr>
+ {{end}}</tbody>
+ </table>
+{{end}}
+
+{{define "SearchPkgs"}}
+ <table class="table table-condensed">
<thead><tr><th>Path</th><th>Synopsis</th></tr></thead>
- <tbody>{{range .}}<tr><td>{{if .Path|isValidImportPath}}<a href="/{{.Path}}">{{.Path|importPath}}</a>{{else}}{{.Path|importPath}}{{end}}</td><td>{{.Synopsis|importPath}}</td></tr>
+ <tbody>{{range .}}
+ <tr><td>
+ {{if .Path|isValidImportPath}}
+ <a href="/{{.Path}}">{{.Path|importPath}}</a>
+ <ul class="list-inline">
+ <li class="additional-info">{{.ImportCount}} imports</li>
+ {{if .Fork}}<li class="additional-info">· fork</li>{{end}}
+ {{if .Stars}}<li class="additional-info">· {{.Stars}} stars</li>{{end}}
+ </ul>
+ {{else}}{{.Path|importPath}}</td>
+ {{end}}
+ <td class="synopsis">{{.Synopsis|importPath}}</td></tr>
{{end}}</tbody>
- </table>
+ </table>
{{end}}
{{define "PkgCmdHeader"}}{{with .pdoc}}
diff --git a/gddo-server/assets/templates/results.html b/gddo-server/assets/templates/results.html
index 5160a9b..ad56976 100644
--- a/gddo-server/assets/templates/results.html
+++ b/gddo-server/assets/templates/results.html
@@ -4,10 +4,10 @@
<div class="well">
{{template "SearchBox" .q}}
</div>
- <p>Try this search on <a href="http://go-search.org/search?q={{.q}}">Go-Search</a>
+ <p>Try this search on <a href="http://go-search.org/search?q={{.q}}">Go-Search</a>
or <a href="https://github.com/search?q={{.q}}+language:go">GitHub</a>.
{{if .pkgs}}
- {{template "Pkgs" .pkgs}}
+ {{template "SearchPkgs" .pkgs}}
{{else}}
<p>No packages found.
{{end}}
diff --git a/gosrc/bitbucket.go b/gosrc/bitbucket.go
index 437349c..3593cc5 100644
--- a/gosrc/bitbucket.go
+++ b/gosrc/bitbucket.go
@@ -30,6 +30,8 @@
ForkOf struct {
Scm string
} `json:"fork_of"`
+ Followers int `json:"followers"`
+ IsFork bool `json:"is_fork"`
}
func getBitbucketDir(client *http.Client, match map[string]string, savedEtag string) (*Directory, error) {
@@ -115,6 +117,8 @@
Subdirectories: contents.Directories,
VCS: match["vcs"],
DeadEndFork: isBitbucketDeadEndFork(repo),
+ Fork: repo.IsFork,
+ Stars: repo.Followers,
}, nil
}
diff --git a/gosrc/github.go b/gosrc/github.go
index 5ae969f..e02fb32 100644
--- a/gosrc/github.go
+++ b/gosrc/github.go
@@ -162,6 +162,7 @@
var repo = struct {
Fork bool `json:"fork"`
+ Stars int `json:"stars"`
CreatedAt time.Time `json:"created_at"`
PushedAt time.Time `json:"pushed_at"`
}{}
@@ -183,6 +184,8 @@
Subdirectories: subdirs,
VCS: "git",
DeadEndFork: isDeadEndFork,
+ Fork: repo.Fork,
+ Stars: repo.Stars,
}, nil
}
diff --git a/gosrc/gosrc.go b/gosrc/gosrc.go
index 1505737..b30c360 100644
--- a/gosrc/gosrc.go
+++ b/gosrc/gosrc.go
@@ -70,6 +70,13 @@
// followed by one %d (source line number), or be empty string if not available.
// Example: "%s#L%d".
LineFmt string
+
+ // Whether the repository of this directory is a fork of another one.
+ Fork bool
+
+ // How many stars (for a GitHub project) or followers (for a BitBucket
+ // project) the repository of this directory has.
+ Stars int
}
// Project represents a repository.