Currently we have some license issues. We are working on it.

Commit a8087f93 authored by zauberstuhl's avatar zauberstuhl
Browse files

Add user statistics to personal page

parent 071faf67
......@@ -57,6 +57,15 @@ type Uinfo struct {
Response struct {
User struct {
User_name string
Stats struct {
Total_badges int
Total_friends int
Total_checkins int
Total_beers int
Total_created_beers int
Total_followings int
Total_photos int
}
}
}
}
......@@ -83,7 +92,8 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
return
}
username, err := fetch_username(token)
info, err := fetch_user_info(token)
username := info.Response.User.User_name
if err != nil || username == "" {
log.Print("Cannot fetch user info: ", err)
http.Redirect(w, r, "/", 301)
......@@ -97,9 +107,17 @@ func authHandler(w http.ResponseWriter, r *http.Request) {
return
}
var stats = info.Response.User.Stats
var user = User{
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
TotalBadges: stats.Total_beers,
TotalFriends: stats.Total_friends,
TotalCheckins: stats.Total_checkins,
TotalBeers: stats.Total_beers,
TotalCreatedBeers: stats.Total_created_beers,
TotalFollowings: stats.Total_followings,
TotalPhotos: stats.Total_photos,
Username: username,
Token: token,
}
......@@ -125,11 +143,27 @@ func fetch_auth(code string) (string, error) {
CLIENTID, CLIENTSECRET, REDIRECTURL, code), &uauth)
}
func fetch_username(token string) (string, error) {
var info Uinfo
func fetch_user_info(token string) (info Uinfo, err error) {
url := fmt.Sprintf(
"%s/v4/user/info/?access_token=%s", rootUrl, token)
return info.Response.User.User_name, get(url, &info)
return info, get(url, &info)
}
func fetch_checkins(user *User, id uint, next bool) (resp Uresp, err error) {
url := fmt.Sprintf(
"%s/v4/user/checkins/%s?access_token=%s",
rootUrl, user.Username, user.Token)
if id > 0 {
param := "min_id"
if next {
param = "max_id"
}
url = fmt.Sprintf("%s&%s=%d", url, param, id)
}
log.Print("url ", url)
return resp, get(url, &resp)
}
func get(url string, v interface{}) error {
......
......@@ -4,3 +4,4 @@ clientID: 1234
clientSecret: 123456789
mapboxKey: 123456789
debug: false
worker: true
......@@ -51,8 +51,20 @@
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<span class="navbar-text">
Checkins {{.CurrentUser.TotalCheckins}}&nbsp;
</span>
<span class="navbar-text">
Unique Beers {{.CurrentUser.TotalBeers}}&nbsp;
</span>
<span class="navbar-text">
Badges {{.CurrentUser.TotalBadges}}&nbsp;
</span>
<span class="navbar-text ml-auto">
Database checkins {{lenLoc .CurrentUser.Locations}} updated at {{.CurrentUser.UpdatedAt.Format "2006-01-02"}}
</span>
<ul class="navbar-nav ml-auto">
{{if not .UserOK}}
{{if or (not .CurrentUser.Locations) (not .UserOK)}}
<li class="nav-item">
<a class="nav-link" href="https://untappd.com/oauth/authenticate?client_id=
{{- .Viper.GetString "clientID"}}&response_type=code&redirect_url=
......@@ -78,7 +90,7 @@
</nav>
</header>
{{if .Locations}}
{{if .CurrentUser.Locations}}
<div id="mapid"></div>
<script src="/assets/vendor/js/leaflet.js"></script>
<script src="/assets/vendor/js/control.trackplayback.js"></script>
......@@ -93,7 +105,7 @@
}).addTo(mymap);
{{if .PlaybackOK}}
var trackplayback = new L.TrackPlayBack({{.Locations.LatLngTimeJSON}}, mymap, {
var trackplayback = new L.TrackPlayBack({{.CurrentUser.Locations.LatLngTimeJSON}}, mymap, {
clockOptions: {
// the default speed
// caculate method: fpstime * Math.pow(2, speed - 1)
......@@ -129,7 +141,7 @@
// remove checkbox
$(".trackplayback-checkbox").hide();
{{else}}
{{range .Locations}}
{{range .CurrentUser.Locations}}
var marker = L.marker([{{.Lat}}, {{.Lng}}]).addTo(mymap);
marker.bindPopup(`
<table>
......@@ -154,13 +166,18 @@
<div class="container">
<h1 class="jumbotron-heading">Unmappd - Your beer map</h1>
{{if .UserOK}}
<p class="lead text-muted">No data is available for this user! Please login at least once.</p>
<form method="GET" action="https://untappd.com/oauth/authenticate">
<input type="hidden" name="client_id" value="{{.Viper.GetString "clientID"}}"/>
<input type="hidden" name="response_type" value="code"/>
<input type="hidden" name="redirect_url" value="{{.Viper.GetString "redirectUrl"}}"/>
<button class="btn btn-primary" type="submit">Login &amp; pull</button>
</form>
{{if ne .CurrentUser.Username ""}}
<object data="/assets/images/waiting.svg" type="image/svg+xml"></object>
<p class="lead text-muted">Your checkins being processed..</p>
{{else}}
<p class="lead text-muted">No data is available for this user! Please login at least once.</p>
<form method="GET" action="https://untappd.com/oauth/authenticate">
<input type="hidden" name="client_id" value="{{.Viper.GetString "clientID"}}"/>
<input type="hidden" name="response_type" value="code"/>
<input type="hidden" name="redirect_url" value="{{.Viper.GetString "redirectUrl"}}"/>
<button class="btn btn-primary" type="submit">Login &amp; pull</button>
</form>
{{end}}
{{else}}
<p class="lead text-muted">Login and display all venues you checked in via Untappd</p>
<p class="text-muted">Latest users:</p>
......
......@@ -36,6 +36,14 @@ type User struct {
Username string `gorm:"unique_index"`
Token string
TotalBadges int
TotalFriends int
TotalCheckins int
TotalBeers int
TotalCreatedBeers int
TotalFollowings int
TotalPhotos int
Locations Locations
}
......
<svg class="lds-gear" width="100px" height="100px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" style="background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%;"><link xmlns="" type="text/css" id="dark-mode" rel="stylesheet" href=""/><style xmlns="" type="text/css" id="dark-mode-custom-style"/><g transform="translate(50 50)">
<g>
<animateTransform attributeName="transform" type="rotate" values="0;360" keyTimes="0;1" dur="10s" repeatCount="indefinite"/><path d="M37.43995192304605 -6.5 L47.43995192304605 -6.5 L47.43995192304605 6.5 L37.43995192304605 6.5 A38 38 0 0 1 35.67394948182593 13.090810836924174 L35.67394948182593 13.090810836924174 L44.33420351967032 18.090810836924174 L37.83420351967032 29.34914108612188 L29.17394948182593 24.34914108612188 A38 38 0 0 1 24.34914108612188 29.17394948182593 L24.34914108612188 29.17394948182593 L29.34914108612188 37.83420351967032 L18.090810836924184 44.33420351967032 L13.090810836924183 35.67394948182593 A38 38 0 0 1 6.5 37.43995192304605 L6.5 37.43995192304605 L6.500000000000001 47.43995192304605 L-6.499999999999995 47.43995192304606 L-6.499999999999996 37.43995192304606 A38 38 0 0 1 -13.09081083692417 35.67394948182593 L-13.09081083692417 35.67394948182593 L-18.09081083692417 44.33420351967032 L-29.34914108612187 37.834203519670325 L-24.349141086121872 29.173949481825936 A38 38 0 0 1 -29.17394948182592 24.34914108612189 L-29.17394948182592 24.34914108612189 L-37.83420351967031 29.349141086121893 L-44.33420351967031 18.0908108369242 L-35.67394948182592 13.090810836924193 A38 38 0 0 1 -37.43995192304605 6.5000000000000036 L-37.43995192304605 6.5000000000000036 L-47.43995192304605 6.500000000000004 L-47.43995192304606 -6.499999999999993 L-37.43995192304606 -6.499999999999994 A38 38 0 0 1 -35.67394948182593 -13.090810836924167 L-35.67394948182593 -13.090810836924167 L-44.33420351967032 -18.090810836924163 L-37.834203519670325 -29.34914108612187 L-29.173949481825936 -24.34914108612187 A38 38 0 0 1 -24.349141086121893 -29.17394948182592 L-24.349141086121893 -29.17394948182592 L-29.349141086121897 -37.834203519670304 L-18.0908108369242 -44.334203519670304 L-13.090810836924195 -35.67394948182592 A38 38 0 0 1 -6.500000000000005 -37.43995192304605 L-6.500000000000005 -37.43995192304605 L-6.500000000000007 -47.43995192304605 L6.49999999999999 -47.43995192304606 L6.499999999999992 -37.43995192304606 A38 38 0 0 1 13.090810836924149 -35.67394948182594 L13.090810836924149 -35.67394948182594 L18.090810836924142 -44.33420351967033 L29.349141086121847 -37.83420351967034 L24.349141086121854 -29.17394948182595 A38 38 0 0 1 29.17394948182592 -24.349141086121893 L29.17394948182592 -24.349141086121893 L37.834203519670304 -29.349141086121897 L44.334203519670304 -18.0908108369242 L35.67394948182592 -13.090810836924197 A38 38 0 0 1 37.43995192304605 -6.500000000000007 M0 -20A20 20 0 1 0 0 20 A20 20 0 1 0 0 -20" fill="#ffcc00"/></g></g></svg>
......@@ -45,6 +45,9 @@ var TemplateFuncs = map[string]interface{}{
}
return fmt.Sprintf(nginxRewrite, username)
},
"lenLoc": func(loc Locations) int {
return len(loc)
},
}
func handler(w http.ResponseWriter, r *http.Request) {
......@@ -65,9 +68,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
paramUser, userOK := r.URL.Query()["user"]
if userOK {
err = db.First(&user, "username = ?", paramUser[0]).Error
if err != nil {
userOK = false
} else {
if err == nil {
// first time setup
if len(user.Locations) == 0 {
fetch(&user, false)
......@@ -84,12 +85,12 @@ func handler(w http.ResponseWriter, r *http.Request) {
_, playbackOK := r.URL.Query()["playback"]
if err = tmpl.ExecuteTemplate(w, "index.tmpl", struct{
Viper *viper.Viper
Locations Locations
CurrentUser User
PlaybackOK, UserOK bool
LatestUsers Users
}{
Viper: viper.GetViper(),
Locations: user.Locations,
CurrentUser: user,
PlaybackOK: playbackOK,
UserOK: userOK,
LatestUsers: latestUsers,
......
......@@ -20,18 +20,22 @@ package main
import (
"time"
"fmt"
"log"
"github.com/spf13/viper"
"github.com/robfig/cron"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
func init() {
log.Print("Start cronjobs..")
cronjob := cron.New()
cronjob.AddFunc("@hourly", cronHandler)
cronjob.Start()
if viper.GetBool("worker") {
log.Print("Start cronjobs..")
cronjob := cron.New()
cronjob.AddFunc("@every 1m", cronHandler)
cronjob.Start()
} else {
log.Print("[WARNING] Skipping workers!!")
}
}
func cronHandler() {
......@@ -50,55 +54,68 @@ func cronHandler() {
}
for _, user := range users {
fetch(&user, true)
err = info_worker(&user)
if err == nil {
err = checkin_worker(&user, false)
if err != nil && viper.GetBool("debug") {
log.Printf("checkin_worker failed: %+v\n", err)
}
} else if viper.GetBool("debug") {
log.Printf("info_worker failed: %+v\n", err)
}
}
}
func fetch(user *User, cron bool) {
func info_worker(user *User) error {
db, err := openDatabase()
if err != nil {
log.Print(err)
return
return err
}
defer db.Close()
var maxID uint
if cron {
for _, location := range user.Locations {
if maxID < location.ID {
maxID = location.ID
}
}
info, err := fetch_user_info(user.Token)
if err != nil {
return err
}
for {
url := fmt.Sprintf(
"%s/v4/user/checkins/%s?access_token=%s",
rootUrl, user.Username, user.Token)
if maxID > 0 {
param := "max_id"
if cron {
param = "min_id"
}
url = fmt.Sprintf("%s&%s=%d", url, param, maxID)
}
var stats = info.Response.User.Stats
// db.Save is too recursive so we will use db.Updates
// also we should use a map instead of a struct cause
// of some known problems with default values
//return db.Save(user).Error
return db.Model(&User{}).Where("id = ?", user.ID).
Updates(map[string]interface{}{
"updated_at": time.Now(),
"total_badges": stats.Total_beers,
"total_friends": stats.Total_friends,
"total_checkins": stats.Total_checkins,
"total_beers": stats.Total_beers,
"total_created_beers": stats.Total_created_beers,
"total_followings": stats.Total_followings,
"total_photos": stats.Total_photos,
}).Error
}
log.Print("url ", url)
func checkin_worker(user *User, all bool) error {
db, err := openDatabase()
if err != nil {
return err
}
defer db.Close()
var response Uresp
err := get(url, &response)
var id uint
for {
resp, err := fetch_checkins(user, id, all)
if err != nil {
log.Print("get ", err)
break
return err
}
// avoid a re-run if the last result was empty
if len(response.Response.Checkins.Items) == 0 {
if len(resp.Response.Checkins.Items) == 0 {
break
}
for i, item := range response.Response.Checkins.Items {
for i, item := range resp.Response.Checkins.Items {
switch venue := item.Venue.(type) {
case map[string]interface{}:
geo, ok := venue["location"].(map[string]interface{})
......@@ -159,6 +176,7 @@ func fetch(user *User, cron bool) {
user.Locations = append(user.Locations, location)
}
}
maxID = uint(response.Response.Pagination.Max_id)
id = uint(resp.Response.Pagination.Max_id)
}
return nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment