mirror of
https://github.com/samsonjs/retrogit.git
synced 2026-04-27 15:07:43 +00:00
Weekly digest option.
This commit is contained in:
parent
71ef392cfa
commit
40540fd91f
5 changed files with 122 additions and 31 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue