Skip to content

Commit

Permalink
feat: custom time intervals (resolve #115)
Browse files Browse the repository at this point in the history
  • Loading branch information
muety committed Feb 13, 2021
1 parent daf67b8 commit 3051059
Show file tree
Hide file tree
Showing 14 changed files with 118 additions and 54 deletions.
2 changes: 1 addition & 1 deletion config.default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ db:

security:
password_salt: # CHANGE !
insecure_cookies: false
insecure_cookies: false # You need to set this to 'true' when on localhost
cookie_max_age: 172800
allow_signup: true
expose_metrics: false
1 change: 1 addition & 0 deletions models/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type SummaryItemContainer struct {

type SummaryViewModel struct {
*Summary
User *User
LanguageColors map[string]string
EditorColors map[string]string
OSColors map[string]string
Expand Down
1 change: 1 addition & 0 deletions models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type User struct {
ShareOSs bool `json:"-" gorm:"default:false; type:bool; column:share_oss"`
ShareMachines bool `json:"-" gorm:"default:false; type:bool"`
IsAdmin bool `json:"-" gorm:"default:false; type:bool"`
HasData bool `json:"-" gorm:"default:false; type:bool"`
WakatimeApiKey string `json:"-"`
}

Expand Down
1 change: 1 addition & 0 deletions repositories/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ func (r *UserRepository) Update(user *models.User) (*models.User, error) {
"share_projects": user.ShareProjects,
"share_machines": user.ShareMachines,
"wakatime_api_key": user.WakatimeApiKey,
"has_data": user.HasData,
}

result := r.db.Model(user).Updates(updateMap)
Expand Down
15 changes: 13 additions & 2 deletions routes/api/heartbeat.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {

if !hb.Valid() {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Invalid heartbeat object."))
w.Write([]byte("invalid heartbeat object"))
return
}

Expand All @@ -82,10 +82,21 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {

if err := h.heartbeatSrvc.InsertBatch(heartbeats); err != nil {
w.WriteHeader(http.StatusInternalServerError)
logbuch.Error(err.Error())
w.Write([]byte(conf.ErrInternalServerError))
logbuch.Error("failed to batch-insert heartbeats – %v", err)
return
}

if !user.HasData {
user.HasData = true
if _, err := h.userSrvc.Update(user); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(conf.ErrInternalServerError))
logbuch.Error("failed to update user – %v", err)
return
}
}

utils.RespondJSON(w, http.StatusCreated, constructSuccessResponse(len(heartbeats)))
}

Expand Down
2 changes: 1 addition & 1 deletion routes/api/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ func (h *MetricsHandler) getAdminMetrics(user *models.User) (*mm.Metrics, error)

activeUsers, err := h.userSrvc.GetActive()
if err != nil {
logbuch.Error("failed to retrieve active users for metric", err)
logbuch.Error("failed to retrieve active users for metric – %v", err)
return nil, err
}

Expand Down
20 changes: 11 additions & 9 deletions routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ var templates map[string]*template.Template
func loadTemplates() {
const tplPath = "/views"
tpls := template.New("").Funcs(template.FuncMap{
"json": utils.Json,
"date": utils.FormatDateHuman,
"title": strings.Title,
"join": strings.Join,
"add": utils.Add,
"capitalize": utils.Capitalize,
"toRunes": utils.ToRunes,
"entityTypes": models.SummaryTypes,
"typeName": typeName,
"json": utils.Json,
"date": utils.FormatDateHuman,
"simpledate": utils.FormatDate,
"simpledatetime": utils.FormatDateTime,
"title": strings.Title,
"join": strings.Join,
"add": utils.Add,
"capitalize": utils.Capitalize,
"toRunes": utils.ToRunes,
"entityTypes": models.SummaryTypes,
"typeName": typeName,
"getBasePath": func() string {
return config.Get().Server.BasePath
},
Expand Down
1 change: 1 addition & 0 deletions routes/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {

vm := models.SummaryViewModel{
Summary: summary,
User: user,
LanguageColors: utils.FilterColors(h.config.App.GetLanguageColors(), summary.Languages),
EditorColors: utils.FilterColors(h.config.App.GetEditorColors(), summary.Editors),
OSColors: utils.FilterColors(h.config.App.GetOSColors(), summary.OperatingSystems),
Expand Down
5 changes: 5 additions & 0 deletions static/assets/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ body {

.bg-gray-850 {
background-color: #242b3a;
}

::-webkit-calendar-picker-indicator {
filter: invert(1);
cursor: pointer;
}
19 changes: 17 additions & 2 deletions static/assets/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ String.prototype.toHHMMSS = function () {
return hours + ':' + minutes + ':' + seconds
}

String.prototype.toHHMM = function () {
var sec_num = parseInt(this, 10)
var hours = Math.floor(sec_num / 3600)
var minutes = Math.floor((sec_num - (hours * 3600)) / 60)

if (hours < 10 && hours > 0) {
hours = '0' + hours
}
if (minutes < 10 && minutes > 0) {
minutes = '0' + minutes
}
return hours + ':' + minutes
}

function draw(subselection) {
function getTooltipOptions(key) {
return {
Expand Down Expand Up @@ -319,8 +333,9 @@ function equalizeHeights() {
function getTotal(items) {
const el = document.getElementById('total-span')
if (!el) return
let total = items.reduce((acc, d) => acc + d.total, 0)
el.innerText = total.toString().toHHMMSS()
const total = items.reduce((acc, d) => acc + d.total, 0)
const formatted = total.toString().toHHMM()
el.innerText = `${formatted.split(':')[0]} hours, ${formatted.split(':')[1]} minutes`
}

function getRandomColor(seed) {
Expand Down
8 changes: 8 additions & 0 deletions utils/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ import (
)

func ParseDate(date string) (time.Time, error) {
return time.Parse(config.SimpleDateFormat, date)
}

func ParseDateTime(date string) (time.Time, error) {
return time.Parse(config.SimpleDateTimeFormat, date)
}

func FormatDate(date time.Time) string {
return date.Format(config.SimpleDateFormat)
}

func FormatDateTime(date time.Time) string {
return date.Format(config.SimpleDateTimeFormat)
}

Expand Down
14 changes: 10 additions & 4 deletions utils/summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,20 @@ func ParseSummaryParams(r *http.Request) (*models.SummaryParams, error) {
} else if start := params.Get("start"); start != "" {
err, from, to = ResolveIntervalRaw(start)
} else {
from, err = ParseDate(params.Get("from"))
from, err = ParseDateTime(params.Get("from"))
if err != nil {
return nil, errors.New("missing 'from' parameter")
from, err = ParseDate(params.Get("from"))
if err != nil {
return nil, errors.New("missing 'from' parameter")
}
}

to, err = ParseDate(params.Get("to"))
to, err = ParseDateTime(params.Get("to"))
if err != nil {
return nil, errors.New("missing 'to' parameter")
to, err = ParseDate(params.Get("to"))
if err != nil {
return nil, errors.New("missing 'to' parameter")
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions views/settings.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
{{ template "header.tpl.html" . }}

<div class="w-full flex justify-center">
<div class="flex items-center justify-between max-w-xl flex-grow">
<div class="flex items-center justify-between max-w-2xl flex-grow">
<div><a href="" class="text-gray-500 text-sm">&larr; Go back</a></div>
<div><h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Settings</h1></div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
Expand All @@ -32,7 +32,7 @@
{{ template "alerts.tpl.html" . }}

<main class="mt-4 flex-grow flex justify-center w-full">
<div class="flex flex-col flex-grow max-w-xl mt-8">
<div class="flex flex-col flex-grow max-w-2xl mt-8">

<details class="my-8 pb-8 border-b border-gray-700">
<summary class="cursor-pointer">
Expand Down
79 changes: 46 additions & 33 deletions views/summary.tpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,43 +36,56 @@
</div>

<div class="flex items-center justify-center">
<h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Your Coding Statistics 🤓</h1>
<h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Summary</h1>
</div>

<div class="text-white text-sm flex items-center justify-center mt-4 self-center max-w-lg flex-wrap">
<a href="summary?interval=today" class="mx-2 my-1 border-b border-green-700">Today</a>
<a href="summary?interval=yesterday" class="mx-2 my-1 border-b border-green-700">Yesterday</a>
<a href="summary?interval=week" class="mx-2 my-1 border-b border-green-700">This Week</a>
<a href="summary?interval=month" class="mx-2 my-1 border-b border-green-700">This Month</a>
<a href="summary?interval=year" class="mx-2 my-1 border-b border-green-700">This Year</a>
<a href="summary?interval=last_7_days" class="mx-2 my-1 border-b border-green-700">Past 7 Days</a>
<a href="summary?interval=last_30_days" class="mx-2 my-1 border-b border-green-700">Past 30 Days</a>
<a href="summary?interval=last_12_months" class="mx-2 my-1 border-b border-green-700">Past 12 Months</a>
<a href="summary?interval=any" class="mx-2 my-1 border-b border-green-700">All Time</a>
{{ if .User.HasData }}

<div class="self-center border border-gray-700 shadow mt-8 rounded-md p-4 bg-gray-900">
<form class="text-white flex flex-nowrap items-center justify-center self-center max-w-xl flex-wrap space-x-8">
<div class="flex space-x-1">
<label for="from-date-picker" class="text-gray-300 pl-1">▶️ Start:</label>
<input id="from-date-picker" type="date" name="from" class="text-sm text-gray-300 bg-gray-800 rounded-md text-center cursor-pointer"
value="{{ .FromTime.T | simpledate }}" required>
</div>
<div class="flex space-x-1">
<label for="to-date-picker" class="text-gray-300 pl-1">⏹️ End:</label>
<input id="to-date-picker" type="date" name="to" class="text-sm text-gray-300 bg-gray-800 rounded-md text-center cursor-pointer"
value="{{ .ToTime.T | simpledate }}" required>
</div>
<div>
<button type="submit" class="py-1 px-3 rounded bg-green-700 hover:bg-green-800 text-white text-sm">Show</button>
</div>
</form>

<div class="text-gray-300 text-sm flex items-center justify-center mt-4 self-center max-w-lg flex-wrap">
<a href="summary?interval=today" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">Today</a>
<a href="summary?interval=yesterday" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">Yesterday</a>
<a href="summary?interval=week" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">This Week</a>
<a href="summary?interval=month" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">This Month</a>
<a href="summary?interval=year" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">This Year</a>
<a href="summary?interval=last_7_days" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">Past 7 Days</a>
<a href="summary?interval=last_30_days" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">Past 30 Days</a>
<a href="summary?interval=last_12_months" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">Past 12 Months</a>
<a href="summary?interval=any" class="px-1 my-1 mx-1 border-b hover:border-b-2 border-gray-700 hover:bg-green-700 rounded hover:border-none">All Time</a>
</div>
</div>

{{ end }}

{{ template "alerts.tpl.html" . }}

<main class="flex flex-col items-center mt-10 flex-grow">

<div class="flex justify-center">
<div class="p-1">
<div class="flex justify-center p-4 bg-gray-900 border border-gray-700 text-gray-300 rounded-md shadow">
<p class="mx-2"><strong>▶️</strong> <span title="Start Time">{{ .FromTime.T | date }}</span></p>
<p class="mx-2"><strong>⏹️</strong> <span title="End Time">{{ .ToTime.T | date }}</span></p>
<p class="mx-2">
<strong>⏱️</strong>
{{ if gt .Summary.TotalTime 0 }}
<span id="total-span" title="Total Hours"></span>
{{ else }}
<span title="Total Hours">No Data</span>
{{ end }}
</p>
</div>
</div>
</div>
{{ if .User.HasData }}

{{ if or (gt .Summary.TotalTime 0) (ne .RawQuery "") }}
<span class="text-white text-lg text-gray-300 text-center mb-4">
<span class="text-xl">⏱️&nbsp;</span>
Showing a total of <span id="total-span" title="Total Hours" class="text-white text-xl font-semibold border-b-2 border-green-700"></span>
<span class="text-sm my-2">
(from <span title="Start Time" class="border-b border-gray-700">{{ .FromTime.T | date }}</span> to <span title="End Time" class="border-b border-gray-700">{{ .ToTime.T | date }}</span>)
</span>
</span>

<div class="flex flex-wrap justify-center">
<div class="w-full lg:w-1/2 p-1">
Expand All @@ -87,7 +100,7 @@ <h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Yo
</div>
<canvas id="chart-projects" class="mt-2"></canvas>
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
<span class="text-md font-semibold text-gray-500 mt-4">No data ...</span>
<span class="text-md font-semibold text-gray-500 mt-4">No data</span>
</div>
</div>
</div>
Expand All @@ -103,7 +116,7 @@ <h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Yo
</div>
<canvas id="chart-os"></canvas>
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
<span class="text-md font-semibold text-gray-500 mt-4">No data ...</span>
<span class="text-md font-semibold text-gray-500 mt-4">No data</span>
</div>
</div>
</div>
Expand All @@ -119,7 +132,7 @@ <h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Yo
</div>
<canvas id="chart-language"></canvas>
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
<span class="text-md font-semibold text-gray-500 mt-4">No data ...</span>
<span class="text-md font-semibold text-gray-500 mt-4">No data</span>
</div>
</div>
</div>
Expand All @@ -135,7 +148,7 @@ <h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Yo
</div>
<canvas id="chart-editor"></canvas>
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
<span class="text-md font-semibold text-gray-500 mt-4">No data ...</span>
<span class="text-md font-semibold text-gray-500 mt-4">No data</span>
</div>
</div>
</div>
Expand All @@ -151,7 +164,7 @@ <h1 class="font-semibold text-2xl text-white m-0 border-b-4 border-green-700">Yo
</div>
<canvas id="chart-machine"></canvas>
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
<span class="text-md font-semibold text-gray-500 mt-4">No data ...</span>
<span class="text-md font-semibold text-gray-500 mt-4">No data</span>
</div>
</div>
</div>
Expand Down

0 comments on commit 3051059

Please sign in to comment.