<commit_before>package hdf5

// #include "hdf5.h"
// #include "hdf5_hl.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"
	"reflect"
	"runtime"
	"unsafe"
)

type Table struct {
	Location
}

func newPacketTable(id C.hid_t) *Table {
	t := &Table{Location{Identifier{id}}}
	runtime.SetFinalizer(t, (*Table).finalizer)
	return t
}

func (t *Table) finalizer() {
	err := t.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing packet table: %s", err))
	}
}

// Close closes an open packet table.
func (t *Table) Close() error {
	if t.id > 0 {
		err := h5err(C.H5PTclose(t.id))
		if err != nil {
			t.id = 0
		}
		return err
	}
	return nil
}

// IsValid returns whether or not an indentifier points to a packet table.
func (t *Table) IsValid() bool {
	o := int(C.H5PTis_valid(t.id))
	if o < 0 {
		return false
	}
	return true
}

func (t *Table) Id() int {
	return int(t.id)
}

// ReadPackets reads a number of packets from a packet table.
func (t *Table) ReadPackets(start, nrecords int, data interface{}) error {
	c_start := C.hsize_t(start)
	c_nrecords := C.size_t(nrecords)
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_data := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Len()))
		}
		c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr())

	case reflect.Slice:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Len()))
		}
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	default:
		panic(fmt.Sprintf("unhandled kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data)
	return h5err(err)
}

// Append appends packets to the end of a packet table.
func (t *Table) Append(data interface{}) error {
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_nrecords := C.size_t(0)
	c_data := unsafe.Pointer(nil)

	switch rt.Kind() {

	case reflect.Array:
		c_nrecords = C.size_t(rv.Len())
		c_data = unsafe.Pointer(rv.UnsafeAddr())

	case reflect.Slice:
		c_nrecords = C.size_t(rv.Len())
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	case reflect.String:
		c_nrecords = C.size_t(rv.Len())
		str := (*reflect.StringHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.Elem().UnsafeAddr())

	default:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.UnsafeAddr())
	}

	err := C.H5PTappend(t.id, c_nrecords, c_data)
	return h5err(err)
}

// Next reads packets from a packet table starting at the current index.
func (t *Table) Next(data interface{}) error {
	rt := reflect.TypeOf(data)
	rv := reflect.ValueOf(data)
	n := C.size_t(0)
	cdata := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	case reflect.Slice:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	default:
		panic(fmt.Sprintf("unsupported kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTget_next(t.id, n, cdata)
	return h5err(err)
}

// NumPackets returns the number of packets in a packet table.
func (t *Table) NumPackets() (int, error) {
	c_nrecords := C.hsize_t(0)
	err := C.H5PTget_num_packets(t.id, &c_nrecords)
	return int(c_nrecords), h5err(err)
}

// CreateIndex resets a packet table's index to the first packet.
func (t *Table) CreateIndex() error {
	err := C.H5PTcreate_index(t.id)
	return h5err(err)
}

// SetIndex sets a packet table's index.
func (t *Table) SetIndex(index int) error {
	c_idx := C.hsize_t(index)
	err := C.H5PTset_index(t.id, c_idx)
	return h5err(err)
}

// Type returns an identifier for a copy of the datatype for a dataset.
func (t *Table) Type() (*Datatype, error) {
	hid := C.H5Dget_type(t.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	return copyDatatype(hid)
}

func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	chunk := C.hsize_t(chunkSize)
	compr := C.int(compression)
	hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}

func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) {
	var err error
	switch dt := dtype.(type) {
	case reflect.Type:
		if hdfDtype, err := NewDataTypeFromType(dt); err == nil {
			return createTable(id, name, hdfDtype, chunkSize, compression)
		}
	case *Datatype:
		return createTable(id, name, dt, chunkSize, compression)
	default:
		if hdfDtype, err := NewDataTypeFromType(reflect.TypeOf(dtype)); err == nil {
			return createTable(id, name, hdfDtype, chunkSize, compression)
		}
	}
	return nil, err
}

func openTable(id C.hid_t, name string) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	hid := C.H5PTopen(id, c_name)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}
<commit_msg>h5pt: doc<commit_after>package hdf5

// #include "hdf5.h"
// #include "hdf5_hl.h"
// #include <stdlib.h>
// #include <string.h>
import "C"

import (
	"fmt"
	"reflect"
	"runtime"
	"unsafe"
)

// Table is an hdf5 packet-table.
type Table struct {
	Location
}

func newPacketTable(id C.hid_t) *Table {
	t := &Table{Location{Identifier{id}}}
	runtime.SetFinalizer(t, (*Table).finalizer)
	return t
}

func (t *Table) finalizer() {
	err := t.Close()
	if err != nil {
		panic(fmt.Sprintf("error closing packet table: %s", err))
	}
}

// Close closes an open packet table.
func (t *Table) Close() error {
	if t.id > 0 {
		err := h5err(C.H5PTclose(t.id))
		if err != nil {
			t.id = 0
		}
		return err
	}
	return nil
}

// IsValid returns whether or not an indentifier points to a packet table.
func (t *Table) IsValid() bool {
	o := int(C.H5PTis_valid(t.id))
	if o < 0 {
		return false
	}
	return true
}

func (t *Table) Id() int {
	return int(t.id)
}

// ReadPackets reads a number of packets from a packet table.
func (t *Table) ReadPackets(start, nrecords int, data interface{}) error {
	c_start := C.hsize_t(start)
	c_nrecords := C.size_t(nrecords)
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_data := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Len()))
		}
		c_data = unsafe.Pointer(rv.Index(0).UnsafeAddr())

	case reflect.Slice:
		if rv.Len() < nrecords {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Len()))
		}
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	default:
		panic(fmt.Sprintf("unhandled kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTread_packets(t.id, c_start, c_nrecords, c_data)
	return h5err(err)
}

// Append appends packets to the end of a packet table.
func (t *Table) Append(data interface{}) error {
	rv := reflect.Indirect(reflect.ValueOf(data))
	rt := rv.Type()
	c_nrecords := C.size_t(0)
	c_data := unsafe.Pointer(nil)

	switch rt.Kind() {

	case reflect.Array:
		c_nrecords = C.size_t(rv.Len())
		c_data = unsafe.Pointer(rv.UnsafeAddr())

	case reflect.Slice:
		c_nrecords = C.size_t(rv.Len())
		slice := (*reflect.SliceHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(slice.Data)

	case reflect.String:
		c_nrecords = C.size_t(rv.Len())
		str := (*reflect.StringHeader)(unsafe.Pointer(rv.UnsafeAddr()))
		c_data = unsafe.Pointer(str.Data)

	case reflect.Ptr:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.Elem().UnsafeAddr())

	default:
		c_nrecords = C.size_t(1)
		c_data = unsafe.Pointer(rv.UnsafeAddr())
	}

	err := C.H5PTappend(t.id, c_nrecords, c_data)
	return h5err(err)
}

// Next reads packets from a packet table starting at the current index.
func (t *Table) Next(data interface{}) error {
	rt := reflect.TypeOf(data)
	rv := reflect.ValueOf(data)
	n := C.size_t(0)
	cdata := unsafe.Pointer(nil)
	switch rt.Kind() {
	case reflect.Array:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in array (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	case reflect.Slice:
		if rv.Cap() <= 0 {
			panic(fmt.Sprintf("not enough capacity in slice (cap=%d)", rv.Cap()))
		}
		cdata = unsafe.Pointer(rv.Index(0).UnsafeAddr())
		n = C.size_t(rv.Cap())
	default:
		panic(fmt.Sprintf("unsupported kind (%s), need slice or array", rt.Kind()))
	}
	err := C.H5PTget_next(t.id, n, cdata)
	return h5err(err)
}

// NumPackets returns the number of packets in a packet table.
func (t *Table) NumPackets() (int, error) {
	c_nrecords := C.hsize_t(0)
	err := C.H5PTget_num_packets(t.id, &c_nrecords)
	return int(c_nrecords), h5err(err)
}

// CreateIndex resets a packet table's index to the first packet.
func (t *Table) CreateIndex() error {
	err := C.H5PTcreate_index(t.id)
	return h5err(err)
}

// SetIndex sets a packet table's index.
func (t *Table) SetIndex(index int) error {
	c_idx := C.hsize_t(index)
	err := C.H5PTset_index(t.id, c_idx)
	return h5err(err)
}

// Type returns an identifier for a copy of the datatype for a dataset.
func (t *Table) Type() (*Datatype, error) {
	hid := C.H5Dget_type(t.id)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	return copyDatatype(hid)
}

func createTable(id C.hid_t, name string, dtype *Datatype, chunkSize, compression int) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	chunk := C.hsize_t(chunkSize)
	compr := C.int(compression)
	hid := C.H5PTcreate_fl(id, c_name, dtype.id, chunk, compr)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}

func createTableFrom(id C.hid_t, name string, dtype interface{}, chunkSize, compression int) (*Table, error) {
	var err error
	switch dt := dtype.(type) {
	case reflect.Type:
		if hdfDtype, err := NewDataTypeFromType(dt); err == nil {
			return createTable(id, name, hdfDtype, chunkSize, compression)
		}
	case *Datatype:
		return createTable(id, name, dt, chunkSize, compression)
	default:
		if hdfDtype, err := NewDataTypeFromType(reflect.TypeOf(dtype)); err == nil {
			return createTable(id, name, hdfDtype, chunkSize, compression)
		}
	}
	return nil, err
}

func openTable(id C.hid_t, name string) (*Table, error) {
	c_name := C.CString(name)
	defer C.free(unsafe.Pointer(c_name))

	hid := C.H5PTopen(id, c_name)
	err := h5err(C.herr_t(int(hid)))
	if err != nil {
		return nil, err
	}
	table := newPacketTable(hid)
	return table, err
}
<|endoftext|>


<commit_before>package ppc

import (
	"database/sql"

	"github.com/bentranter/password"
	// "github.com/lib/pq"
)

var defaultStore = newDB()

// PqStore is a reference to the password store.
type PqStore struct {
	DB *sql.DB
}

// Store stores the given id and secret in Postgres. It will hash the secret
// using bcrypt before storing it.
func (p *PqStore) Store(id string, secret string) (string, error) {
	hashedSecret, err := password.Hash(secret)
	if err != nil {
		return "", err
	}

	// @TODO
	p.DB.Query("", hashedSecret)
	return "", nil
}

// Retrieve retrieves from Postgres the hashed secret given an id and secret.
func (p *PqStore) Retrieve(id string, secret string) (string, error) {
	p.DB.Query("")

	return "", nil
}

// newDB returns a default DB
func newDB() *PqStore {
	db, err := sql.Open("postgres", "user=ppc dbname=ppc sslmode=verify-full")
	if err != nil {
		panic(err)
	}

	return &PqStore{
		DB: db,
	}
}
<commit_msg>Import driver, use better naming<commit_after>package ppc

import (
	"database/sql"

	"github.com/bentranter/password"
	// Use the Postgres DB driver
	_ "github.com/lib/pq"
)

var defaultStore = newDB()

// PgStore is a reference to the password store.
type PgStore struct {
	DB *sql.DB
}

// Store stores the given id and secret in Postgres. It will hash the secret
// using bcrypt before storing it.
func (p *PgStore) Store(id string, secret string) (string, error) {
	hashedSecret, err := password.Hash(secret)
	if err != nil {
		return "", err
	}

	// @TODO
	p.DB.Query("", hashedSecret)
	return "", nil
}

// Retrieve retrieves from Postgres the hashed secret given an id and secret.
func (p *PgStore) Retrieve(id string, secret string) (string, error) {
	p.DB.Query("")

	return "", nil
}

// newDB returns a default DB
func newDB() *PgStore {
	db, err := sql.Open("postgres", "user=ppc dbname=ppc sslmode=verify-full")
	if err != nil {
		panic(err)
	}

	return &PgStore{
		DB: db,
	}
}
<|endoftext|>


<commit_before>/**
 *  Copyright 2015 Paul Querna
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package cacheobject

import (
	"fmt"
	"net/http"
	"time"
)

// Repersents an HTTP Warning: http://tools.ietf.org/html/rfc7234#section-5.5
type Warning int

const (
	// Response is Stale
	// A cache SHOULD generate this whenever the sent response is stale.
	WarningResponseIsStale = 110

	// Revalidation Failed
	// A cache SHOULD generate this when sending a stale
	// response because an attempt to validate the response failed, due to an
	// inability to reach the server.
	WarningRevalidationFailed = 111

	// Disconnected Operation
	// A cache SHOULD generate this if it is intentionally disconnected from
	// the rest of the network for a period of time.
	WarningDisconnectedOperation = 112

	// Heuristic Expiration
	//
	// A cache SHOULD generate this if it heuristically chose a freshness
	// lifetime greater than 24 hours and the response's age is greater than
	// 24 hours.
	WarningHeuristicExpiration = 113

	// Miscellaneous Warning
	//
	// The warning text can include arbitrary information to be presented to
	// a human user or logged.  A system receiving this warning MUST NOT
	// take any automated action, besides presenting the warning to the
	// user.
	WarningMiscellaneousWarning = 199

	// Transformation Applied
	//
	// This Warning code MUST be added by a proxy if it applies any
	// transformation to the representation, such as changing the
	// content-coding, media-type, or modifying the representation data,
	// unless this Warning code already appears in the response.
	WarningTransformationApplied = 214

	// Miscellaneous Persistent Warning
	//
	// The warning text can include arbitrary information to be presented to
	// a human user or logged.  A system receiving this warning MUST NOT
	// take any automated action.
	WarningMiscellaneousPersistentWarning = 299
)

func (w Warning) HeaderString(agent string, date time.Time) string {
	if agent == "" {
		agent = "-"
	} else {
		// TODO(pquerna): this doesn't escape agent if it contains bad things.
		agent = `"` + agent + `"`
	}
	return fmt.Sprintf(`%d %s "%s" %s`, w, agent, w.String(), date.Format(http.TimeFormat))
}

func (w Warning) String() string {
	switch w {
	case WarningResponseIsStale:
		return "Response is Stale"
	case WarningRevalidationFailed:
		return "Revalidation Failed"
	case WarningDisconnectedOperation:
		return "Disconnected Operation"
	case WarningHeuristicExpiration:
		return "Heuristic Expiration"
	case WarningMiscellaneousWarning:
		// TODO(pquerna): ideally had a better way to override this one code.
		return "Miscellaneous Warning"
	case WarningTransformationApplied:
		return "Transformation Applied"
	case WarningMiscellaneousPersistentWarning:
		// TODO(pquerna): same as WarningMiscellaneousWarning
		return "Miscellaneous Persistent Warning"
	}

	panic(w)
}
<commit_msg>make warning values actually of the type warning<commit_after>/**
 *  Copyright 2015 Paul Querna
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package cacheobject

import (
	"fmt"
	"net/http"
	"time"
)

// Repersents an HTTP Warning: http://tools.ietf.org/html/rfc7234#section-5.5
type Warning int

const (
	// Response is Stale
	// A cache SHOULD generate this whenever the sent response is stale.
	WarningResponseIsStale Warning = 110

	// Revalidation Failed
	// A cache SHOULD generate this when sending a stale
	// response because an attempt to validate the response failed, due to an
	// inability to reach the server.
	WarningRevalidationFailed Warning = 111

	// Disconnected Operation
	// A cache SHOULD generate this if it is intentionally disconnected from
	// the rest of the network for a period of time.
	WarningDisconnectedOperation Warning = 112

	// Heuristic Expiration
	//
	// A cache SHOULD generate this if it heuristically chose a freshness
	// lifetime greater than 24 hours and the response's age is greater than
	// 24 hours.
	WarningHeuristicExpiration Warning = 113

	// Miscellaneous Warning
	//
	// The warning text can include arbitrary information to be presented to
	// a human user or logged.  A system receiving this warning MUST NOT
	// take any automated action, besides presenting the warning to the
	// user.
	WarningMiscellaneousWarning Warning = 199

	// Transformation Applied
	//
	// This Warning code MUST be added by a proxy if it applies any
	// transformation to the representation, such as changing the
	// content-coding, media-type, or modifying the representation data,
	// unless this Warning code already appears in the response.
	WarningTransformationApplied Warning = 214

	// Miscellaneous Persistent Warning
	//
	// The warning text can include arbitrary information to be presented to
	// a human user or logged.  A system receiving this warning MUST NOT
	// take any automated action.
	WarningMiscellaneousPersistentWarning Warning = 299
)

func (w Warning) HeaderString(agent string, date time.Time) string {
	if agent == "" {
		agent = "-"
	} else {
		// TODO(pquerna): this doesn't escape agent if it contains bad things.
		agent = `"` + agent + `"`
	}
	return fmt.Sprintf(`%d %s "%s" %s`, w, agent, w.String(), date.Format(http.TimeFormat))
}

func (w Warning) String() string {
	switch w {
	case WarningResponseIsStale:
		return "Response is Stale"
	case WarningRevalidationFailed:
		return "Revalidation Failed"
	case WarningDisconnectedOperation:
		return "Disconnected Operation"
	case WarningHeuristicExpiration:
		return "Heuristic Expiration"
	case WarningMiscellaneousWarning:
		// TODO(pquerna): ideally had a better way to override this one code.
		return "Miscellaneous Warning"
	case WarningTransformationApplied:
		return "Transformation Applied"
	case WarningMiscellaneousPersistentWarning:
		// TODO(pquerna): same as WarningMiscellaneousWarning
		return "Miscellaneous Persistent Warning"
	}

	panic(w)
}
<|endoftext|>


<commit_before>package handler

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"github.com/Lunchr/luncher-api/db"
	"github.com/Lunchr/luncher-api/db/model"
	"github.com/Lunchr/luncher-api/router"
	"github.com/Lunchr/luncher-api/session"
	"github.com/deiwin/facebook"
	"github.com/julienschmidt/httprouter"
)

// OfferGroupPost handles GET requests to /restaurant/posts/:date. It returns all current day's offers for the region.
func OfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants) router.HandlerWithParams {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
		date model.DateWithoutTime) *router.HandlerError {
		post, err := c.GetByDate(date, restaurant.ID)
		if err == mgo.ErrNotFound {
			return router.NewHandlerError(err, "Offer group post not found", http.StatusNotFound)
		} else if err != nil {
			return router.NewHandlerError(err, "An error occured while trying to fetch a offer group post", http.StatusInternalServerError)
		}
		return writeJSON(w, post)
	}
	return forDate(sessionManager, users, restaurants, handler)
}

// PostOfferGroupPost handles POST requests to /restaurant/posts. It stores the info in the DB and updates the post in FB.
func PostOfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	offers db.Offers, regions db.Regions, fbAuth facebook.Authenticator) router.Handler {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant) *router.HandlerError {
		post, handlerErr := parseOfferGroupPost(r, restaurant)
		if handlerErr != nil {
			return handlerErr
		}
		insertedPosts, err := c.Insert(post)
		if err != nil {
			return router.NewHandlerError(err, "Failed to store the post in the DB", http.StatusInternalServerError)
		}
		insertedPost := insertedPosts[0]
		if handlerErr = updateGroupPost(insertedPost, user, restaurant, offers, regions, c, fbAuth); handlerErr != nil {
			return handlerErr
		}
		return writeJSON(w, insertedPost)
	}
	return forRestaurant(sessionManager, users, restaurants, handler)
}

// PutOfferGroupPost handles PUT requests to /restaurant/posts/:date. It stores the info in the DB and updates the post in FB.
func PutOfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	offers db.Offers, regions db.Regions, fbAuth facebook.Authenticator) router.HandlerWithParams {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
		date model.DateWithoutTime) *router.HandlerError {
		post, handlerErr := parseOfferGroupPost(r, restaurant)
		if handlerErr != nil {
			return handlerErr
		}
		if post.Date != date {
			return router.NewSimpleHandlerError("Unexpected date value", http.StatusBadRequest)
		}
		err := c.UpdateByID(post.ID, post)
		if err != nil {
			return router.NewSimpleHandlerError("Failed to insert the post to DB", http.StatusBadRequest)
		}
		return writeJSON(w, post)
	}
	return forDate(sessionManager, users, restaurants, handler)
}

type HandlerWithRestaurantAndDate func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
	date model.DateWithoutTime) *router.HandlerError

func forDate(sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	handler HandlerWithRestaurantAndDate) router.HandlerWithParams {
	handlerWithRestaurant := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, user *model.User,
		restaurant *model.Restaurant) *router.HandlerError {
		date := model.DateWithoutTime(ps.ByName("date"))
		if date == "" {
			return router.NewStringHandlerError("Date not specified!", "Please specify a date", http.StatusBadRequest)
		}
		if !date.IsValid() {
			return router.NewSimpleHandlerError("Invalid date specified", http.StatusBadRequest)
		}
		return handler(w, r, user, restaurant, date)
	}
	return forRestaurantWithParams(sessionManager, users, restaurants, handlerWithRestaurant)
}

func parseOfferGroupPost(r *http.Request, restaurant *model.Restaurant) (*model.OfferGroupPost, *router.HandlerError) {
	var post struct {
		ID              bson.ObjectId `json:"_id"`
		MessageTemplate string        `json:"message_template"`
		Date            string        `json:"date"`
	}
	err := json.NewDecoder(r.Body).Decode(&post)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to parse the post", http.StatusBadRequest)
	}
	date := model.DateWithoutTime(post.Date)
	if date == "" {
		return nil, router.NewStringHandlerError("Date not specified!", "Please specify a date", http.StatusBadRequest)
	}
	if !date.IsValid() {
		return nil, router.NewSimpleHandlerError("Invalid date specified", http.StatusBadRequest)
	}
	return &model.OfferGroupPost{
		ID:              post.ID,
		MessageTemplate: post.MessageTemplate,
		Date:            date,
		RestaurantID:    restaurant.ID,
	}, nil
}

func updateGroupPostForDate(date model.DateWithoutTime, user *model.User, restaurant *model.Restaurant, offers db.Offers,
	regions db.Regions, groupPosts db.OfferGroupPosts, fbAuth facebook.Authenticator) *router.HandlerError {
	post, err := groupPosts.GetByDate(date, restaurant.ID)
	if err == mgo.ErrNotFound {
		postToInsert := &model.OfferGroupPost{
			RestaurantID:    restaurant.ID,
			Date:            date,
			MessageTemplate: restaurant.DefaultGroupPostMessageTemplate,
		}
		insertedPosts, err := groupPosts.Insert(postToInsert)
		if err != nil {
			router.NewHandlerError(err, "Failed to create a group post with restaurant defaults", http.StatusInternalServerError)
		}
		post = insertedPosts[0]
	} else if err != nil {
		return router.NewHandlerError(err, "Failed to fetch a group post for that date", http.StatusInternalServerError)
	}
	return updateGroupPost(post, user, restaurant, offers, regions, groupPosts, fbAuth)
}

func updateGroupPost(post *model.OfferGroupPost, user *model.User, restaurant *model.Restaurant, offers db.Offers,
	regions db.Regions, groupPosts db.OfferGroupPosts, fbAuth facebook.Authenticator) *router.HandlerError {
	if restaurant.FacebookPageID == "" {
		return nil
	}
	fbAPI := fbAuth.APIConnection(&user.Session.FacebookUserToken)
	// Remove the current post from FB, if it's already there
	if post.FBPostID != "" {
		err := fbAPI.PostDelete(user.Session.FacebookPageToken, post.FBPostID)
		if err != nil {
			return router.NewHandlerError(err, "Failed to delete the current post from Facebook", http.StatusBadGateway)
		}
	}
	offersForDate, handlerErr := getOffersForDate(post.Date, restaurant, offers, regions)
	if handlerErr != nil {
		return handlerErr
	} else if len(offersForDate) == 0 {
		return nil
	}
	message := formFBMessage(post, offersForDate)
	// Add the new version
	fbPost, err := fbAPI.PagePublish(user.Session.FacebookPageToken, restaurant.FacebookPageID, message)
	if err != nil {
		return router.NewHandlerError(err, "Failed to post the offer to Facebook", http.StatusBadGateway)
	}
	post.FBPostID = fbPost.ID
	if err = groupPosts.UpdateByID(post.ID, post); err != nil {
		return router.NewHandlerError(err, "Failed to update a group post in the DB", http.StatusInternalServerError)
	}
	return nil
}

func formFBMessage(post *model.OfferGroupPost, offers []*model.Offer) string {
	offerMessages := make([]string, len(offers))
	for i, offer := range offers {
		offerMessages[i] = formFBOfferMessage(offer)
	}
	offersMessage := strings.Join(offerMessages, "\n")
	return fmt.Sprintf("%s\n\n%s", post.MessageTemplate, offersMessage)
}

func formFBOfferMessage(o *model.Offer) string {
	// TODO get rid of the hard-coded €
	return fmt.Sprintf("%s - %.2f€", o.Title, o.Price)
}

func getOffersForDate(date model.DateWithoutTime, restaurant *model.Restaurant, offers db.Offers, regions db.Regions) ([]*model.Offer, *router.HandlerError) {
	region, err := regions.GetName(restaurant.Region)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to find the restaurant's region", http.StatusInternalServerError)
	}
	location, err := time.LoadLocation(region.Location)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to load region's location", http.StatusInternalServerError)
	}
	startTime, endTime, err := date.TimeBounds(location)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to parse a date", http.StatusInternalServerError)
	}
	offersForDate, err := offers.GetForRestaurantWithinTimeBounds(restaurant.ID, startTime, endTime)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to find offers for this date", http.StatusInternalServerError)
	}
	return offersForDate, nil
}
<commit_msg>Fix group post PUT not updating the post in FB<commit_after>package handler

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"

	"github.com/Lunchr/luncher-api/db"
	"github.com/Lunchr/luncher-api/db/model"
	"github.com/Lunchr/luncher-api/router"
	"github.com/Lunchr/luncher-api/session"
	"github.com/deiwin/facebook"
	"github.com/julienschmidt/httprouter"
)

// OfferGroupPost handles GET requests to /restaurant/posts/:date. It returns all current day's offers for the region.
func OfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants) router.HandlerWithParams {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
		date model.DateWithoutTime) *router.HandlerError {
		post, err := c.GetByDate(date, restaurant.ID)
		if err == mgo.ErrNotFound {
			return router.NewHandlerError(err, "Offer group post not found", http.StatusNotFound)
		} else if err != nil {
			return router.NewHandlerError(err, "An error occured while trying to fetch a offer group post", http.StatusInternalServerError)
		}
		return writeJSON(w, post)
	}
	return forDate(sessionManager, users, restaurants, handler)
}

// PostOfferGroupPost handles POST requests to /restaurant/posts. It stores the info in the DB and updates the post in FB.
func PostOfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	offers db.Offers, regions db.Regions, fbAuth facebook.Authenticator) router.Handler {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant) *router.HandlerError {
		post, handlerErr := parseOfferGroupPost(r, restaurant)
		if handlerErr != nil {
			return handlerErr
		}
		insertedPosts, err := c.Insert(post)
		if err != nil {
			return router.NewHandlerError(err, "Failed to store the post in the DB", http.StatusInternalServerError)
		}
		insertedPost := insertedPosts[0]
		if handlerErr = updateGroupPost(insertedPost, user, restaurant, offers, regions, c, fbAuth); handlerErr != nil {
			return handlerErr
		}
		return writeJSON(w, insertedPost)
	}
	return forRestaurant(sessionManager, users, restaurants, handler)
}

// PutOfferGroupPost handles PUT requests to /restaurant/posts/:date. It stores the info in the DB and updates the post in FB.
func PutOfferGroupPost(c db.OfferGroupPosts, sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	offers db.Offers, regions db.Regions, fbAuth facebook.Authenticator) router.HandlerWithParams {
	handler := func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
		date model.DateWithoutTime) *router.HandlerError {
		post, handlerErr := parseOfferGroupPost(r, restaurant)
		if handlerErr != nil {
			return handlerErr
		}
		if post.Date != date {
			return router.NewSimpleHandlerError("Unexpected date value", http.StatusBadRequest)
		}
		err := c.UpdateByID(post.ID, post)
		if err != nil {
			return router.NewSimpleHandlerError("Failed to insert the post to DB", http.StatusBadRequest)
		}
		if handlerErr = updateGroupPost(post, user, restaurant, offers, regions, c, fbAuth); handlerErr != nil {
			return handlerErr
		}
		return writeJSON(w, post)
	}
	return forDate(sessionManager, users, restaurants, handler)
}

type HandlerWithRestaurantAndDate func(w http.ResponseWriter, r *http.Request, user *model.User, restaurant *model.Restaurant,
	date model.DateWithoutTime) *router.HandlerError

func forDate(sessionManager session.Manager, users db.Users, restaurants db.Restaurants,
	handler HandlerWithRestaurantAndDate) router.HandlerWithParams {
	handlerWithRestaurant := func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, user *model.User,
		restaurant *model.Restaurant) *router.HandlerError {
		date := model.DateWithoutTime(ps.ByName("date"))
		if date == "" {
			return router.NewStringHandlerError("Date not specified!", "Please specify a date", http.StatusBadRequest)
		}
		if !date.IsValid() {
			return router.NewSimpleHandlerError("Invalid date specified", http.StatusBadRequest)
		}
		return handler(w, r, user, restaurant, date)
	}
	return forRestaurantWithParams(sessionManager, users, restaurants, handlerWithRestaurant)
}

func parseOfferGroupPost(r *http.Request, restaurant *model.Restaurant) (*model.OfferGroupPost, *router.HandlerError) {
	var post struct {
		ID              bson.ObjectId `json:"_id"`
		MessageTemplate string        `json:"message_template"`
		Date            string        `json:"date"`
	}
	err := json.NewDecoder(r.Body).Decode(&post)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to parse the post", http.StatusBadRequest)
	}
	date := model.DateWithoutTime(post.Date)
	if date == "" {
		return nil, router.NewStringHandlerError("Date not specified!", "Please specify a date", http.StatusBadRequest)
	}
	if !date.IsValid() {
		return nil, router.NewSimpleHandlerError("Invalid date specified", http.StatusBadRequest)
	}
	return &model.OfferGroupPost{
		ID:              post.ID,
		MessageTemplate: post.MessageTemplate,
		Date:            date,
		RestaurantID:    restaurant.ID,
	}, nil
}

func updateGroupPostForDate(date model.DateWithoutTime, user *model.User, restaurant *model.Restaurant, offers db.Offers,
	regions db.Regions, groupPosts db.OfferGroupPosts, fbAuth facebook.Authenticator) *router.HandlerError {
	post, err := groupPosts.GetByDate(date, restaurant.ID)
	if err == mgo.ErrNotFound {
		postToInsert := &model.OfferGroupPost{
			RestaurantID:    restaurant.ID,
			Date:            date,
			MessageTemplate: restaurant.DefaultGroupPostMessageTemplate,
		}
		insertedPosts, err := groupPosts.Insert(postToInsert)
		if err != nil {
			router.NewHandlerError(err, "Failed to create a group post with restaurant defaults", http.StatusInternalServerError)
		}
		post = insertedPosts[0]
	} else if err != nil {
		return router.NewHandlerError(err, "Failed to fetch a group post for that date", http.StatusInternalServerError)
	}
	return updateGroupPost(post, user, restaurant, offers, regions, groupPosts, fbAuth)
}

func updateGroupPost(post *model.OfferGroupPost, user *model.User, restaurant *model.Restaurant, offers db.Offers,
	regions db.Regions, groupPosts db.OfferGroupPosts, fbAuth facebook.Authenticator) *router.HandlerError {
	if restaurant.FacebookPageID == "" {
		return nil
	}
	fbAPI := fbAuth.APIConnection(&user.Session.FacebookUserToken)
	// Remove the current post from FB, if it's already there
	if post.FBPostID != "" {
		err := fbAPI.PostDelete(user.Session.FacebookPageToken, post.FBPostID)
		if err != nil {
			return router.NewHandlerError(err, "Failed to delete the current post from Facebook", http.StatusBadGateway)
		}
	}
	offersForDate, handlerErr := getOffersForDate(post.Date, restaurant, offers, regions)
	if handlerErr != nil {
		return handlerErr
	} else if len(offersForDate) == 0 {
		return nil
	}
	message := formFBMessage(post, offersForDate)
	// Add the new version
	fbPost, err := fbAPI.PagePublish(user.Session.FacebookPageToken, restaurant.FacebookPageID, message)
	if err != nil {
		return router.NewHandlerError(err, "Failed to post the offer to Facebook", http.StatusBadGateway)
	}
	post.FBPostID = fbPost.ID
	if err = groupPosts.UpdateByID(post.ID, post); err != nil {
		return router.NewHandlerError(err, "Failed to update a group post in the DB", http.StatusInternalServerError)
	}
	return nil
}

func formFBMessage(post *model.OfferGroupPost, offers []*model.Offer) string {
	offerMessages := make([]string, len(offers))
	for i, offer := range offers {
		offerMessages[i] = formFBOfferMessage(offer)
	}
	offersMessage := strings.Join(offerMessages, "\n")
	return fmt.Sprintf("%s\n\n%s", post.MessageTemplate, offersMessage)
}

func formFBOfferMessage(o *model.Offer) string {
	// TODO get rid of the hard-coded €
	return fmt.Sprintf("%s - %.2f€", o.Title, o.Price)
}

func getOffersForDate(date model.DateWithoutTime, restaurant *model.Restaurant, offers db.Offers, regions db.Regions) ([]*model.Offer, *router.HandlerError) {
	region, err := regions.GetName(restaurant.Region)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to find the restaurant's region", http.StatusInternalServerError)
	}
	location, err := time.LoadLocation(region.Location)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to load region's location", http.StatusInternalServerError)
	}
	startTime, endTime, err := date.TimeBounds(location)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to parse a date", http.StatusInternalServerError)
	}
	offersForDate, err := offers.GetForRestaurantWithinTimeBounds(restaurant.ID, startTime, endTime)
	if err != nil {
		return nil, router.NewHandlerError(err, "Failed to find offers for this date", http.StatusInternalServerError)
	}
	return offersForDate, nil
}
<|endoftext|>


<commit_before>package fritz

// Thermostat models the "HKR" device.
// codebeat:disable[TOO_MANY_IVARS]
type Thermostat struct {
	Measured   string `xml:"tist"`       // Measured temperature.
	Goal       string `xml:"tsoll"`      // Desired temperature, user controlled.
	Saving     string `xml:"absenk"`     // Energy saving temperature.
	Comfort    string `xml:"komfort"`    // Comfortable temperature.
	Lock       string `xml:"lock"`       // Switch locked (box defined)? 1/0 (empty if not known or if there was an error).
	DeviceLock string `xml:"devicelock"` // Switch locked (device defined)? 1/0 (empty if not known or if there was an error).
	ErrorCode  string `xml:"errorcode"`  // Error codes: 0 = OK, 1 = ... see https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AHA-HTTP-Interface.pdf.
	BatteryLow string `xml:"batterylow"` // "0" if the battery is OK, "1" if it is running low on capacity.
}

// codebeat:enable[TOO_MANY_IVARS]
<commit_msg>Issue #22: add next change to thermostat data model<commit_after>package fritz

// Thermostat models the "HKR" device.
// codebeat:disable[TOO_MANY_IVARS]
type Thermostat struct {
	Measured   string `xml:"tist"`           // Measured temperature.
	Goal       string `xml:"tsoll"`          // Desired temperature, user controlled.
	Saving     string `xml:"absenk"`         // Energy saving temperature.
	Comfort    string `xml:"komfort"`        // Comfortable temperature.
	NextChange NextChange `xml:"nextchange"` // The next scheduled temperature change.
	Lock       string `xml:"lock"`           // Switch locked (box defined)? 1/0 (empty if not known or if there was an error).
	DeviceLock string `xml:"devicelock"`     // Switch locked (device defined)? 1/0 (empty if not known or if there was an error).
	ErrorCode  string `xml:"errorcode"`      // Error codes: 0 = OK, 1 = ... see https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AHA-HTTP-Interface.pdf.
	BatteryLow string `xml:"batterylow"`     // "0" if the battery is OK, "1" if it is running low on capacity.
}

type NextChange struct {
	TimeStamp string `xml:"endperiod"` // Timestamp (epoch time) when the next temperature switch is scheduled.
	Goal      string `xml:"tchange"`   // The temperature to switch to. Same unit convention as in Thermostat.Measured.
}

// codebeat:enable[TOO_MANY_IVARS]
<|endoftext|>


<commit_before>package gosh_test

import (
	"fmt"

	. "github.com/polydawn/gosh"
)

func ExampleNormalFlow() {
	Sh("echo", "hello world!")

	// Output:
	// hello world!
}

func ExampleStartCollectFlow() {
	proc := Gosh("echo", "hello world!").Start()
	proc.Wait()

	// Output:
	// hello world!
}

func ExampleCollectOutput() {
	str := Gosh("echo", "hello world!").Output()
	fmt.Println(str)

	// Output:
	// hello world!
}
<commit_msg>More examples: env and exit codes.<commit_after>package gosh_test

import (
	"fmt"

	. "github.com/polydawn/gosh"
)

func ExampleNormalFlow() {
	Sh("echo", "hello world!")

	// Output:
	// hello world!
}

func ExampleStartCollectFlow() {
	proc := Gosh("echo", "hello world!").Start()
	proc.Wait()

	// Output:
	// hello world!
}

func ExampleCollectOutput() {
	str := Gosh("echo", "hello world!").Output()
	fmt.Println(str)

	// Output:
	// hello world!
}

func ExampleEnvVars() {
	str := Gosh(ClearEnv{}, Env{"key": "val"}, "env").Output()
	fmt.Println(str)

	// Output:
	// key=val
}

func ExampleErrorExit() {
	defer func() {
		err := recover()
		fmt.Println("code", err.(FailureExitCode).Code)
	}()
	Sh("bash", "-c", "exit 22")

	// Output:
	// code 22
}

func ExampleOkExit() {
	Sh("bash", "-c", "exit 22", Opts{OkExit: AnyExit})
	// (no output; point is just that it doesn't panic)

	// Output:
}
<|endoftext|>


<commit_before>package clui

import (
	"fmt"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

type BarData struct {
	Value float64
	Title string
	Fg    term.Attribute
	Bg    term.Attribute
	Ch    rune
}

type BarDataCell struct {
	Item     string
	Id       int
	Value    float64
	MaxValue float64
	Fg       term.Attribute
	Bg       term.Attribute
	Ch       rune
}

/*
Label is a decorative control that can display text in horizontal
or vertical direction. Other available text features are alignment
and multi-line ability. Text can be single- or multi-colored with
tags inside the text. Multi-colored strings have limited support
of alignment feature: if text is longer than Label width the text
is always left aligned
*/
type BarChart struct {
	ControlBase
	data        []BarData
	autosize    bool
	gap         int
	barWidth    int
	legendWidth int
	valueWidth  int
	showMarks   bool
	showTicks   bool
	showTitles  bool
	onDrawCell  func(*BarDataCell)
}

/*
NewLabel creates a new label.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
w and h - are minimal size of the control.
title - is Label title.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func NewBarChart(view View, parent Control, w, h int, scale int) *BarChart {
	c := new(BarChart)

	if w == AutoSize {
		w = 10
	}
	if h == AutoSize {
		h = 5
	}

	c.view = view
	c.parent = parent

	c.SetSize(w, h)
	c.SetConstraints(w, h)
	c.tabSkip = true
	c.showTitles = true
	c.barWidth = 3
	c.data = make([]BarData, 0)

	if parent != nil {
		parent.AddChild(c, scale)
	}

	return c
}

// Repaint draws the control on its View surface
func (b *BarChart) Repaint() {
	canvas := b.view.Canvas()
	tm := b.view.Screen().Theme()

	fg, bg := RealColor(tm, b.fg, ColorBarChartText), RealColor(tm, b.bg, ColorBarChartBack)
	canvas.FillRect(b.x, b.y, b.width, b.height, term.Cell{Ch: ' ', Fg: fg, Bg: bg})

	if len(b.data) == 0 {
		return
	}

	b.drawRulers(tm, fg, bg)
	b.drawValues(tm, fg, bg)
	b.drawLegend(tm, fg, bg)
	b.drawBars(tm, fg, bg)
}

func (b *BarChart) barHeight() int {
	if b.showTitles {
		return b.height - 2
	} else {
		return b.height
	}
}

func (b *BarChart) drawBars(tm Theme, fg, bg term.Attribute) {
	if len(b.data) == 0 {
		return
	}

	start, width := b.calculateBarArea()
	if width < 2 {
		return
	}

	barW := b.calculateBarWidth()
	if barW == 0 {
		return
	}

	coeff, max := b.calculateMultiplier()
	if coeff == 0.0 {
		return
	}

	h := b.barHeight()
	pos := start
	canvas := b.view.Canvas()
	parts := []rune(tm.SysObject(ObjBarChart))

	for idx, d := range b.data {
		if pos+barW >= start+width {
			break
		}

		fColor, bColor := d.Fg, d.Bg
		ch := d.Ch
		if fColor == ColorDefault {
			fColor = fg
		}
		if bColor == ColorDefault {
			bColor = bg
		}
		if ch == 0 {
			ch = parts[0]
		}

		barH := int(d.Value * coeff)
		if b.onDrawCell == nil {
			cell := term.Cell{Ch: ch, Fg: fg, Bg: bg}
			canvas.FillRect(b.x+pos, b.y+h-barH, barW, barH, cell)
		} else {
			cellDef := BarDataCell{Item: d.Title, Id: idx, Value: d.Value, MaxValue: max, Fg: fg, Bg: bg, Ch: ch}
			for dy := 0; dy < barH; dy++ {
				req := cellDef
				b.onDrawCell(&req)
				cell := term.Cell{Ch: req.Ch, Fg: req.Fg, Bg: req.Bg}
				for dx := 0; dx < barW; dx++ {
					canvas.PutSymbol(b.x+pos+dx, b.y+h-1-dy, cell)
				}
			}
		}

		if b.showTitles {
			if b.showTicks {
				c := parts[7]
				canvas.PutSymbol(b.x+pos+barW/2, b.y+h, term.Cell{Ch: c, Bg: bg, Fg: fg})
			}
			var s string
			shift := 0
			if xs.Len(d.Title) > barW {
				s = CutText(d.Title, barW)
			} else {
				shift, s = AlignText(d.Title, barW, AlignCenter)
			}
			canvas.PutText(b.x+pos+shift, b.y+h+1, s, fg, bg)
		}

		pos += barW + b.gap
	}
}

func (b *BarChart) drawLegend(tm Theme, fg, bg term.Attribute) {
	pos, width := b.calculateBarArea()
	if pos+width >= b.width-3 {
		return
	}

	canvas := b.view.Canvas()
	for idx, d := range b.data {
		if idx >= b.height {
			break
		}

		canvas.PutSymbol(b.x+pos+width, b.y+idx, term.Cell{Ch: d.Ch, Fg: d.Fg, Bg: d.Bg})
		s := CutText(fmt.Sprintf(" - %v", d.Title), b.legendWidth)
		canvas.PutText(b.x+pos+width+1, b.y+idx, s, fg, bg)
	}
}

func (b *BarChart) drawValues(tm Theme, fg, bg term.Attribute) {
	pos, _ := b.calculateBarArea()
	if pos == 0 {
		return
	}

	h := b.barHeight()
	coeff, max := b.calculateMultiplier()
	if max == coeff {
		return
	}

	canvas := b.view.Canvas()
	dy := 0
	format := fmt.Sprintf("%%%vf", b.valueWidth)
	for dy < h-1 {
		v := float64(h-dy) / float64(h) * max
		s := fmt.Sprintf(format, v)
		s = CutText(s, b.valueWidth)
		canvas.PutText(b.x, b.y+dy, s, fg, bg)

		dy += 2
	}
}

func (b *BarChart) drawRulers(tm Theme, fg, bg term.Attribute) {
	if b.valueWidth <= 0 && b.legendWidth <= 0 && !b.showTitles {
		return
	}

	pos, vWidth := b.calculateBarArea()

	parts := []rune(tm.SysObject(ObjBarChart))
	h := b.barHeight()

	if pos > 0 {
		pos--
		vWidth++
	}

	// horizontal and vertical lines, corner
	cH, cV, cC := parts[1], parts[2], parts[5]
	canvas := b.view.Canvas()

	if pos > 0 {
		for dy := 0; dy < h; dy++ {
			canvas.PutSymbol(b.x+pos, b.y+dy, term.Cell{Ch: cV, Fg: fg, Bg: bg})
		}
	}
	if b.showTitles {
		for dx := 0; dx < vWidth; dx++ {
			canvas.PutSymbol(b.x+pos+dx, b.y+h, term.Cell{Ch: cH, Fg: fg, Bg: bg})
		}
	}
	if pos > 0 && b.showTitles {
		canvas.PutSymbol(b.x+pos, b.y+h, term.Cell{Ch: cC, Fg: fg, Bg: bg})
	}
}

func (b *BarChart) calculateBarArea() (int, int) {
	w := b.width
	pos := 0

	if b.valueWidth < w/2 {
		w = w - b.valueWidth - 1
		pos = b.valueWidth + 1
	}

	if b.legendWidth < w/2 {
		w -= b.legendWidth
	}

	return pos, w
}

func (b *BarChart) calculateBarWidth() int {
	if len(b.data) == 0 {
		return 0
	}

	if !b.autosize {
		return b.barWidth
	}

	w := b.width
	if b.valueWidth < w/2 {
		w = w - b.valueWidth - 1
	}
	if b.legendWidth < w/2 {
		w -= b.legendWidth
	}

	dataCount := len(b.data)
	minSize := dataCount*b.barWidth + (dataCount-1)*b.gap
	if minSize >= w {
		return b.barWidth
	}

	return (w - (dataCount-1)*b.gap) / dataCount
}

func (b *BarChart) calculateMultiplier() (float64, float64) {
	if len(b.data) == 0 {
		return 0, 0
	}

	h := b.barHeight()
	if h <= 1 {
		return 0, 0
	}

	max := b.data[0].Value
	for _, val := range b.data {
		if val.Value > max {
			max = val.Value
		}
	}

	if max == 0 {
		return 0, 0
	}

	return float64(h) / max, max
}

func (b *BarChart) AddData(val BarData) {
	b.data = append(b.data, val)
}

func (b *BarChart) ClearData() {
	b.data = make([]BarData, 0)
}

func (b *BarChart) SetData(data []BarData) {
	b.data = make([]BarData, len(data))
	copy(b.data, data)
}

func (b *BarChart) AutoSize() bool {
	return b.autosize
}

func (b *BarChart) SetAutoSize(auto bool) {
	b.autosize = auto
}

func (b *BarChart) Gap() int {
	return b.gap
}

func (b *BarChart) SetGap(gap int) {
	b.gap = gap
}

func (b *BarChart) MinBarSize() int {
	return b.barWidth
}

func (b *BarChart) SetMinBarSize(size int) {
	b.barWidth = size
}

func (b *BarChart) ValueWidth() int {
	return b.valueWidth
}

func (b *BarChart) SetValueWidth(width int) {
	b.valueWidth = width
}

func (b *BarChart) ShowTitles() bool {
	return b.showTitles
}

func (b *BarChart) SetShowTitles(show bool) {
	b.showTitles = show
}

func (b *BarChart) LegendWidth() int {
	return b.legendWidth
}

func (b *BarChart) SetLegendWidth(width int) {
	b.legendWidth = width
}

func (b *BarChart) OnDrawCell(fn func(*BarDataCell)) {
	b.onDrawCell = fn
}

func (b *BarChart) ShowMarks() bool {
	return b.showMarks
}

func (b *BarChart) SetShowMarks(show bool) {
	b.showMarks = show
}
<commit_msg>#37 - basic BarChart and demo for it<commit_after>package clui

import (
	"fmt"
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

// BarData is info about one bar in the chart. Every
// bar can be customized by setting its own colors and
// rune to draw the bar. Use ColorDefault for Fg and Bg,
// and 0 for Ch to draw with BarChart defaults
type BarData struct {
	Value float64
	Title string
	Fg    term.Attribute
	Bg    term.Attribute
	Ch    rune
}

// BarDataCell is used in callback to user to draw with
// customized colors and runes
type BarDataCell struct {
	// Title of the bar
	Item string
	// order number of the bar
	ID int
	// value of the bar that is currently drawn
	Value float64
	// maximum value of the bar
	MaxValue float64
	// Default attributes and rune to draw the bar
	Fg term.Attribute
	Bg term.Attribute
	Ch rune
}

/*
BarChart is a chart that represents grouped data with
rectangular bars. It can be monochrome - defaut behavior.
One can assign individual color to each bar and even use
custom drawn bars to display multicolored bars depending
on bar value.
All bars have the same width: either constant BarSize - in
case of AutoSize is false, or automatically calculated but
cannot be less than BarSize. Bars that do not fit the chart
area are not displayed.
BarChart displays vertical axis with values on the chart left
if ValueWidth greater than 0, horizontal axis with bar titles
if ShowTitles is true (to enable displaying marks on horizontal
axis, set ShowMarks to true), and chart legend on the right if
LegendWidth is greater than 3.
If LegendWidth is greater than half of the chart it is not
displayed. The same is applied to ValueWidth
*/
type BarChart struct {
	ControlBase
	data        []BarData
	autosize    bool
	gap         int
	barWidth    int
	legendWidth int
	valueWidth  int
	showMarks   bool
	showTicks   bool
	showTitles  bool
	onDrawCell  func(*BarDataCell)
}

/*
NewBarChart creates a new bar chart.
view - is a View that manages the control
parent - is container that keeps the control. The same View can be a view and a parent at the same time.
w and h - are minimal size of the control.
scale - the way of scaling the control when the parent is resized. Use DoNotScale constant if the
control should keep its original size.
*/
func NewBarChart(view View, parent Control, w, h int, scale int) *BarChart {
	c := new(BarChart)

	if w == AutoSize {
		w = 10
	}
	if h == AutoSize {
		h = 5
	}

	c.view = view
	c.parent = parent

	c.SetSize(w, h)
	c.SetConstraints(w, h)
	c.tabSkip = true
	c.showTitles = true
	c.barWidth = 3
	c.data = make([]BarData, 0)

	if parent != nil {
		parent.AddChild(c, scale)
	}

	return c
}

// Repaint draws the control on its View surface
func (b *BarChart) Repaint() {
	canvas := b.view.Canvas()
	tm := b.view.Screen().Theme()

	fg, bg := RealColor(tm, b.fg, ColorBarChartText), RealColor(tm, b.bg, ColorBarChartBack)
	canvas.FillRect(b.x, b.y, b.width, b.height, term.Cell{Ch: ' ', Fg: fg, Bg: bg})

	if len(b.data) == 0 {
		return
	}

	b.drawRulers(tm, fg, bg)
	b.drawValues(tm, fg, bg)
	b.drawLegend(tm, fg, bg)
	b.drawBars(tm, fg, bg)
}

func (b *BarChart) barHeight() int {
	if b.showTitles {
		return b.height - 2
	}
	return b.height
}

func (b *BarChart) drawBars(tm Theme, fg, bg term.Attribute) {
	if len(b.data) == 0 {
		return
	}

	start, width := b.calculateBarArea()
	if width < 2 {
		return
	}

	barW := b.calculateBarWidth()
	if barW == 0 {
		return
	}

	coeff, _ := b.calculateMultiplier()
	if coeff == 0.0 {
		return
	}

	h := b.barHeight()
	pos := start
	canvas := b.view.Canvas()
	parts := []rune(tm.SysObject(ObjBarChart))

	for idx, d := range b.data {
		if pos+barW >= start+width {
			break
		}

		fColor, bColor := d.Fg, d.Bg
		ch := d.Ch
		if fColor == ColorDefault {
			fColor = fg
		}
		if bColor == ColorDefault {
			bColor = bg
		}
		if ch == 0 {
			ch = parts[0]
		}

		barH := int(d.Value * coeff)
		if b.onDrawCell == nil {
			cell := term.Cell{Ch: ch, Fg: fg, Bg: bg}
			canvas.FillRect(b.x+pos, b.y+h-barH, barW, barH, cell)
		} else {
			cellDef := BarDataCell{Item: d.Title, ID: idx, Value: d.Value * coeff, MaxValue: d.Value, Fg: fg, Bg: bg, Ch: ch}
			for dy := 0; dy < barH; dy++ {
				req := cellDef
				b.onDrawCell(&req)
				cell := term.Cell{Ch: req.Ch, Fg: req.Fg, Bg: req.Bg}
				for dx := 0; dx < barW; dx++ {
					canvas.PutSymbol(b.x+pos+dx, b.y+h-1-dy, cell)
				}
			}
		}

		if b.showTitles {
			if b.showTicks {
				c := parts[7]
				canvas.PutSymbol(b.x+pos+barW/2, b.y+h, term.Cell{Ch: c, Bg: bg, Fg: fg})
			}
			var s string
			shift := 0
			if xs.Len(d.Title) > barW {
				s = CutText(d.Title, barW)
			} else {
				shift, s = AlignText(d.Title, barW, AlignCenter)
			}
			canvas.PutText(b.x+pos+shift, b.y+h+1, s, fg, bg)
		}

		pos += barW + b.gap
	}
}

func (b *BarChart) drawLegend(tm Theme, fg, bg term.Attribute) {
	pos, width := b.calculateBarArea()
	if pos+width >= b.width-3 {
		return
	}

	canvas := b.view.Canvas()
	for idx, d := range b.data {
		if idx >= b.height {
			break
		}

		canvas.PutSymbol(b.x+pos+width, b.y+idx, term.Cell{Ch: d.Ch, Fg: d.Fg, Bg: d.Bg})
		s := CutText(fmt.Sprintf(" - %v", d.Title), b.legendWidth)
		canvas.PutText(b.x+pos+width+1, b.y+idx, s, fg, bg)
	}
}

func (b *BarChart) drawValues(tm Theme, fg, bg term.Attribute) {
	pos, _ := b.calculateBarArea()
	if pos == 0 {
		return
	}

	h := b.barHeight()
	coeff, max := b.calculateMultiplier()
	if max == coeff {
		return
	}

	canvas := b.view.Canvas()
	dy := 0
	format := fmt.Sprintf("%%%vf", b.valueWidth)
	for dy < h-1 {
		v := float64(h-dy) / float64(h) * max
		s := fmt.Sprintf(format, v)
		s = CutText(s, b.valueWidth)
		canvas.PutText(b.x, b.y+dy, s, fg, bg)

		dy += 2
	}
}

func (b *BarChart) drawRulers(tm Theme, fg, bg term.Attribute) {
	if b.valueWidth <= 0 && b.legendWidth <= 0 && !b.showTitles {
		return
	}

	pos, vWidth := b.calculateBarArea()

	parts := []rune(tm.SysObject(ObjBarChart))
	h := b.barHeight()

	if pos > 0 {
		pos--
		vWidth++
	}

	// horizontal and vertical lines, corner
	cH, cV, cC := parts[1], parts[2], parts[5]
	canvas := b.view.Canvas()

	if pos > 0 {
		for dy := 0; dy < h; dy++ {
			canvas.PutSymbol(b.x+pos, b.y+dy, term.Cell{Ch: cV, Fg: fg, Bg: bg})
		}
	}
	if b.showTitles {
		for dx := 0; dx < vWidth; dx++ {
			canvas.PutSymbol(b.x+pos+dx, b.y+h, term.Cell{Ch: cH, Fg: fg, Bg: bg})
		}
	}
	if pos > 0 && b.showTitles {
		canvas.PutSymbol(b.x+pos, b.y+h, term.Cell{Ch: cC, Fg: fg, Bg: bg})
	}
}

func (b *BarChart) calculateBarArea() (int, int) {
	w := b.width
	pos := 0

	if b.valueWidth < w/2 {
		w = w - b.valueWidth - 1
		pos = b.valueWidth + 1
	}

	if b.legendWidth < w/2 {
		w -= b.legendWidth
	}

	return pos, w
}

func (b *BarChart) calculateBarWidth() int {
	if len(b.data) == 0 {
		return 0
	}

	if !b.autosize {
		return b.barWidth
	}

	w := b.width
	if b.valueWidth < w/2 {
		w = w - b.valueWidth - 1
	}
	if b.legendWidth < w/2 {
		w -= b.legendWidth
	}

	dataCount := len(b.data)
	minSize := dataCount*b.barWidth + (dataCount-1)*b.gap
	if minSize >= w {
		return b.barWidth
	}

	sz := (w - (dataCount-1)*b.gap) / dataCount
	if sz == 0 {
		sz = 1
	}

	return sz
}

func (b *BarChart) calculateMultiplier() (float64, float64) {
	if len(b.data) == 0 {
		return 0, 0
	}

	h := b.barHeight()
	if h <= 1 {
		return 0, 0
	}

	max := b.data[0].Value
	for _, val := range b.data {
		if val.Value > max {
			max = val.Value
		}
	}

	if max == 0 {
		return 0, 0
	}

	return float64(h) / max, max
}

// AddData appends a new bar to a chart
func (b *BarChart) AddData(val BarData) {
	b.data = append(b.data, val)
}

// ClearData removes all bar from chart
func (b *BarChart) ClearData() {
	b.data = make([]BarData, 0)
}

// SetData assign a new bar list to a chart
func (b *BarChart) SetData(data []BarData) {
	b.data = make([]BarData, len(data))
	copy(b.data, data)
}

// AutoSize returns whether automatic bar width
// calculation is on. If AutoSize is false then all
// bars have width BarWidth. If AutoSize is true then
// bar width is the maximum of three values: BarWidth,
// calculated width that makes all bars fit the
// bar chart area, and 1
func (b *BarChart) AutoSize() bool {
	return b.autosize
}

// SetAutoSize enables or disables automatic bar
// width calculation
func (b *BarChart) SetAutoSize(auto bool) {
	b.autosize = auto
}

// Gap returns width of visual gap between two adjacent bars
func (b *BarChart) Gap() int {
	return b.gap
}

// SetGap sets the space width between two adjacent bars
func (b *BarChart) SetGap(gap int) {
	b.gap = gap
}

// MinBarWidth returns current minimal bar width
func (b *BarChart) MinBarWidth() int {
	return b.barWidth
}

// SetMinBarWidth changes the minimal bar width
func (b *BarChart) SetMinBarWidth(size int) {
	b.barWidth = size
}

// ValueWidth returns the width of the area at the left of
// chart used to draw values. Set it to 0 to turn off the
// value panel
func (b *BarChart) ValueWidth() int {
	return b.valueWidth
}

// SetValueWidth changes width of the value panel on the left
func (b *BarChart) SetValueWidth(width int) {
	b.valueWidth = width
}

// ShowTitles returns if chart displays horizontal axis and
// bar titles under it
func (b *BarChart) ShowTitles() bool {
	return b.showTitles
}

// SetShowTitles turns on and off horizontal axis and bar titles
func (b *BarChart) SetShowTitles(show bool) {
	b.showTitles = show
}

// LegendWidth returns width of chart legend displayed at the
// right side of the chart. Set it to 0 to disable legend
func (b *BarChart) LegendWidth() int {
	return b.legendWidth
}

// SetLegendWidth sets new legend panel width
func (b *BarChart) SetLegendWidth(width int) {
	b.legendWidth = width
}

// OnDrawCell sets callback that allows to draw multicolored
// bars. BarChart sends the current attrubutes and rune that
// it is going to use to display as well as the current value
// of the bar. A user can change the values of BarDataCell
// depending on some external data or calculations - only
// changing colors and rune makes sense. Changing anything else
// does not affect the chart
func (b *BarChart) OnDrawCell(fn func(*BarDataCell)) {
	b.onDrawCell = fn
}

// ShowMarks returns if horizontal axis has mark under each
// bar. To show marks, ShowTitles must be enabled.
func (b *BarChart) ShowMarks() bool {
	return b.showMarks
}

// SetShowMarks turns on and off marks under horizontal axis
func (b *BarChart) SetShowMarks(show bool) {
	b.showMarks = show
}
<|endoftext|>


<commit_before>package displayers

import (
	"fmt"
	"io"
	"strings"

	"github.com/digitalocean/godo"

	"github.com/digitalocean/doctl/do"
)

type KubernetesClusters struct {
	KubernetesClusters do.KubernetesClusters
}

var _ Displayable = &KubernetesClusters{}

func (clusters *KubernetesClusters) JSON(out io.Writer) error {
	return writeJSON(clusters.KubernetesClusters, out)
}

func (clusters *KubernetesClusters) Cols() []string {
	return []string{
		"ID",
		"Name",
		"Region",
		"Version",
		"Status",
		"Endpoint",
		"IPv4",
		"ClusterSubnet",
		"ServiceSubnet",
		"Tags",
		"Created",
		"Updated",
		"NodePools",
	}
}

func (clusters *KubernetesClusters) ColMap() map[string]string {
	return map[string]string{
		"ID":            "ID",
		"Name":          "Name",
		"Region":        "Region",
		"Version":       "Version",
		"ClusterSubnet": "Cluster Subnet",
		"ServiceSubnet": "Service Subnet",
		"IPv4":          "IPv4",
		"Endpoint":      "Endpoint",
		"Tags":          "Tags",
		"Status":        "Status",
		"Created":       "Created At",
		"Updated":       "Updated At",
		"NodePools":     "Node Pools",
	}
}

func (clusters *KubernetesClusters) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(clusters.KubernetesClusters))

	for _, cluster := range clusters.KubernetesClusters {
		tags := strings.Join(cluster.Tags, ",")
		nodePools := make([]string, 0, len(cluster.NodePools))
		for _, pool := range cluster.NodePools {
			nodePools = append(nodePools, pool.Name)
		}
		if cluster.Status == nil {
			cluster.Status = new(godo.KubernetesClusterStatus)
		}

		o := map[string]interface{}{
			"ID":            cluster.ID,
			"Name":          cluster.Name,
			"Region":        cluster.RegionSlug,
			"Version":       cluster.VersionSlug,
			"ClusterSubnet": cluster.ClusterSubnet,
			"ServiceSubnet": cluster.ServiceSubnet,
			"IPv4":          cluster.IPv4,
			"Endpoint":      cluster.Endpoint,
			"Tags":          tags,
			"Status":        cluster.Status.State,
			"Created":       cluster.CreatedAt,
			"Updated":       cluster.UpdatedAt,
			"NodePools":     fmt.Sprintf(strings.Join(nodePools, " ")),
		}
		out = append(out, o)
	}

	return out
}

type KubernetesNodePools struct {
	KubernetesNodePools do.KubernetesNodePools
}

var _ Displayable = &KubernetesNodePools{}

func (nodePools *KubernetesNodePools) JSON(out io.Writer) error {
	return writeJSON(nodePools.KubernetesNodePools, out)
}

func (nodePools *KubernetesNodePools) Cols() []string {
	return []string{
		"ID",
		"Name",
		"Size",
		"Count",
		"Tags",
		"Nodes",
	}
}

func (nodePools *KubernetesNodePools) ColMap() map[string]string {
	return map[string]string{
		"ID":    "ID",
		"Name":  "Name",
		"Size":  "Size",
		"Count": "Count",
		"Tags":  "Tags",
		"Nodes": "Nodes",
	}
}

func (nodePools *KubernetesNodePools) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(nodePools.KubernetesNodePools))

	for _, nodePools := range nodePools.KubernetesNodePools {
		tags := strings.Join(nodePools.Tags, ",")
		nodes := make([]string, 0, len(nodePools.Nodes))
		for _, node := range nodePools.Nodes {
			nodes = append(nodes, node.Name)
		}

		o := map[string]interface{}{
			"ID":    nodePools.ID,
			"Name":  nodePools.Name,
			"Size":  nodePools.Size,
			"Count": nodePools.Count,
			"Tags":  tags,
			"Nodes": nodes,
		}
		out = append(out, o)
	}

	return out
}

type KubernetesVersions struct {
	KubernetesVersions do.KubernetesVersions
}

var _ Displayable = &KubernetesVersions{}

func (versions *KubernetesVersions) JSON(out io.Writer) error {
	return writeJSON(versions.KubernetesVersions, out)
}

func (versions *KubernetesVersions) Cols() []string {
	return []string{
		"Slug",
		"KubernetesVersion",
	}
}

func (versions *KubernetesVersions) ColMap() map[string]string {
	return map[string]string{
		"Slug":              "Slug",
		"KubernetesVersion": "Kubernetes Version",
	}
}

func (versions *KubernetesVersions) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(versions.KubernetesVersions))

	for _, version := range versions.KubernetesVersions {

		o := map[string]interface{}{
			"Slug":              version.KubernetesVersion.Slug,
			"KubernetesVersion": version.KubernetesVersion.KubernetesVersion,
		}
		out = append(out, o)
	}

	return out
}
<commit_msg>k8s: gofmt<commit_after>package displayers

import (
	"fmt"
	"io"
	"strings"

	"github.com/digitalocean/doctl/do"
	"github.com/digitalocean/godo"
)

type KubernetesClusters struct {
	KubernetesClusters do.KubernetesClusters
}

var _ Displayable = &KubernetesClusters{}

func (clusters *KubernetesClusters) JSON(out io.Writer) error {
	return writeJSON(clusters.KubernetesClusters, out)
}

func (clusters *KubernetesClusters) Cols() []string {
	return []string{
		"ID",
		"Name",
		"Region",
		"Version",
		"Status",
		"Endpoint",
		"IPv4",
		"ClusterSubnet",
		"ServiceSubnet",
		"Tags",
		"Created",
		"Updated",
		"NodePools",
	}
}

func (clusters *KubernetesClusters) ColMap() map[string]string {
	return map[string]string{
		"ID":            "ID",
		"Name":          "Name",
		"Region":        "Region",
		"Version":       "Version",
		"ClusterSubnet": "Cluster Subnet",
		"ServiceSubnet": "Service Subnet",
		"IPv4":          "IPv4",
		"Endpoint":      "Endpoint",
		"Tags":          "Tags",
		"Status":        "Status",
		"Created":       "Created At",
		"Updated":       "Updated At",
		"NodePools":     "Node Pools",
	}
}

func (clusters *KubernetesClusters) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(clusters.KubernetesClusters))

	for _, cluster := range clusters.KubernetesClusters {
		tags := strings.Join(cluster.Tags, ",")
		nodePools := make([]string, 0, len(cluster.NodePools))
		for _, pool := range cluster.NodePools {
			nodePools = append(nodePools, pool.Name)
		}
		if cluster.Status == nil {
			cluster.Status = new(godo.KubernetesClusterStatus)
		}

		o := map[string]interface{}{
			"ID":            cluster.ID,
			"Name":          cluster.Name,
			"Region":        cluster.RegionSlug,
			"Version":       cluster.VersionSlug,
			"ClusterSubnet": cluster.ClusterSubnet,
			"ServiceSubnet": cluster.ServiceSubnet,
			"IPv4":          cluster.IPv4,
			"Endpoint":      cluster.Endpoint,
			"Tags":          tags,
			"Status":        cluster.Status.State,
			"Created":       cluster.CreatedAt,
			"Updated":       cluster.UpdatedAt,
			"NodePools":     fmt.Sprintf(strings.Join(nodePools, " ")),
		}
		out = append(out, o)
	}

	return out
}

type KubernetesNodePools struct {
	KubernetesNodePools do.KubernetesNodePools
}

var _ Displayable = &KubernetesNodePools{}

func (nodePools *KubernetesNodePools) JSON(out io.Writer) error {
	return writeJSON(nodePools.KubernetesNodePools, out)
}

func (nodePools *KubernetesNodePools) Cols() []string {
	return []string{
		"ID",
		"Name",
		"Size",
		"Count",
		"Tags",
		"Nodes",
	}
}

func (nodePools *KubernetesNodePools) ColMap() map[string]string {
	return map[string]string{
		"ID":    "ID",
		"Name":  "Name",
		"Size":  "Size",
		"Count": "Count",
		"Tags":  "Tags",
		"Nodes": "Nodes",
	}
}

func (nodePools *KubernetesNodePools) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(nodePools.KubernetesNodePools))

	for _, nodePools := range nodePools.KubernetesNodePools {
		tags := strings.Join(nodePools.Tags, ",")
		nodes := make([]string, 0, len(nodePools.Nodes))
		for _, node := range nodePools.Nodes {
			nodes = append(nodes, node.Name)
		}

		o := map[string]interface{}{
			"ID":    nodePools.ID,
			"Name":  nodePools.Name,
			"Size":  nodePools.Size,
			"Count": nodePools.Count,
			"Tags":  tags,
			"Nodes": nodes,
		}
		out = append(out, o)
	}

	return out
}

type KubernetesVersions struct {
	KubernetesVersions do.KubernetesVersions
}

var _ Displayable = &KubernetesVersions{}

func (versions *KubernetesVersions) JSON(out io.Writer) error {
	return writeJSON(versions.KubernetesVersions, out)
}

func (versions *KubernetesVersions) Cols() []string {
	return []string{
		"Slug",
		"KubernetesVersion",
	}
}

func (versions *KubernetesVersions) ColMap() map[string]string {
	return map[string]string{
		"Slug":              "Slug",
		"KubernetesVersion": "Kubernetes Version",
	}
}

func (versions *KubernetesVersions) KV() []map[string]interface{} {
	out := make([]map[string]interface{}, 0, len(versions.KubernetesVersions))

	for _, version := range versions.KubernetesVersions {

		o := map[string]interface{}{
			"Slug":              version.KubernetesVersion.Slug,
			"KubernetesVersion": version.KubernetesVersion.KubernetesVersion,
		}
		out = append(out, o)
	}

	return out
}
<|endoftext|>


<commit_before>// +build linux

package disk

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

// Get disk I/O statistics
func Get() ([]DiskStats, error) {
	file, err := os.Open("/proc/diskstats")
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return collectDiskStats(file)
}

// DiskStats represents disk I/O statistics for linux
type DiskStats struct {
	Name                            string
	ReadsCompleted, WritesCompleted uint64
}

func collectDiskStats(out io.Reader) ([]DiskStats, error) {
	scanner := bufio.NewScanner(out)
	var diskStats []DiskStats
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) < 14 {
			continue
		}
		// Reference: Documentation/iostats.txt in the source of Linux
		name := fields[2]
		readsCompleted, err := strconv.ParseUint(fields[3], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("failed to parse reads completed of %s", name)
		}
		writesCompleted, err := strconv.ParseUint(fields[7], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("failed to parse writes completed of %s", name)
		}
		diskStats = append(diskStats, DiskStats{
			Name:            name,
			ReadsCompleted:  readsCompleted,
			WritesCompleted: writesCompleted,
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("scan error for /proc/net/dev: %s", err)
	}
	return diskStats, nil
}
<commit_msg>fix error message for disk statistics on linux<commit_after>// +build linux

package disk

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
)

// Get disk I/O statistics
func Get() ([]DiskStats, error) {
	file, err := os.Open("/proc/diskstats")
	if err != nil {
		return nil, err
	}
	defer file.Close()
	return collectDiskStats(file)
}

// DiskStats represents disk I/O statistics for linux
type DiskStats struct {
	Name                            string
	ReadsCompleted, WritesCompleted uint64
}

func collectDiskStats(out io.Reader) ([]DiskStats, error) {
	scanner := bufio.NewScanner(out)
	var diskStats []DiskStats
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) < 14 {
			continue
		}
		// Reference: Documentation/iostats.txt in the source of Linux
		name := fields[2]
		readsCompleted, err := strconv.ParseUint(fields[3], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("failed to parse reads completed of %s", name)
		}
		writesCompleted, err := strconv.ParseUint(fields[7], 10, 64)
		if err != nil {
			return nil, fmt.Errorf("failed to parse writes completed of %s", name)
		}
		diskStats = append(diskStats, DiskStats{
			Name:            name,
			ReadsCompleted:  readsCompleted,
			WritesCompleted: writesCompleted,
		})
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("scan error for /proc/diskstats: %s", err)
	}
	return diskStats, nil
}
<|endoftext|>


<commit_before>package i2c

import (
	"bytes"
	"errors"
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Driver = (*BME280Driver)(nil)

// --------- HELPERS
func initTestBME280Driver() (driver *BME280Driver) {
	driver, _ = initTestBME280DriverWithStubbedAdaptor()
	return
}

func initTestBME280DriverWithStubbedAdaptor() (*BME280Driver, *i2cTestAdaptor) {
	adaptor := newI2cTestAdaptor()
	return NewBME280Driver(adaptor), adaptor
}

// --------- TESTS

func TestNewBME280Driver(t *testing.T) {
	// Does it return a pointer to an instance of BME280Driver?
	var bme280 interface{} = NewBME280Driver(newI2cTestAdaptor())
	_, ok := bme280.(*BME280Driver)
	if !ok {
		t.Errorf("NewBME280Driver() should have returned a *BME280Driver")
	}
}

func TestBME280Driver(t *testing.T) {
	bme280 := initTestBME280Driver()
	gobottest.Refute(t, bme280.Connection(), nil)
}

func TestBME280DriverStart(t *testing.T) {
	bme280, _ := initTestBME280DriverWithStubbedAdaptor()
	gobottest.Assert(t, bme280.Start(), nil)
}

func TestBME280DriverStartWriteError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cWriteImpl = func([]byte) (int, error) {
		return 0, errors.New("write error")
	}
	gobottest.Assert(t, bme280.Start(), errors.New("write error"))
}

func TestBME280DriverStartReadError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		return 0, errors.New("read error")
	}
	gobottest.Assert(t, bme280.Start(), errors.New("read error"))
}

func TestBME280DriverHalt(t *testing.T) {
	bme280 := initTestBME280Driver()

	gobottest.Assert(t, bme280.Halt(), nil)
}

func TestBME280DriverMeasurements(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			buf.Write([]byte{75})
		} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
			buf.Write([]byte{129, 0, 0})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterHumidityMSB {
			buf.Write([]byte{111, 83})
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}
	bme280.Start()
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, nil)
	gobottest.Assert(t, hum, float32(51.20179))
}

func TestBME280DriverInitH1Error(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			return 0, errors.New("h1 read error")
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}

	gobottest.Assert(t, bme280.Start(), errors.New("h1 read error"))
}

func TestBME280DriverInitH2Error(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			buf.Write([]byte{75})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			return 0, errors.New("h2 read error")
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}

	gobottest.Assert(t, bme280.Start(), errors.New("h2 read error"))
}

func TestBME280DriverHumidityWriteError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	bme280.Start()

	adaptor.i2cWriteImpl = func([]byte) (int, error) {
		return 0, errors.New("write error")
	}
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, errors.New("write error"))
	gobottest.Assert(t, hum, float32(0.0))
}

func TestBME280DriverHumidityReadError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	bme280.Start()

	adaptor.i2cReadImpl = func([]byte) (int, error) {
		return 0, errors.New("read error")
	}
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, errors.New("read error"))
	gobottest.Assert(t, hum, float32(0.0))
}

func TestBME280DriverSetName(t *testing.T) {
	b := initTestBME280Driver()
	b.SetName("TESTME")
	gobottest.Assert(t, b.Name(), "TESTME")
}

func TestBME280DriverOptions(t *testing.T) {
	b := NewBME280Driver(newI2cTestAdaptor(), WithBus(2))
	gobottest.Assert(t, b.GetBusOrDefault(1), 2)
}
<commit_msg>i2c: increase test coverage for bme280<commit_after>package i2c

import (
	"bytes"
	"errors"
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Driver = (*BME280Driver)(nil)

// --------- HELPERS
func initTestBME280Driver() (driver *BME280Driver) {
	driver, _ = initTestBME280DriverWithStubbedAdaptor()
	return
}

func initTestBME280DriverWithStubbedAdaptor() (*BME280Driver, *i2cTestAdaptor) {
	adaptor := newI2cTestAdaptor()
	return NewBME280Driver(adaptor), adaptor
}

// --------- TESTS

func TestNewBME280Driver(t *testing.T) {
	// Does it return a pointer to an instance of BME280Driver?
	var bme280 interface{} = NewBME280Driver(newI2cTestAdaptor())
	_, ok := bme280.(*BME280Driver)
	if !ok {
		t.Errorf("NewBME280Driver() should have returned a *BME280Driver")
	}
}

func TestBME280Driver(t *testing.T) {
	bme280 := initTestBME280Driver()
	gobottest.Refute(t, bme280.Connection(), nil)
}

func TestBME280DriverStart(t *testing.T) {
	bme280, _ := initTestBME280DriverWithStubbedAdaptor()
	gobottest.Assert(t, bme280.Start(), nil)
}

func TestBME280DriverStartWriteError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cWriteImpl = func([]byte) (int, error) {
		return 0, errors.New("write error")
	}
	gobottest.Assert(t, bme280.Start(), errors.New("write error"))
}

func TestBME280DriverStartReadError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		return 0, errors.New("read error")
	}
	gobottest.Assert(t, bme280.Start(), errors.New("read error"))
}

func TestBME280DriverHalt(t *testing.T) {
	bme280 := initTestBME280Driver()

	gobottest.Assert(t, bme280.Halt(), nil)
}

func TestBME280DriverMeasurements(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			buf.Write([]byte{75})
		} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
			buf.Write([]byte{129, 0, 0})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterHumidityMSB {
			buf.Write([]byte{111, 83})
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}
	bme280.Start()
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, nil)
	gobottest.Assert(t, hum, float32(51.20179))
}

func TestBME280DriverInitH1Error(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			return 0, errors.New("h1 read error")
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}

	gobottest.Assert(t, bme280.Start(), errors.New("h1 read error"))
}

func TestBME280DriverInitH2Error(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			buf.Write([]byte{75})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			return 0, errors.New("h2 read error")
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}

	gobottest.Assert(t, bme280.Start(), errors.New("h2 read error"))
}

func TestBME280DriverHumidityWriteError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	bme280.Start()

	adaptor.i2cWriteImpl = func([]byte) (int, error) {
		return 0, errors.New("write error")
	}
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, errors.New("write error"))
	gobottest.Assert(t, hum, float32(0.0))
}

func TestBME280DriverHumidityReadError(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	bme280.Start()

	adaptor.i2cReadImpl = func([]byte) (int, error) {
		return 0, errors.New("read error")
	}
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, errors.New("read error"))
	gobottest.Assert(t, hum, float32(0.0))
}

func TestBME280DriverHumidityNotEnabled(t *testing.T) {
	bme280, adaptor := initTestBME280DriverWithStubbedAdaptor()
	adaptor.i2cReadImpl = func(b []byte) (int, error) {
		buf := new(bytes.Buffer)
		// Values produced by dumping data from actual sensor
		if adaptor.written[len(adaptor.written)-1] == bmp280RegisterCalib00 {
			buf.Write([]byte{126, 109, 214, 102, 50, 0, 54, 149, 220, 213, 208, 11, 64, 30, 166, 255, 249, 255, 172, 38, 10, 216, 189, 16})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH1 {
			buf.Write([]byte{75})
		} else if adaptor.written[len(adaptor.written)-1] == bmp280RegisterTempData {
			buf.Write([]byte{129, 0, 0})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterCalibDigH2LSB {
			buf.Write([]byte{112, 1, 0, 19, 1, 0, 30})
		} else if adaptor.written[len(adaptor.written)-1] == bme280RegisterHumidityMSB {
			buf.Write([]byte{0x80, 0x00})
		}
		copy(b, buf.Bytes())
		return buf.Len(), nil
	}
	bme280.Start()
	hum, err := bme280.Humidity()
	gobottest.Assert(t, err, errors.New("Humidity disabled"))
	gobottest.Assert(t, hum, float32(0.0))
}

func TestBME280DriverSetName(t *testing.T) {
	b := initTestBME280Driver()
	b.SetName("TESTME")
	gobottest.Assert(t, b.Name(), "TESTME")
}

func TestBME280DriverOptions(t *testing.T) {
	b := NewBME280Driver(newI2cTestAdaptor(), WithBus(2))
	gobottest.Assert(t, b.GetBusOrDefault(1), 2)
}
<|endoftext|>