Merge changes I81b22398,I19a72da6

* changes:
  hswaw/site: add spaceapi
  hswaw/site: add checkinator integration
This commit is contained in:
q3k 2021-07-18 13:47:19 +00:00 committed by Gerrit Code Review
commit 21be807b05
9 changed files with 231 additions and 15 deletions

View file

@ -4,14 +4,17 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
go_library(
name = "go_default_library",
srcs = [
"at.go",
"feeds.go",
"main.go",
"spaceapi.go",
"views.go",
],
importpath = "code.hackerspace.pl/hscloud/hswaw/site",
visibility = ["//visibility:private"],
deps = [
"//go/mirko:go_default_library",
"//hswaw/site/calendar:go_default_library",
"//hswaw/site/static:static_go",
"//hswaw/site/templates:templates_go",
"@com_github_golang_glog//:go_default_library",
@ -25,8 +28,8 @@ go_binary(
)
container_image(
name="latest",
base="@prodimage-bionic//image",
name = "latest",
base = "@prodimage-bionic//image",
files = [":site"],
directory = "/hswaw/site/",
entrypoint = ["/hswaw/site/site"],

48
hswaw/site/at.go Normal file
View file

@ -0,0 +1,48 @@
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
)
const (
atURL = "https://at.hackerspace.pl/api"
)
// atStatus is the result of queruing checkinator/at (Hackerspace presence
// service).
type atStatus struct {
// Users is the list of present and publicly visible users.
Users []atUser `json:"users"`
// ESPs is the number of ESP{8266,32} devices.
ESPs int `json:"esps"`
// Kektops is the number of nettop “Kektop” devices.
Kektops int `json:"kektops"`
// Unknown is the number of unknown devices in the network.
Unknown int `json:"unknown"`
}
type atUser struct {
Login string `json:"login"`
}
func getAt(ctx context.Context) (*atStatus, error) {
r, err := http.NewRequestWithContext(ctx, "GET", atURL, nil)
if err != nil {
return nil, fmt.Errorf("NewRequest(%q): %w", atURL, err)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
return nil, fmt.Errorf("GET: %w", err)
}
defer res.Body.Close()
var status atStatus
if err := json.NewDecoder(res.Body).Decode(&status); err != nil {
return nil, fmt.Errorf("when decoding JSON: %w", err)
}
return &status, nil
}

View file

@ -3,12 +3,12 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"load.go",
"event.go",
"load.go",
"time.go",
],
importpath = "code.hackerspace.pl/hscloud/hswaw/site/calendar",
visibility = ["//visibility:private"],
visibility = ["//hswaw/site:__subpackages__"],
deps = [
"@com_github_arran4_golang_ical//:go_default_library",
"@com_github_golang_glog//:go_default_library",

View file

@ -14,8 +14,8 @@ import (
)
const (
// eventsURL is the calendar from which we load public Hackerspace events.
eventsURL = "https://owncloud.hackerspace.pl/remote.php/dav/public-calendars/g8toktZrA9fyAHNi/?export"
// EventsURL is the calendar from which we load public Hackerspace events.
EventsURL = "https://owncloud.hackerspace.pl/remote.php/dav/public-calendars/g8toktZrA9fyAHNi/?export"
)
// eventsBySooner sorts upcoming events so the one that happens the soonest
@ -107,13 +107,13 @@ func parseUpcomingEvents(now time.Time, data io.Reader) ([]*UpcomingEvent, error
// relative to the given time 'now' as per the Warsaw Hackerspace public
// calender (from owncloud.hackerspace.pl).
func GetUpcomingEvents(ctx context.Context, now time.Time) ([]*UpcomingEvent, error) {
r, err := http.NewRequestWithContext(ctx, "GET", eventsURL, nil)
r, err := http.NewRequestWithContext(ctx, "GET", EventsURL, nil)
if err != nil {
return nil, fmt.Errorf("NewRequest(%q): %w", eventsURL, err)
return nil, fmt.Errorf("NewRequest(%q): %w", EventsURL, err)
}
res, err := http.DefaultClient.Do(r)
if err != nil {
return nil, fmt.Errorf("Do(%q): %w", eventsURL, err)
return nil, fmt.Errorf("Do(%q): %w", EventsURL, err)
}
defer res.Body.Close()
return parseUpcomingEvents(now, res.Body)

View file

@ -102,5 +102,6 @@ func (s *service) handleHTTPStatic(w http.ResponseWriter, r *http.Request) {
func (s *service) registerHTTP(mux *http.ServeMux) {
mux.HandleFunc("/static/", s.handleHTTPStatic)
mux.HandleFunc("/spaceapi", s.handleSpaceAPI)
mux.HandleFunc("/", s.handleIndex)
}

119
hswaw/site/spaceapi.go Normal file
View file

@ -0,0 +1,119 @@
package main
import (
"context"
"code.hackerspace.pl/hscloud/hswaw/site/calendar"
"github.com/golang/glog"
)
// SpaceAPIResponse, per https://spaceapi.io/ - kinda. Mostly rewritten from
// old implementation, someone should update this to use the official schema.
type SpaceAPIResponse struct {
API string `json:"api"`
Space string `json:"space"`
Logo string `json:"logo"`
URL string `json:"url"`
Location SpaceAPILocation `json:"location"`
State SpaceAPIState `json:"state"`
Contact map[string]string `json:"contact"`
IssueReportChannels []string `json:"issue_report_channels"`
Projects []string `json:"projects"`
Feeds map[string]SpaceAPIFeed `json:"feeds"`
Sensors map[string][]SpaceAPISensor `json:"sensors"`
}
type SpaceAPILocation struct {
Latitude float64 `json:"lat"`
Longitude float64 `json:"lon"`
Address string `json:"address"`
}
type SpaceAPIState struct {
Open bool `json:"open"`
Message string `json:"message"`
Icon struct {
Open string `json:"open"`
Closed string `json:"closed"`
} `json:"icon"`
}
type SpaceAPIFeed struct {
Type string `json:"type"`
URL string `json:"url"`
}
type SpaceAPISensor struct {
Value int `json:"value"`
Names []string `json:"names"`
}
func generateSpaceAPIResponse(ctx context.Context) SpaceAPIResponse {
state := SpaceAPIState{}
state.Icon.Open = "https://static.hackerspace.pl/img/status-open-small.png"
state.Icon.Closed = "https://static.hackerspace.pl/img/status-closed-small.png"
// TODO(q3k): post-coronavirus, make automatically open based on calendar
// events and Open Thursdays.
open := false
if open {
state.Open = true
state.Message = "open for public"
} else {
state.Open = false
state.Message = "members only"
}
peopleNowPresent := SpaceAPISensor{}
atState, err := getAt(ctx)
if err != nil {
glog.Errorf("Failed to get checkinator status: %v", err)
} else {
peopleNowPresent.Names = make([]string, len(atState.Users))
for i, u := range atState.Users {
peopleNowPresent.Names[i] = u.Login
}
peopleNowPresent.Value = len(peopleNowPresent.Names)
}
res := SpaceAPIResponse{
API: "0.13",
Space: "Warsaw Hackerspace",
Logo: "https://static.hackerspace.pl/img/syrenka-black.png",
URL: "https://hackerspace.pl",
Location: SpaceAPILocation{
Latitude: 52.24160,
Longitude: 20.98485,
Address: "ul. Wolność 2A, 01-018 Warszawa, Poland",
},
State: state,
Contact: map[string]string{
"irc": "irc://irc.libera.chat/#hswaw",
"twitter": "@hackerspacepl",
"facebook": "hackerspacepl",
"ml": "waw@lists.hackerspace.pl",
},
IssueReportChannels: []string{"irc"},
Projects: []string{
"https://wiki.hackerspace.pl/projects",
},
Feeds: map[string]SpaceAPIFeed{
"blog": SpaceAPIFeed{
Type: "atom",
URL: feedsURLs["blog"],
},
"calendar": SpaceAPIFeed{
Type: "ical",
URL: calendar.EventsURL,
},
"wiki": SpaceAPIFeed{
Type: "rss",
URL: "https://wiki.hackerspace.pl/feed.php",
},
},
Sensors: map[string][]SpaceAPISensor{
"people_now_present": []SpaceAPISensor{peopleNowPresent},
},
}
return res
}

View file

@ -141,9 +141,19 @@ li i {
opacity: 60%;
}
#quicklinks {
float: right;
font-family: monospace;
font-size: 14px;
margin: 2rem;
.atlist {
display: inline;
list-style: none;
}
.atlist li {
display: inline;
}
.atlist li:after {
content: ", ";
}
.atlist li:last-child:after {
content: ".";
}

View file

@ -36,6 +36,28 @@ ul. Wolność 2A
<p>
<b>Hackerspace nie zna barier.</b> Jeśli masz ciekawy pomysł i szukasz ludzi chętnych do współpracy lub po prostu potrzebujesz miejsca i sprzętu - <a href="">zapraszamy</a>!
</p>
<h3>Kto jest teraz w spejsie?</h3>
<p>
{{ if ne .AtError nil }}
<i>Ups, nie udało się załadować stanu checkinatora.</i>
{{ else }}
{{ $count := len .AtStatus.Users }}
{{ if gt $count 4 }}
Według <a href="https://at.hackerspace.pl">naszych instrumentów</a> w spejsie obecnie znajduje się {{ $count }} osób:
{{ else if gt $count 1 }}
Według <a href="https://at.hackerspace.pl">naszych instrumentów</a> w spejsie obecnie znajdują się {{ $count }} osoby:
{{ else if gt $count 0 }}
Według <a href="https://at.hackerspace.pl">naszych instrumentów</a> w spejsie obecnie znajduje się jedna osoba:
{{ else }}
Według <a href="https://at.hackerspace.pl">naszych instrumentów</a> w spejsie obecnie nie ma nikogo.
{{ end }}
<ul class="atlist">
{{ range .AtStatus.Users }}
<li>{{ .Login }}</li>
{{ end }}
</ul>
{{ end }}
</p>
</div>
<div class="blog">
<h2>Blog</h2>

View file

@ -1,6 +1,7 @@
package main
import (
"encoding/json"
"fmt"
"html/template"
"net/http"
@ -53,7 +54,19 @@ func render(w http.ResponseWriter, t *template.Template, data interface{}) {
// handleIndex handles rendering the main page at /.
func (s *service) handleIndex(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
atStatus, atError := getAt(ctx)
render(w, tmplIndex, map[string]interface{}{
"Entries": s.getFeeds(),
"AtStatus": atStatus,
"AtError": atError,
})
}
func (s *service) handleSpaceAPI(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(generateSpaceAPIResponse(ctx))
}