Weekly digest option.

This commit is contained in:
Mihai Parparita 2014-09-27 15:52:57 -07:00
parent 71ef392cfa
commit 40540fd91f
5 changed files with 122 additions and 31 deletions

View file

@ -23,6 +23,8 @@ type Account struct {
TimezoneLocation *time.Location `datastore:"-,"` TimezoneLocation *time.Location `datastore:"-,"`
ExcludedRepoIds []int `datastore:",noindex"` ExcludedRepoIds []int `datastore:",noindex"`
DigestEmailAddress string DigestEmailAddress string
Frequency string
WeeklyDay time.Weekday
} }
func getAccount(c appengine.Context, githubUserId int) (*Account, error) { func getAccount(c appengine.Context, githubUserId int) (*Account, error) {
@ -49,6 +51,9 @@ func initAccount(account *Account) error {
if len(account.TimezoneName) == 0 { if len(account.TimezoneName) == 0 {
account.TimezoneName = "America/Los_Angeles" account.TimezoneName = "America/Los_Angeles"
} }
if len(account.Frequency) == 0 {
account.Frequency = "daily"
}
account.TimezoneLocation, err = time.LoadLocation(account.TimezoneName) account.TimezoneLocation, err = time.LoadLocation(account.TimezoneName)
if err != nil { if err != nil {
return err return err
@ -56,18 +61,20 @@ func initAccount(account *Account) error {
return nil return nil
} }
func getAllAccountGithubUserIds(c appengine.Context) ([]int, error) { func getAllAccounts(c appengine.Context) ([]Account, error) {
q := datastore.NewQuery("Account") q := datastore.NewQuery("Account")
var accounts []Account var accounts []Account
_, err := q.GetAll(c, &accounts) _, err := q.GetAll(c, &accounts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
result := make([]int, len(accounts))
for i := range accounts { for i := range accounts {
result[i] = accounts[i].GitHubUserId err = initAccount(&accounts[i])
if err != nil {
return nil, err
} }
return result, nil }
return accounts, nil
} }
func (account *Account) IsRepoIdExcluded(repoId int) bool { func (account *Account) IsRepoIdExcluded(repoId int) bool {

View file

@ -13,8 +13,9 @@ import (
const ( const (
CommitDisplayDateFormat = "3:04pm" CommitDisplayDateFormat = "3:04pm"
CommitDisplayDateTooltipFormat = "Monday January 2 3:04pm" CommitDisplayDateFullFormat = "Monday January 2 3:04pm"
DigestDisplayDateFormat = "January 2, 2006" DigestDisplayDateFormat = "January 2, 2006"
DigestDisplayShortDateFormat = "January 2"
DigestDisplayDayOfWeekFormat = "Monday" DigestDisplayDayOfWeekFormat = "Monday"
) )
@ -52,12 +53,16 @@ func (commit DigestCommit) DisplayDate() string {
return commit.PushDate.Format(CommitDisplayDateFormat) return commit.PushDate.Format(CommitDisplayDateFormat)
} }
func (commit DigestCommit) WeeklyDisplayDate() string {
return commit.PushDate.Format(CommitDisplayDateFullFormat)
}
func (commit DigestCommit) DisplayDateTooltip() string { func (commit DigestCommit) DisplayDateTooltip() string {
// But show the full details in a tooltip // But show the full details in a tooltip
return fmt.Sprintf( return fmt.Sprintf(
"Pushed at %s\nCommited at %s", "Pushed at %s\nCommited at %s",
commit.PushDate.Format(CommitDisplayDateTooltipFormat), commit.PushDate.Format(CommitDisplayDateFullFormat),
commit.CommitDate.Format(CommitDisplayDateTooltipFormat)) commit.CommitDate.Format(CommitDisplayDateFullFormat))
} }
type RepoDigest struct { type RepoDigest struct {
@ -76,6 +81,7 @@ type IntervalDigest struct {
yearDelta int yearDelta int
StartTime time.Time StartTime time.Time
EndTime time.Time EndTime time.Time
Weekly bool
RepoDigests []*RepoDigest RepoDigests []*RepoDigest
repos []*Repo repos []*Repo
} }
@ -116,6 +122,8 @@ func (digest *IntervalDigest) Description() string {
} else { } else {
formattedRepoCount = fmt.Sprintf("%d repositories", repoCount) formattedRepoCount = fmt.Sprintf("%d repositories", repoCount)
} }
if !digest.Weekly {
dayOfWeek := digest.StartTime.Format(DigestDisplayDayOfWeekFormat) dayOfWeek := digest.StartTime.Format(DigestDisplayDayOfWeekFormat)
// Insert a zero-width space inside the day of the week so that Gmail's // Insert a zero-width space inside the day of the week so that Gmail's
// event detection doesn't pick it up. // event detection doesn't pick it up.
@ -127,6 +135,22 @@ func (digest *IntervalDigest) Description() string {
formattedRepoCount) formattedRepoCount)
} }
formattedEndTime := digest.EndTime.Format(DigestDisplayDateFormat)
var formattedStartTime string
if digest.StartTime.Year() == digest.EndTime.Year() {
formattedStartTime = digest.StartTime.Format(DigestDisplayShortDateFormat)
} else {
formattedStartTime = digest.StartTime.Format(DigestDisplayDateFormat)
}
formattedStartTime = fmt.Sprintf("%s\u200B%s", formattedStartTime[:1], formattedStartTime[1:])
formattedEndTime = fmt.Sprintf("%s\u200B%s", formattedEndTime[:1], formattedEndTime[1:])
return fmt.Sprintf("You had %s in %s the week of %s to %s.",
formattedCommitCount,
formattedRepoCount,
formattedStartTime,
formattedEndTime)
}
type Digest struct { type Digest struct {
User *github.User User *github.User
TimezoneLocation *time.Location TimezoneLocation *time.Location
@ -152,7 +176,11 @@ func newDigest(c appengine.Context, githubClient *github.Client, account *Accoun
if digestStartTime.Before(oldestDigestTime) { if digestStartTime.Before(oldestDigestTime) {
break break
} }
digestEndTime := digestStartTime.AddDate(0, 0, 1) daysInDigest := 1
if account.Frequency == "weekly" {
daysInDigest = 7
}
digestEndTime := digestStartTime.AddDate(0, 0, daysInDigest)
// Only look at repos that may have activity in the digest interval. // Only look at repos that may have activity in the digest interval.
var intervalRepos []*Repo var intervalRepos []*Repo
@ -169,6 +197,7 @@ func newDigest(c appengine.Context, githubClient *github.Client, account *Accoun
RepoDigests: make([]*RepoDigest, 0, len(intervalRepos)), RepoDigests: make([]*RepoDigest, 0, len(intervalRepos)),
StartTime: digestStartTime, StartTime: digestStartTime,
EndTime: digestEndTime, EndTime: digestEndTime,
Weekly: account.Frequency == "weekly",
}) })
} }

View file

@ -238,15 +238,23 @@ func sendDigestHandler(w http.ResponseWriter, r *http.Request) {
func digestCronHandler(w http.ResponseWriter, r *http.Request) { func digestCronHandler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r) c := appengine.NewContext(r)
githubUserIds, err := getAllAccountGithubUserIds(c) accounts, err := getAllAccounts(c)
if err != nil { if err != nil {
c.Errorf("Error looking up accounts: %s", err.Error()) c.Errorf("Error looking up accounts: %s", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
for _, githubUserId := range githubUserIds { for _, account := range accounts {
c.Infof("Enqueing task for %d...", githubUserId) if account.Frequency == "weekly" {
sendDigestForAccountFunc.Call(c, githubUserId) now := time.Now().In(account.TimezoneLocation)
if now.Weekday() != account.WeeklyDay {
c.Infof("Skipping %d, since it wants weekly digests on %ss and today is a %s.",
account.GitHubUserId, account.WeeklyDay, now.Weekday())
continue
}
}
c.Infof("Enqueing task for %d...", account.GitHubUserId)
sendDigestForAccountFunc.Call(c, account.GitHubUserId)
} }
fmt.Fprint(w, "Done") fmt.Fprint(w, "Done")
} }
@ -421,6 +429,14 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
account.Frequency = r.FormValue("frequency")
weeklyDay, err := strconv.Atoi(r.FormValue("weekly_day"))
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
account.WeeklyDay = time.Weekday(weeklyDay)
timezoneName := r.FormValue("timezone_name") timezoneName := r.FormValue("timezone_name")
_, err = time.LoadLocation(timezoneName) _, err = time.LoadLocation(timezoneName)
if err != nil { if err != nil {

View file

@ -15,6 +15,31 @@
<form method="POST" action="{{routeUrl "save-settings"}}"> <form method="POST" action="{{routeUrl "save-settings"}}">
<div class="setting">
<label>
Frequency:
<select name="frequency" id="frequency" onchange="updateWeeklyDayContainer()">
<option value="daily" {{if eq "daily" .Account.Frequency}}selected{{end}}>Daily</option>
<option value="weekly" {{if eq "weekly" .Account.Frequency}}selected{{end}}>Weekly</option>
</select>
<span id="weekly-day-container">
on
<select name="weekly_day">
<option value="0" {{if eq 0 .Account.WeeklyDay}}selected{{end}}>Sundays</option>
<option value="1" {{if eq 1 .Account.WeeklyDay}}selected{{end}}>Mondays</option>
<option value="2" {{if eq 2 .Account.WeeklyDay}}selected{{end}}>Tuesdays</option>
<option value="3" {{if eq 3 .Account.WeeklyDay}}selected{{end}}>Wednesdays</option>
<option value="4" {{if eq 4 .Account.WeeklyDay}}selected{{end}}>Thursdays</option>
<option value="5" {{if eq 5 .Account.WeeklyDay}}selected{{end}}>Fridays</option>
<option value="6" {{if eq 6 .Account.WeeklyDay}}selected{{end}}>Saturdays</option>
</select>
</span>
<div class="explanation">
How often you'd like to get digests. If there is no activity on that day or week, then no email will be sent.
</div>
</label>
</div>
<div class="setting"> <div class="setting">
<label> <label>
Timezone: Timezone:
@ -96,5 +121,18 @@
<input type="submit" value="Save Settings"> <input type="submit" value="Save Settings">
</form> </form>
<script>
function updateWeeklyDayContainer() {
var frequencyNode = document.getElementById("frequency");
var weeklyDayContainerNode = document.getElementById("weekly-day-container");
if (frequencyNode.value == "weekly") {
weeklyDayContainerNode.style.display = "inline";
} else {
weeklyDayContainerNode.style.display = "none";
}
}
updateWeeklyDayContainer();
</script>
{{end}} {{end}}

View file

@ -16,6 +16,7 @@
</p> </p>
{{range .IntervalDigests }} {{range .IntervalDigests }}
{{$interval := .}}
<h1 style="{{style "interval-header"}}">{{.Header}}</h1> <h1 style="{{style "interval-header"}}">{{.Header}}</h1>
<p>{{.Description}}</p> <p>{{.Description}}</p>
@ -36,7 +37,7 @@
<a href="{{.URL}}" <a href="{{.URL}}"
style="{{style "link" "commit.footer.link"}}">{{.DisplaySHA}}</a> style="{{style "link" "commit.footer.link"}}">{{.DisplaySHA}}</a>
<i title={{.DisplayDateTooltip}} <i title={{.DisplayDateTooltip}}
style="{{style "commit.footer.date"}}">{{.DisplayDate}}</i> style="{{style "commit.footer.date"}}">{{if $interval.Weekly}}{{.WeeklyDisplayDate}}{{else}}{{.DisplayDate}}{{end}}</i>
</div> </div>
</div> </div>
{{end}} {{end}}