<commit_before>package tests

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

// TestNewContract tries to creates new contracts on the platform.
//
// CLASSIC SCENARIO
// 1. client1 registers on the platform
// 2. client1 sends a new contract on the platform, but client2 is not here yet
// 3. client2 registers on the platform
// 4. client2 sends a new contract on the platform, and everyone is here
//
// BAD CASES
// 1. client2 sends a new contract with a wrong password
// 2. client3 sends a new contract without authentication
// 3. client1 sends a new contract with an invalid filepath
func TestNewContract(t *testing.T) {
	// Cleanup
	eraseDatabase()

	// Start the platform
	workingDir, err := ioutil.TempDir("", "dfss_")
	assert.Equal(t, nil, err)
	_, _, _, stop, ca, err := startPlatform(workingDir)
	assert.Equal(t, nil, err)
	defer stop()

	time.Sleep(2 * time.Second)

	// Register client1
	client1, err := createClient(workingDir, ca, 0)
	assert.Equal(t, nil, err)

	err = registerAndAuth(client1, "client1@example.com", "password", "", true, true)
	assert.Equal(t, nil, err)

	// Create contract
	client1 = newClient(client1)
	setLastArg(client1, "new", true)
	client1.Stdin = strings.NewReader(
		"password\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"A very nice comment\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client1, "Operation succeeded with a warning message: Some users are not ready yet\n")
	assert.NotEqual(t, nil, err)

	// Check database
	contract := getContract("contract.txt", 0)
	assert.Equal(t, false, contract.Ready)
	assert.Equal(t, "A very nice comment", contract.Comment)
	assert.Equal(t, "6a95f6bcd6282186a7b1175fbaab4809ca5f665f7c4d55675de2399c83e67252069d741a88c766b1a79206d6dfbd5552cd7f9bc69b43bee161d1337228b4a4a8", fmt.Sprintf("%x", contract.File.Hash))
	assert.Equal(t, 2, len(contract.Signers))
	assert.Equal(t, "client1@example.com", contract.Signers[0].Email)
	assert.Equal(t, "client2@example.com", contract.Signers[1].Email)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) == 0)

	// Register second signer
	client2, err := createClient(workingDir, ca, 0)
	assert.Equal(t, nil, err)
	err = registerAndAuth(client2, "client2@example.com", "password2", "", true, true)
	assert.Equal(t, nil, err)

	// Check database²
	contract = getContract("contract.txt", 0)
	assert.Equal(t, true, contract.Ready)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) > 0)

	// Create a second contract
	client2 = newClient(client2)
	setLastArg(client2, "new", true)
	client2.Stdin = strings.NewReader(
		"password2\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"Another comment with some accents héhé\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client2, "")
	assert.Equal(t, nil, err)

	// Check database³
	contract = getContract("contract.txt", 1)
	assert.Equal(t, true, contract.Ready)
	assert.Equal(t, "Another comment with some accents héhé", contract.Comment)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) > 0)

	// Bad case: wrong password
	client2 = newClient(client2)
	setLastArg(client2, "new", true)
	client2.Stdin = strings.NewReader(
		"wrongPwd\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client2, "x509: decryption password incorrect\n")
	assert.NotEqual(t, nil, err)

	// Bad case: no authentication
	client3, err := createClient(workingDir, ca, 0)
	setLastArg(client3, "new", false)
	client3.Stdin = strings.NewReader(
		"\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"\n" +
			"client1@example.com\n" +
			"\n",
	)
	err = client3.Run()
	assert.NotEqual(t, nil, err)

	// Bad case: bad filepath
	client1 = newClient(client1)
	setLastArg(client1, "new", true)
	client1.Stdin = strings.NewReader(
		"password\n" +
			"invalidFile\n" +
			"client1@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client1, "open invalidFile: no such file or directory\n")
	assert.NotEqual(t, nil, err)

	// Check number of stored contracts
	assert.Equal(t, 2, dbManager.Get("contracts").Count())
}
<commit_msg>[tests] Fix random fail in integration tests<commit_after>package tests

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

// TestNewContract tries to creates new contracts on the platform.
//
// CLASSIC SCENARIO
// 1. client1 registers on the platform
// 2. client1 sends a new contract on the platform, but client2 is not here yet
// 3. client2 registers on the platform
// 4. client2 sends a new contract on the platform, and everyone is here
//
// BAD CASES
// 1. client2 sends a new contract with a wrong password
// 2. client3 sends a new contract without authentication
// 3. client1 sends a new contract with an invalid filepath
func TestNewContract(t *testing.T) {
	// Cleanup
	eraseDatabase()

	// Start the platform
	workingDir, err := ioutil.TempDir("", "dfss_")
	assert.Equal(t, nil, err)
	_, _, _, stop, ca, err := startPlatform(workingDir)
	assert.Equal(t, nil, err)
	defer stop()

	time.Sleep(2 * time.Second)

	// Register client1
	client1, err := createClient(workingDir, ca, 0)
	assert.Equal(t, nil, err)

	err = registerAndAuth(client1, "client1@example.com", "password", "", true, true)
	assert.Equal(t, nil, err)

	// Create contract
	client1 = newClient(client1)
	setLastArg(client1, "new", true)
	client1.Stdin = strings.NewReader(
		"password\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"A very nice comment\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client1, "Operation succeeded with a warning message: Some users are not ready yet\n")
	assert.NotEqual(t, nil, err)

	// Check database
	contract := getContract("contract.txt", 0)
	assert.Equal(t, false, contract.Ready)
	assert.Equal(t, "A very nice comment", contract.Comment)
	assert.Equal(t, "6a95f6bcd6282186a7b1175fbaab4809ca5f665f7c4d55675de2399c83e67252069d741a88c766b1a79206d6dfbd5552cd7f9bc69b43bee161d1337228b4a4a8", fmt.Sprintf("%x", contract.File.Hash))
	assert.Equal(t, 2, len(contract.Signers))
	assert.Equal(t, "client1@example.com", contract.Signers[0].Email)
	assert.Equal(t, "client2@example.com", contract.Signers[1].Email)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) == 0)

	// Register second signer
	client2, err := createClient(workingDir, ca, 0)
	assert.Equal(t, nil, err)
	err = registerAndAuth(client2, "client2@example.com", "password2", "", true, true)
	assert.Equal(t, nil, err)

	// Check database²
	time.Sleep(time.Second) // Allowed delay to let some time to propagate the contract readiness
	contract = getContract("contract.txt", 0)
	assert.Equal(t, true, contract.Ready)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) > 0)

	// Create a second contract
	client2 = newClient(client2)
	setLastArg(client2, "new", true)
	client2.Stdin = strings.NewReader(
		"password2\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"Another comment with some accents héhé\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client2, "")
	assert.Equal(t, nil, err)

	// Check database³
	contract = getContract("contract.txt", 1)
	assert.Equal(t, true, contract.Ready)
	assert.Equal(t, "Another comment with some accents héhé", contract.Comment)
	assert.True(t, len(contract.Signers[0].Hash) > 0)
	assert.True(t, len(contract.Signers[1].Hash) > 0)

	// Bad case: wrong password
	client2 = newClient(client2)
	setLastArg(client2, "new", true)
	client2.Stdin = strings.NewReader(
		"wrongPwd\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"\n" +
			"client1@example.com\n" +
			"client2@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client2, "x509: decryption password incorrect\n")
	assert.NotEqual(t, nil, err)

	// Bad case: no authentication
	client3, err := createClient(workingDir, ca, 0)
	setLastArg(client3, "new", false)
	client3.Stdin = strings.NewReader(
		"\n" +
			filepath.Join("testdata", "contract.txt") + "\n" +
			"\n" +
			"client1@example.com\n" +
			"\n",
	)
	err = client3.Run()
	assert.NotEqual(t, nil, err)

	// Bad case: bad filepath
	client1 = newClient(client1)
	setLastArg(client1, "new", true)
	client1.Stdin = strings.NewReader(
		"password\n" +
			"invalidFile\n" +
			"client1@example.com\n" +
			"\n",
	)
	err = checkStderr(t, client1, "open invalidFile: no such file or directory\n")
	assert.NotEqual(t, nil, err)

	// Check number of stored contracts
	assert.Equal(t, 2, dbManager.Get("contracts").Count())
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"path/filepath"
	"strings"

	"github.com/cryptix/git-remote-ipfs/Godeps/_workspace/src/github.com/ipfs/go-ipfs-shell"
	"github.com/cryptix/git-remote-ipfs/Godeps/_workspace/src/gopkg.in/errgo.v1"
)

func listInfoRefs(forPush bool) error {
	refsCat, err := ipfsShell.Cat(filepath.Join(ipfsRepoPath, "info", "refs"))
	if err != nil {
		return errgo.Notef(err, "failed to cat info/refs from %s", ipfsRepoPath)
	}
	s := bufio.NewScanner(refsCat)
	for s.Scan() {
		hashRef := strings.Split(s.Text(), "\t")
		if len(hashRef) != 2 {
			return errgo.Newf("processing info/refs: what is this: %v", hashRef)
		}
		ref2hash[hashRef[1]] = hashRef[0]
		log.WithField("ref", hashRef[1]).WithField("sha1", hashRef[0]).Debug("got ref")
	}
	if err := s.Err(); err != nil {
		return errgo.Notef(err, "ipfs.Cat(info/refs) scanner error")
	}
	return nil
}

func listHeadRef() (string, error) {
	headCat, err := ipfsShell.Cat(filepath.Join(ipfsRepoPath, "HEAD"))
	if err != nil {
		return "", errgo.Notef(err, "failed to cat HEAD from %s", ipfsRepoPath)
	}
	head, err := ioutil.ReadAll(headCat)
	if err != nil {
		return "", errgo.Notef(err, "failed to readAll HEAD from %s", ipfsRepoPath)
	}
	if !bytes.HasPrefix(head, []byte("ref: ")) {
		return "", errgo.Newf("illegal HEAD file from %s: %q", ipfsRepoPath, head)
	}
	headRef := string(bytes.TrimSpace(head[5:]))
	headHash, ok := ref2hash[headRef]
	if !ok {
		// use first hash in map?..
		return "", errgo.Newf("unknown HEAD reference %q", headRef)
	}
	log.WithField("ref", headRef).WithField("sha1", headHash).Debug("got HEAD ref")
	return headHash, headCat.Close()
}

func listIterateRefs(forPush bool) error {
	refsDir := filepath.Join(ipfsRepoPath, "refs")
	return Walk(refsDir, func(p string, info *shell.LsLink, err error) error {
		if err != nil {
			return errgo.Notef(err, "walk(%s) failed", p)
		}
		log.WithField("info", info).Debug("iterateRefs: walked to:", p)
		if info.Type == 2 {
			rc, err := ipfsShell.Cat(p)
			if err != nil {
				return errgo.Notef(err, "walk(%s) cat ref failed", p)
			}
			data, err := ioutil.ReadAll(rc)
			if err != nil {
				return errgo.Notef(err, "walk(%s) readAll failed", p)
			}
			if err := rc.Close(); err != nil {
				return errgo.Notef(err, "walk(%s) cat close failed", p)
			}
			sha1 := strings.TrimSpace(string(data))
			refName := strings.TrimPrefix(p, ipfsRepoPath)
			ref2hash[refName] = sha1
			log.WithField("refMap", ref2hash).Debug("ref2hash map updated")
		}
		return nil
	})
}

// semi-todo make shell implement http.FileSystem
// then we can reuse filepath.Walk and make a lot of other stuff simpler
var SkipDir = errgo.Newf("walk: skipping")

type WalkFunc func(path string, info *shell.LsLink, err error) error

func walk(path string, info *shell.LsLink, walkFn WalkFunc) error {
	err := walkFn(path, info, nil)
	if err != nil {
		if info.Type == 1 && err == SkipDir {
			return nil
		}
		return err
	}
	if info.Type != 1 {
		return nil
	}
	list, err := ipfsShell.List(path)
	if err != nil {
		log.Error("walk list failed", err)
		return walkFn(path, info, err)
	}
	for _, lnk := range list {
		fname := filepath.Join(path, lnk.Name)
		err = walk(fname, lnk, walkFn)
		if err != nil {
			if lnk.Type != 1 || err != SkipDir {
				return err
			}
		}
	}
	return nil
}

func Walk(root string, walkFn WalkFunc) error {
	list, err := ipfsShell.List(root)
	if err != nil {
		log.Error("walk root failed", err)
		return walkFn(root, nil, err)
	}
	for _, l := range list {
		fname := filepath.Join(root, l.Name)
		if err := walk(fname, l, walkFn); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>use patched version of ipfs-shell List()<commit_after>package main

import (
	"bufio"
	"bytes"
	"io/ioutil"
	"path/filepath"
	"strings"

	"github.com/cryptix/git-remote-ipfs/Godeps/_workspace/src/github.com/ipfs/go-ipfs-shell"
	"github.com/cryptix/git-remote-ipfs/Godeps/_workspace/src/gopkg.in/errgo.v1"
)

func listInfoRefs(forPush bool) error {
	refsCat, err := ipfsShell.Cat(filepath.Join(ipfsRepoPath, "info", "refs"))
	if err != nil {
		return errgo.Notef(err, "failed to cat info/refs from %s", ipfsRepoPath)
	}
	s := bufio.NewScanner(refsCat)
	for s.Scan() {
		hashRef := strings.Split(s.Text(), "\t")
		if len(hashRef) != 2 {
			return errgo.Newf("processing info/refs: what is this: %v", hashRef)
		}
		ref2hash[hashRef[1]] = hashRef[0]
		log.WithField("ref", hashRef[1]).WithField("sha1", hashRef[0]).Debug("got ref")
	}
	if err := s.Err(); err != nil {
		return errgo.Notef(err, "ipfs.Cat(info/refs) scanner error")
	}
	return nil
}

func listHeadRef() (string, error) {
	headCat, err := ipfsShell.Cat(filepath.Join(ipfsRepoPath, "HEAD"))
	if err != nil {
		return "", errgo.Notef(err, "failed to cat HEAD from %s", ipfsRepoPath)
	}
	head, err := ioutil.ReadAll(headCat)
	if err != nil {
		return "", errgo.Notef(err, "failed to readAll HEAD from %s", ipfsRepoPath)
	}
	if !bytes.HasPrefix(head, []byte("ref: ")) {
		return "", errgo.Newf("illegal HEAD file from %s: %q", ipfsRepoPath, head)
	}
	headRef := string(bytes.TrimSpace(head[5:]))
	headHash, ok := ref2hash[headRef]
	if !ok {
		// use first hash in map?..
		return "", errgo.Newf("unknown HEAD reference %q", headRef)
	}
	log.WithField("ref", headRef).WithField("sha1", headHash).Debug("got HEAD ref")
	return headHash, headCat.Close()
}

func listIterateRefs(forPush bool) error {
	refsDir := filepath.Join(ipfsRepoPath, "refs")
	return Walk(refsDir, func(p string, info *shell.LsEntry, err error) error {
		if err != nil {
			return errgo.Notef(err, "walk(%s) failed", p)
		}
		log.WithField("info", info).Debug("iterateRefs: walked to:", p)
		if info.Type == 2 {
			rc, err := ipfsShell.Cat(p)
			if err != nil {
				return errgo.Notef(err, "walk(%s) cat ref failed", p)
			}
			data, err := ioutil.ReadAll(rc)
			if err != nil {
				return errgo.Notef(err, "walk(%s) readAll failed", p)
			}
			if err := rc.Close(); err != nil {
				return errgo.Notef(err, "walk(%s) cat close failed", p)
			}
			sha1 := strings.TrimSpace(string(data))
			refName := strings.TrimPrefix(p, ipfsRepoPath)
			ref2hash[refName] = sha1
			log.WithField("refMap", ref2hash).Debug("ref2hash map updated")
		}
		return nil
	})
}

// semi-todo make shell implement http.FileSystem
// then we can reuse filepath.Walk and make a lot of other stuff simpler
var SkipDir = errgo.Newf("walk: skipping")

type WalkFunc func(path string, info *shell.LsEntry, err error) error

func walk(path string, info *shell.LsEntry, walkFn WalkFunc) error {
	err := walkFn(path, info, nil)
	if err != nil {
		if info.Type == 1 && err == SkipDir {
			return nil
		}
		return err
	}
	if info.Type != 1 {
		return nil
	}
	list, err := ipfsShell.List(path)
	if err != nil {
		log.Error("walk list failed", err)
		return walkFn(path, info, err)
	}
	for _, lnk := range list {
		fname := filepath.Join(path, lnk.Name)
		err = walk(fname, lnk, walkFn)
		if err != nil {
			if lnk.Type != 1 || err != SkipDir {
				return err
			}
		}
	}
	return nil
}

func Walk(root string, walkFn WalkFunc) error {
	list, err := ipfsShell.List(root)
	if err != nil {
		log.Error("walk root failed", err)
		return walkFn(root, nil, err)
	}
	for _, l := range list {
		fname := filepath.Join(root, l.Name)
		if err := walk(fname, l, walkFn); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package OSXKeyboard

/*
#cgo LDFLAGS: -framework ApplicationServices -framework Carbon
extern int listen();
*/
import "C"

import (
  "errors"
  "github.com/GianlucaGuarini/go-observable"
)

// flag variable to check whether the c code was already called
var isListening = false
var o = observable.New()

// Listen start listening the keyboard events
func Listen() {
  // start the c code listeners to call the go function hook
  if isListening == false {
    ok := C.listen()

    if ok == 1 {
      err := errors.New("It was not possible to listen the global keyboard events, make sure to call the progam with 'sudo'")
      panic(err)
    }

    // change the flag to true to avoid entering this condition again
    isListening = true
  }
}

// Subscribe a callback to the "keypress" events
func Subscribe(fn interface{}) {
  o.On("keypress", fn)
}

// Unsubscribe a function to the "keypress" events
func Unsubscribe(fn interface{}) {
  o.Off("keypress", fn)
}

// GoKeypressCallback hook function called in the c code
//export GoKeypressCallback
func GoKeypressCallback(key *C.char) {
  // get the letter just received
  letter := C.GoString(key)
  o.Trigger("keypress", letter)
}
<commit_msg>removed: useless frameworks<commit_after>package OSXKeyboard

/*
#cgo LDFLAGS: -framework Carbon
extern int listen();
*/
import "C"

import (
  "errors"
  "github.com/GianlucaGuarini/go-observable"
)

// flag variable to check whether the c code was already called
var isListening = false
var o = observable.New()

// Listen start listening the keyboard events
func Listen() {
  // start the c code listeners to call the go function hook
  if isListening == false {
    ok := C.listen()

    if ok == 1 {
      err := errors.New("It was not possible to listen the global keyboard events, make sure to call the progam with 'sudo'")
      panic(err)
    }

    // change the flag to true to avoid entering this condition again
    isListening = true
  }
}

// Subscribe a callback to the "keypress" events
func Subscribe(fn interface{}) {
  o.On("keypress", fn)
}

// Unsubscribe a function to the "keypress" events
func Unsubscribe(fn interface{}) {
  o.Off("keypress", fn)
}

// GoKeypressCallback hook function called in the c code
//export GoKeypressCallback
func GoKeypressCallback(key *C.char) {
  // get the letter just received
  letter := C.GoString(key)
  o.Trigger("keypress", letter)
}
<|endoftext|>


<commit_before>package sstable

import (
	"encoding/binary"
	"io"
)

func (t *SSTable) load() error {
	var pos int64
	data := make([]byte, 4+len(magic))
	n, err := t.f.ReadAt(data, 0)
	if err != nil {
		return err
	}
	if n < len(data) {
		return io.ErrUnexpectedEOF
	}
	pos += int64(n)
	if string(data[0:len(magic)]) != magic {
		return ErrBadFormat
	}
	count := binary.BigEndian.Uint32(data[len(magic):])
	r := make([]record, count)
	for i := uint32(0); i < count; i++ {
		data = make([]byte, 9)
		n, err = t.f.ReadAt(data, pos)
		if err != nil {
			return err
		}
		if n < len(data) {
			return io.ErrUnexpectedEOF
		}
		pos += int64(n)
		r[i].length = binary.BigEndian.Uint32(data[0:4])
		r[i].offset = binary.BigEndian.Uint32(data[4:8])
		key := make([]byte, data[8])
		n, err = t.f.ReadAt(key, pos)
		if err != nil {
			return err
		}
		if n < len(key) {
			return io.ErrUnexpectedEOF
		}
		pos += int64(n)
		r[i].key = string(key)
	}
	t.r = r
	return nil
}
<commit_msg>Factor out fullReadAt.<commit_after>package sstable

import (
	"encoding/binary"
	"io"
)

func fullReadAt(f io.ReaderAt, data []byte, offset int64) error {
	n, err := f.ReadAt(data, offset)
	if err != nil {
		return err
	}
	if n < len(data) {
		return io.ErrUnexpectedEOF
	}
	return nil
}

func (t *SSTable) load() error {
	data := make([]byte, 4+len(magic))
	if err := fullReadAt(t.f, data, 0); err != nil {
		return err
	}
	pos := int64(len(data))

	if string(data[0:len(magic)]) != magic {
		return ErrBadFormat
	}
	count := binary.BigEndian.Uint32(data[len(magic):])
	r := make([]record, count)
	for i := uint32(0); i < count; i++ {
		data = make([]byte, 9)
		if err := fullReadAt(t.f, data, pos); err != nil {
			return err
		}
		pos += int64(len(data))
		r[i].length = binary.BigEndian.Uint32(data[0:4])
		r[i].offset = binary.BigEndian.Uint32(data[4:8])
		key := make([]byte, data[8])
		if err := fullReadAt(t.f, key, pos); err != nil {
			return err
		}
		pos += int64(len(key))
		r[i].key = string(key)
	}
	t.r = r
	return nil
}
<|endoftext|>


<commit_before>package main

// #cgo LDFLAGS: -lm
// #include "tclled.h"
import "C"
import "fmt"
import "time"

func setColor(buffer *_Ctype_tcl_buffer, led_number int, r int, g int, b int) {
	C.write_gamma_color_to_buffer(buffer, C.int(led_number), C.uint8_t(r), C.uint8_t(g), C.uint8_t(b))
}

func sendBuffer(device C.int, buffer *_Ctype_tcl_buffer) {
	C.send_buffer(device, buffer)
}

func main() {
	device := C.open_device()
	fmt.Print("Device status: ")
	fmt.Println(device)

	if device <= 0 {
		fmt.Println("Device init failed.")
		return
	}

	C.set_gamma(2.2, 2.2, 2.2)

	spi_status := C.spi_init(device)
	fmt.Print("SPI status: ")
	fmt.Println(spi_status)

	if spi_status != 0 {
		fmt.Println("SPI init failed.")
		return
	}

	buffer := &C.tcl_buffer{}
	tcl_status := C.tcl_init(buffer, 30)
	fmt.Print("TCL status: ")
	fmt.Println(tcl_status)

	if tcl_status != 0 {
		fmt.Println("TCL init failed.")
		return
	}

	color := 0
	goingDown := false
	for true {
		if goingDown {
			color--
		} else {
			color++
		}
		if color < 0 {
			goingDown = false
			color = 0
		} else if color > 255 {
			goingDown = true
			color = 255
		}

		for i := 0; i < 30; i++ {
			setColor(buffer, i, color, color, color)
		}
		sendBuffer(device, buffer)
		time.Sleep(10 * time.Millisecond)
	}
}
<commit_msg>Reorg strand code into struct<commit_after>package main

// #cgo LDFLAGS: -lm
// #include "tclled.h"
import "C"
import "fmt"
import "time"
import "errors"

// import "time"

type Strand struct {
	device   C.int
	buffer   *_Ctype_tcl_buffer
	ledCount int
}

func (s *Strand) Connect(ledCount int) error {
	s.ledCount = ledCount
	s.device = C.open_device()

	if s.device <= 0 {
		return errors.New("Device init failed")
	}

	C.set_gamma(2.2, 2.2, 2.2)
	spiStatus := C.spi_init(s.device)
	if spiStatus != 0 {
		return errors.New("SPI init failed")
	}

	s.buffer = &C.tcl_buffer{}
	tclStatus := C.tcl_init(s.buffer, C.int(s.ledCount))
	if tclStatus != 0 {
		return errors.New("TCL init failed")
	}

	return nil
}

func (s *Strand) SetColor(ledNumber int, r int, g int, b int) {
	C.write_gamma_color_to_buffer(s.buffer, C.int(ledNumber), C.uint8_t(r), C.uint8_t(g), C.uint8_t(b))
}

func (s *Strand) Save() {
	C.send_buffer(s.device, s.buffer)
}

func main() {
	strand := Strand{}
	ledCount := 30
	err := strand.Connect(ledCount)

	if err != nil {
		fmt.Println("Error:")
		fmt.Println(err)
	}

	color := 0
	goingDown := false
	for true {
	for j := 0; j < 1000; j++ {
		if goingDown {
			color--
		} else {
			color++
		}
		if color < 0 {
			goingDown = false
			color = 0
		} else if color > 255 {
			goingDown = true
			color = 255
		}

		for i := 0; i < ledCount; i++ {
			strand.SetColor(i, color, color, color)
		}
		strand.Save()
		time.Sleep(10 * time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	host       = "http://api.mixpanel.com"
	trackPath  = "track"
	engagePath = "engage"
)

// engage constants
const (
	EngageSet = "set"
	EngageSetOnce = "set_once"
	EngageAdd = "add"
	EngageAppend = "append"
	EngageUnion = "union"
	EngageUnset = "unset"
	EngageDelete = "delete"
)

type client struct {
	token string
}

type eventData struct {
	Event string                 `json:"event"`
	Props map[string]interface{} `json:"properties"`
}

type engageData struct {
	Token   string      `json:"$token"`
	Time    int64       `json:"$time"`
	Id      int64       `json:"$distinct_id"`
	Ip      string      `json:"$ip,omitempty"`
	Set     interface{} `json:"$set,omitempty"`
	SetOnce interface{} `json:"$set_once,omitempty"`
	Add     interface{} `json:"$add,omitempty"`
	Append  interface{} `json:"$append,omitempty"`
	Union   interface{} `json:"$union,omitempty"`
	Unset   interface{} `json:"$unset,omitempty"`
	Delete  interface{} `json:"$delete,omitempty"`
}

func New(token string) *client {
	return &client{
		token: token,
	}
}

func (mp *client) Track(uid int64, e string, p map[string]interface{}, params ...map[string]interface{}) bool {
	data := &eventData{
		Event: e,
		Props: map[string]interface{}{
			"time":  time.Now().Unix(),
			"token": mp.token,
		},
	}
	if uid != 0 {
		data.Props["distinct_id"] = strconv.Itoa(int(uid))
	}
	for k, v := range p {
		data.Props[k] = v
	}

	marshaledData, err := json.Marshal(data)
	if err != nil {
		return false
	}

	u := fmt.Sprintf("%s/%s/?data=%s", host, trackPath,
		base64.StdEncoding.EncodeToString(marshaledData))

	parameters := url.Values{}
	// iterate over any query parameters
	for _, val := range params {
		for k, v := range val {
			if str, ok := v.(string); ok {
				/* act on str */
				parameters.Add(k, str)
			} else {
				/* not string - int? */
				if in, ok := v.(int); ok {
					parameters.Add(k, strconv.Itoa(in))
				} else {
					continue
				}
			}

		}
	}
	// append encoded params to url if any
	if qs := parameters.Encode(); qs != "" {
		u += "&" + qs
	}
	// send request
	_, err = http.Get(u)
	if err != nil {
		return false
	}
	return true
}

func (mp *client) Engage(uid int64, p map[string]interface{}, ip string) error {
	profileData := &engageData{
		Token: mp.token,
		Time:  time.Now().Unix(),
	}
	if uid != 0 {
		profileData.Id = uid
	}
	if ip != "" {
		profileData.Ip = ip
	}
	// should probably just add separate methods for each of these
	for k, v := range p {
		switch k {
		case EngageSet:
			profileData.Set = v
			break
		case EngageSetOnce:
			profileData.SetOnce = v
			break
		case EngageAdd:
			profileData.Add = v
			break
		case EngageAppend:
			profileData.Append = v
			break
		case EngageUnion:
			profileData.Union = v
			break
		case EngageUnset:
			profileData.Unset = v
			break
		case EngageDelete:
			profileData.Delete = v
			break
		}
	}

	marshalledData, err := json.Marshal(profileData)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s/%s/?data=%s", host, engagePath, base64.StdEncoding.EncodeToString(marshalledData))

	_, err = http.Get(url)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>revert revert<commit_after>package tracking

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	host       = "http://api.mixpanel.com"
	trackPath  = "track"
	engagePath = "engage"
)

// engage constants
const (
	EngageSet = "set"
	EngageSetOnce = "set_once"
	EngageAdd = "add"
	EngageAppend = "append"
	EngageUnion = "union"
	EngageUnset = "unset"
	EngageDelete = "delete"
)

type client struct {
	token string
}

type eventData struct {
	Event string                 `json:"event"`
	Props map[string]interface{} `json:"properties"`
}

type engageData struct {
	Token   string      `json:"$token"`
	Time    int64       `json:"$time"`
	Id      int64       `json:"$distinct_id"`
	Ip      string      `json:"$ip,omitempty"`
	Set     interface{} `json:"$set,omitempty"`
	SetOnce interface{} `json:"$set_once,omitempty"`
	Add     interface{} `json:"$add,omitempty"`
	Append  interface{} `json:"$append,omitempty"`
	Union   interface{} `json:"$union,omitempty"`
	Unset   interface{} `json:"$unset,omitempty"`
	Delete  interface{} `json:"$delete,omitempty"`
}

func New(token string) *client {
	return &client{
		token: token,
	}
}

func (mp *client) Track(uid int64, e string, p map[string]interface{}, params ...map[string]interface{}) bool {
	data := &eventData{
		Event: e,
		Props: map[string]interface{}{
			"time":  time.Now().Unix(),
			"token": mp.token,
		},
	}
	if uid != 0 {
		data.Props["distinct_id"] = strconv.Itoa(int(uid))
	}
	for k, v := range p {
		data.Props[k] = v
	}

	marshaledData, err := json.Marshal(data)
	if err != nil {
		return false
	}

	u := fmt.Sprintf("%s/%s/?data=%s", host, trackPath,
		base64.StdEncoding.EncodeToString(marshaledData))

	parameters := url.Values{}
	// iterate over any query parameters
	for _, val := range params {
		for k, v := range val {
			if str, ok := v.(string); ok {
				/* act on str */
				parameters.Add(k, str)
			} else {
				/* not string - int? */
				if in, ok := v.(int); ok {
					parameters.Add(k, strconv.Itoa(in))
				} else {
					continue
				}
			}

		}
	}
	// append encoded params to url if any
	if qs := parameters.Encode(); qs != "" {
		u += "&" + qs
	}
	// send request
	resp, err := http.Get(u)
	if err != nil {
		return false
	}
	resp.Body.Close()
	return true
}

func (mp *client) Engage(uid int64, p map[string]interface{}, ip string) error {
	profileData := &engageData{
		Token: mp.token,
		Time:  time.Now().Unix(),
	}
	if uid != 0 {
		profileData.Id = uid
	}
	if ip != "" {
		profileData.Ip = ip
	}
	// should probably just add separate methods for each of these
	for k, v := range p {
		switch k {
		case EngageSet:
			profileData.Set = v
			break
		case EngageSetOnce:
			profileData.SetOnce = v
			break
		case EngageAdd:
			profileData.Add = v
			break
		case EngageAppend:
			profileData.Append = v
			break
		case EngageUnion:
			profileData.Union = v
			break
		case EngageUnset:
			profileData.Unset = v
			break
		case EngageDelete:
			profileData.Delete = v
			break
		}
	}

	marshalledData, err := json.Marshal(profileData)
	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s/%s/?data=%s", host, engagePath, base64.StdEncoding.EncodeToString(marshalledData))

	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	resp.Body.Close()
	return nil
}
<|endoftext|>


<commit_before>package controllers

import (
	// "github.com/eaciit/crowd"
	// "github.com/eaciit/dbox"
	// . "github.com/eaciit/powerplant/sec/consoleapp/generator/helpers"
	. "github.com/eaciit/powerplant/sec/library/models"
	tk "github.com/eaciit/toolkit"
	// "strconv"
	"strings"
	// "time"
	"github.com/tealeg/xlsx"
	"os"
)

// REFunctionalLocation
type REFunctionalLocation struct {
	*BaseController
}

// Generate
func (d *REFunctionalLocation) Generate(base *BaseController) {
	var (
		folderName string = "Functional Location"
	)
	if base != nil {
		d.BaseController = base
	}
	dataSources, path := base.GetDataSource(folderName)
	tk.Println("Generating Functional Location from Excel File..")

	for _, source := range dataSources {
		if strings.Contains(source.Name(), "FLOC Structure") {
			file, e := xlsx.OpenFile(path + "\\" + source.Name())
			if e != nil {
				tk.Println(e)
				os.Exit(0)
			}
			sheet := file.Sheets[0]
			FLCodeColumn := 0
			for _, row := range sheet.Rows {
				d := new(FunctionalLocation)
				for i, cell := range row.Cells {
					if strings.Contains(strings.ToLower(cell.String()), "functional location") && len(strings.Replace(cell.String(), " ", "", -1)) < 20 {
						FLCodeColumn = i
						break
					}
				}
				d.FunctionalLocationCode = row.Cells[FLCodeColumn].String()
				tk.Println(d)
				// Process data for each row (including insert)
			}
		}
	}
}
<commit_msg>Functional Location Generator<commit_after>package controllers

import (
	// "github.com/eaciit/crowd"
	// "github.com/eaciit/dbox"
	// . "github.com/eaciit/powerplant/sec/consoleapp/generator/helpers"
	. "github.com/eaciit/powerplant/sec/library/models"
	tk "github.com/eaciit/toolkit"
	// "strconv"
	"github.com/tealeg/xlsx"
	"os"
	"strings"
	"time"
)

// REFunctionalLocation
type REFunctionalLocation struct {
	*BaseController
}

// Generate
func (d *REFunctionalLocation) Generate(base *BaseController) {
	var (
		folderName string   = "Functional Location"
		StrInds    []string = []string{"FMS", "DISTD", "DISTM", "TRNS", "GN-01", "GN-02", "GN-03", "GN-04"}
	)
	if base != nil {
		d.BaseController = base
	}
	ctx := d.BaseController.Ctx
	dataSources, path := base.GetDataSource(folderName)
	tk.Println("Generating Functional Location from Excel File..")
	for _, source := range dataSources {
		if strings.Contains(source.Name(), "FLOC Structure") {
			file, e := xlsx.OpenFile(path + "\\" + source.Name())
			if e != nil {
				tk.Println(e)
				os.Exit(0)
			}
			sheet := file.Sheets[0]

			for idx, row := range sheet.Rows {
				if strings.Trim(strings.ToLower(row.Cells[1].String()), " ") != "functional location" && strings.Trim(strings.ToLower(row.Cells[1].String()), " ") != "" {
					Str := row.Cells[2].String()
					Description := row.Cells[3].String()
					SupFunctionalLocation := row.Cells[25].String()
					if len(Description) <= 200 && SupFunctionalLocation != "" {
						isMatch := false
						for _, s := range StrInds {
							if strings.Contains(s, strings.Trim(Str, " ")) {
								isMatch = true
								break
							}
						}
						if isMatch {
							data := new(FunctionalLocation)
							data.FunctionalLocationCode = row.Cells[1].String()
							data.Str = row.Cells[2].String()
							data.Description = row.Cells[3].String()
							data.CostCtr = row.Cells[4].String()
							data.Location = row.Cells[5].String()
							data.PIPI = row.Cells[6].String()
							data.PInt = row.Cells[7].String()
							data.MainWorkCtr = row.Cells[8].String()
							data.CatProf = row.Cells[9].String()

							data.SortField = row.Cells[10].String()
							data.ModelNo = row.Cells[11].String()
							data.SerNo = row.Cells[12].String()
							data.UserStatus = row.Cells[13].String()
							data.A = row.Cells[14].String()

							data.ObjectType = row.Cells[15].String()
							data.PG = row.Cells[16].String()
							data.ManParNo = row.Cells[17].String()
							data.Asset = row.Cells[18].String()
							data.Date, _ = time.Parse(row.Cells[19].String(), "2013.01.02")
							data.AcqValue = row.Cells[20].String()
							data.InvNo = row.Cells[21].String()
							data.ConstType = row.Cells[22].String()
							data.StartFrom, _ = time.Parse(row.Cells[23].String(), "2013.01.02")
							data.CreatedOn, _ = time.Parse(row.Cells[24].String(), "2013.01.02")
							data.SupFunctionalLocation = row.Cells[25].String()
							_, e := ctx.InsertOut(data)
							if e != nil {
								tk.Println("ERR on file :", source.Name(), " | ROW :", idx)
								tk.Println(e)
							}
						}
					} else {
						data := new(AnomaliesFunctionalLocation)
						data.FunctionalLocationCode = row.Cells[1].String()
						data.Str = row.Cells[2].String()
						data.Description = row.Cells[3].String()
						data.CostCtr = row.Cells[4].String()
						data.Location = row.Cells[5].String()
						data.PIPI = row.Cells[6].String()
						data.PInt = row.Cells[7].String()
						data.MainWorkCtr = row.Cells[8].String()
						data.CatProf = row.Cells[9].String()

						data.SortField = row.Cells[10].String()
						data.ModelNo = row.Cells[11].String()
						data.SerNo = row.Cells[12].String()
						data.UserStatus = row.Cells[13].String()
						data.A = row.Cells[14].String()

						data.ObjectType = row.Cells[15].String()
						data.PG = row.Cells[16].String()
						data.ManParNo = row.Cells[17].String()
						data.Asset = row.Cells[18].String()
						data.Date, _ = time.Parse(row.Cells[19].String(), "2013.01.02")
						data.AcqValue = row.Cells[20].String()
						data.InvNo = row.Cells[21].String()
						data.ConstType = row.Cells[22].String()
						data.StartFrom, _ = time.Parse(row.Cells[23].String(), "2013.01.02")
						data.CreatedOn, _ = time.Parse(row.Cells[24].String(), "2013.01.02")
						data.SupFunctionalLocation = row.Cells[25].String()
						_, e := ctx.InsertOut(data)
						if e != nil {
							tk.Println("ERR on file :", source.Name(), " | ROW :", idx)
							tk.Println(e)
						}
					}
				}
			}
		}
	}
	tk.Println("Functional Location from Excel File : COMPLETE")
}
<|endoftext|>


<commit_before>package secgroups

import (
	"fmt"
	"net/http"
	"testing"

	th "github.com/rackspace/gophercloud/testhelper"
	fake "github.com/rackspace/gophercloud/testhelper/client"
)

const rootPath = "/os-security-groups"

const listGroupsJSON = `
{
	"security_groups": [
		{
			"description": "default",
			"id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
			"name": "default",
			"rules": [],
			"tenant_id": "openstack"
		}
	]
}
`

func mockListGroupsResponse(t *testing.T) {
	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, listGroupsJSON)
	})
}

func mockListGroupsByServerResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("%s/servers/%s%s", rootPath, serverID, rootPath)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, listGroupsJSON)
	})
}

func mockCreateGroupResponse(t *testing.T) {
	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "security_group": {
    "name": "test",
    "description": "something"
  }
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group": {
    "description": "something",
    "id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "name": "test",
    "rules": [],
    "tenant_id": "openstack"
  }
}
`)
	})
}

func mockUpdateGroupResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "PUT")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
	"security_group": {
		"name": "new_name"
	}
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
	"security_group": {
		"description": "something",
		"id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
		"name": "new_name",
		"rules": [],
		"tenant_id": "openstack"
	}
}
`)
	})
}

func mockGetGroupsResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group": {
    "description": "default",
    "id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "name": "default",
    "rules": [
      {
        "from_port": 80,
        "group": {
          "tenant_id": "openstack",
          "name": "default"
        },
        "ip_protocol": "TCP",
        "to_port": 85,
        "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
        "ip_range": {
						"cidr": "0.0.0.0"
				},
        "id": "ebe599e2-6b8c-457c-b1ff-a75e48f10923"
      }
    ],
    "tenant_id": "openstack"
  }
}
			`)
	})
}

func mockDeleteGroupResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "DELETE")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}

func mockAddRuleResponse(t *testing.T) {
	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "security_group_rule": {
    "from_port": 22,
    "ip_protocol": "TCP",
    "to_port": 22,
    "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "cidr": "0.0.0.0/0"
  }
}	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group_rule": {
    "from_port": 22,
    "group": {},
    "ip_protocol": "TCP",
    "to_port": 22,
    "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "ip_range": {
      "cidr": "0.0.0.0/0"
    },
    "id": "f9a97fcf-3a97-47b0-b76f-919136afb7ed"
  }
}`)
	})
}

func mockDeleteRuleResponse(t *testing.T, ruleID string) {
	url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "DELETE")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}

func mockAddServerToGroupResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("/servers/%s/action", serverID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "addSecurityGroup": {
    "name": "test"
  }
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
	})
}

func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("/servers/%s/action", serverID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
	"removeSecurityGroup": {
		"name": "test"
	}
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
	})
}
<commit_msg>Forgot the fixtures<commit_after>package secgroups

import (
	"fmt"
	"net/http"
	"testing"

	th "github.com/rackspace/gophercloud/testhelper"
	fake "github.com/rackspace/gophercloud/testhelper/client"
)

const rootPath = "/os-security-groups"

const listGroupsJSON = `
{
	"security_groups": [
		{
			"description": "default",
			"id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
			"name": "default",
			"rules": [],
			"tenant_id": "openstack"
		}
	]
}
`

func mockListGroupsResponse(t *testing.T) {
	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, listGroupsJSON)
	})
}

func mockListGroupsByServerResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("%s/servers/%s%s", rootPath, serverID, rootPath)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, listGroupsJSON)
	})
}

func mockCreateGroupResponse(t *testing.T) {
	th.Mux.HandleFunc(rootPath, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "security_group": {
    "name": "test",
    "description": "something"
  }
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group": {
    "description": "something",
    "id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "name": "test",
    "rules": [],
    "tenant_id": "openstack"
  }
}
`)
	})
}

func mockUpdateGroupResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "PUT")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
	"security_group": {
		"name": "new_name"
	}
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
	"security_group": {
		"description": "something",
		"id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
		"name": "new_name",
		"rules": [],
		"tenant_id": "openstack"
	}
}
`)
	})
}

func mockGetGroupsResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "GET")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group": {
    "description": "default",
    "id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "name": "default",
    "rules": [
      {
        "from_port": 80,
        "group": {
          "tenant_id": "openstack",
          "name": "default"
        },
        "ip_protocol": "TCP",
        "to_port": 85,
        "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
        "ip_range": {
						"cidr": "0.0.0.0"
				},
        "id": "ebe599e2-6b8c-457c-b1ff-a75e48f10923"
      }
    ],
    "tenant_id": "openstack"
  }
}
			`)
	})
}

func mockDeleteGroupResponse(t *testing.T, groupID string) {
	url := fmt.Sprintf("%s/%s", rootPath, groupID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "DELETE")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}

func mockAddRuleResponse(t *testing.T) {
	th.Mux.HandleFunc("/os-security-group-rules", func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "security_group_rule": {
    "from_port": 22,
    "ip_protocol": "TCP",
    "to_port": 22,
    "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "cidr": "0.0.0.0/0"
  }
}	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)

		fmt.Fprintf(w, `
{
  "security_group_rule": {
    "from_port": 22,
    "group": {},
    "ip_protocol": "TCP",
    "to_port": 22,
    "parent_group_id": "b0e0d7dd-2ca4-49a9-ba82-c44a148b66a5",
    "ip_range": {
      "cidr": "0.0.0.0/0"
    },
    "id": "f9a97fcf-3a97-47b0-b76f-919136afb7ed"
  }
}`)
	})
}

func mockDeleteRuleResponse(t *testing.T, ruleID string) {
	url := fmt.Sprintf("/os-security-group-rules/%s", ruleID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "DELETE")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}

func mockAddServerToGroupResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("/servers/%s/action", serverID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
  "addSecurityGroup": {
    "name": "test"
  }
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}

func mockRemoveServerFromGroupResponse(t *testing.T, serverID string) {
	url := fmt.Sprintf("/servers/%s/action", serverID)
	th.Mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
		th.TestMethod(t, r, "POST")
		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)

		th.TestJSONRequest(t, r, `
{
	"removeSecurityGroup": {
		"name": "test"
	}
}
	`)

		w.Header().Add("Content-Type", "application/json")
		w.WriteHeader(http.StatusAccepted)
	})
}
<|endoftext|>


<commit_before>package dupfinder2

import (
	"fmt"
	"io/ioutil"
	"os"
)

type Item interface {
	Equals(Item) bool
}

type Group interface {
	Items() []Item
	Accepts(Item) bool
	Add(Item)
}

type Tracker interface {
	Add(Item)
	Dups() []Group
}

type Filter interface {
	CandidateGroups(Item) []Group
	Register(Item, Group)
}

type defaultTracker struct {
	groups []Group
	filter Filter
}

func (t *defaultTracker) Add(item Item) {
	candidates := t.filter.CandidateGroups(item)

	found := false
	for _, g := range candidates {
		if g.Accepts(item) {
			g.Add(item)
			found = true
			break
		}
	}

	if !found {
		group := newGroup(item)
		t.groups = append(t.groups, group)
		t.filter.Register(item, group)
	}
}

func (t *defaultTracker) Dups() []Group {
	dups := make([]Group, 0)
	for _, g := range t.groups {
		if len(g.Items()) > 1 {
			dups = append(dups, g)
		}
	}
	return dups
}

func NewTracker(filter Filter) Tracker {
	return &defaultTracker{filter: filter}
}

type defaultGroup struct {
	items []Item
}

func (g *defaultGroup) Items() []Item {
	return g.items
}

func (g *defaultGroup) Add(item Item) {
	g.items = append(g.items, item)
}

func (g *defaultGroup) Accepts(item Item) bool {
	return g.items[0].Equals(item)
}

func (g *defaultGroup) String() string {
	return fmt.Sprintf("%v", g.items)
}

func newGroup(item Item) Group {
	g := &defaultGroup{}
	g.Add(item)
	return g
}

type FileItem struct {
	Path string
}

func (f *FileItem) Equals(other Item) bool {
	f2 := other.(*FileItem)

	s1, err1 := ioutil.ReadFile(f.Path)
	if err1 != nil {
		panic("could not read file: " + f.Path)
	}

	s2, err2 := ioutil.ReadFile(f2.Path)
	if err2 != nil {
		panic("could not read file: " + f.Path)
	}
	return string(s1) == string(s2)
}

func NewFileItem(path string) Item {
	return &FileItem{path}
}

type Key int

type KeyExtractor interface {
	Key(Item) Key
}

type sizeExtractor struct {
}

func (s *sizeExtractor) Key(item Item) Key {
	fi, e := os.Stat(item.(*FileItem).Path)
	if e != nil {
		return 0
	}
	return Key(fi.Size())
}

type defaultFilter struct {
	byKey        map[Key][]Group
	keyExtractor KeyExtractor
}

func (f *defaultFilter) CandidateGroups(item Item) []Group {
	if g, found := f.byKey[f.keyExtractor.Key(item)]; found {
		return g
	}
	return nil
}

func (f *defaultFilter) Register(item Item, g Group) {
	f.byKey[f.keyExtractor.Key(item)] = append(f.byKey[f.keyExtractor.Key(item)], g)
}

func NewFileFilter() Filter {
	return &defaultFilter{byKey: make(map[Key][]Group), keyExtractor: &sizeExtractor{}}
}
<commit_msg>Eliminate unnecessary flag<commit_after>package dupfinder2

import (
	"fmt"
	"io/ioutil"
	"os"
)

type Item interface {
	Equals(Item) bool
}

type Group interface {
	Items() []Item
	Accepts(Item) bool
	Add(Item)
}

type Tracker interface {
	Add(Item)
	Dups() []Group
}

type Filter interface {
	CandidateGroups(Item) []Group
	Register(Item, Group)
}

type defaultTracker struct {
	groups []Group
	filter Filter
}

func (t *defaultTracker) Add(item Item) {
	candidates := t.filter.CandidateGroups(item)

	for _, g := range candidates {
		if g.Accepts(item) {
			g.Add(item)
			return
		}
	}

	group := newGroup(item)
	t.groups = append(t.groups, group)
	t.filter.Register(item, group)
}

func (t *defaultTracker) Dups() []Group {
	dups := make([]Group, 0)
	for _, g := range t.groups {
		if len(g.Items()) > 1 {
			dups = append(dups, g)
		}
	}
	return dups
}

func NewTracker(filter Filter) Tracker {
	return &defaultTracker{filter: filter}
}

type defaultGroup struct {
	items []Item
}

func (g *defaultGroup) Items() []Item {
	return g.items
}

func (g *defaultGroup) Add(item Item) {
	g.items = append(g.items, item)
}

func (g *defaultGroup) Accepts(item Item) bool {
	return g.items[0].Equals(item)
}

func (g *defaultGroup) String() string {
	return fmt.Sprintf("%v", g.items)
}

func newGroup(item Item) Group {
	g := &defaultGroup{}
	g.Add(item)
	return g
}

type FileItem struct {
	Path string
}

func (f *FileItem) Equals(other Item) bool {
	f2 := other.(*FileItem)

	s1, err1 := ioutil.ReadFile(f.Path)
	if err1 != nil {
		panic("could not read file: " + f.Path)
	}

	s2, err2 := ioutil.ReadFile(f2.Path)
	if err2 != nil {
		panic("could not read file: " + f.Path)
	}
	return string(s1) == string(s2)
}

func NewFileItem(path string) Item {
	return &FileItem{path}
}

type Key int

type KeyExtractor interface {
	Key(Item) Key
}

type sizeExtractor struct {
}

func (s *sizeExtractor) Key(item Item) Key {
	fi, e := os.Stat(item.(*FileItem).Path)
	if e != nil {
		return 0
	}
	return Key(fi.Size())
}

type defaultFilter struct {
	byKey        map[Key][]Group
	keyExtractor KeyExtractor
}

func (f *defaultFilter) CandidateGroups(item Item) []Group {
	if g, found := f.byKey[f.keyExtractor.Key(item)]; found {
		return g
	}
	return nil
}

func (f *defaultFilter) Register(item Item, g Group) {
	f.byKey[f.keyExtractor.Key(item)] = append(f.byKey[f.keyExtractor.Key(item)], g)
}

func NewFileFilter() Filter {
	return &defaultFilter{byKey: make(map[Key][]Group), keyExtractor: &sizeExtractor{}}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Unknwon/com"
	"github.com/go-xorm/xorm"

	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

type NoticeType int

const (
	NOTICE_REPOSITORY NoticeType = iota + 1
)

// Notice represents a system notice for admin.
type Notice struct {
	ID          int64 `xorm:"pk autoincr"`
	Type        NoticeType
	Description string    `xorm:"TEXT"`
	Created     time.Time `xorm:"-"`
	CreatedUnix int64
}

func (n *Notice) BeforeInsert() {
	n.CreatedUnix = time.Now().Unix()
}

func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
	switch colName {
	case "created_unix":
		n.Created = time.Unix(n.CreatedUnix, 0).Local()
	}
}

// TrStr returns a translation format string.
func (n *Notice) TrStr() string {
	return "admin.notices.type_" + com.ToStr(n.Type)
}

// CreateNotice creates new system notice.
func CreateNotice(tp NoticeType, desc string) error {
	// prevent panic if database connection is not available at this point
	if x == nil {
		return fmt.Errorf("Could not save notice due database connection not being available: %d %s", tp, desc)
	}

	n := &Notice{
		Type:        tp,
		Description: desc,
	}
	_, err := x.Insert(n)
	return err
}

// CreateRepositoryNotice creates new system notice with type NOTICE_REPOSITORY.
func CreateRepositoryNotice(desc string) error {
	return CreateNotice(NOTICE_REPOSITORY, desc)
}

// RemoveAllWithNotice removes all directories in given path and
// creates a system notice when error occurs.
func RemoveAllWithNotice(title, path string) {
	var err error
	// workaround for Go not being able to remove read-only files/folders: https://github.com/golang/go/issues/9606
	// this bug should be fixed on Go 1.7, so the workaround should be removed when Gogs don't support Go 1.6 anymore:
	// https://github.com/golang/go/commit/2ffb3e5d905b5622204d199128dec06cefd57790
	if setting.IsWindows {
		// converting "/" to "\" in path on Windows
		path = strings.Replace(path, "/", "\\", -1)
		err = exec.Command("cmd", "/C", "rmdir", "/S", "/Q", path).Run()
	} else {
		err = os.RemoveAll(path)
	}

	if err != nil {
		desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
		log.Warn(desc)
		if err = CreateRepositoryNotice(desc); err != nil {
			log.Error(4, "CreateRepositoryNotice: %v", err)
		}
	}
}

// CountNotices returns number of notices.
func CountNotices() int64 {
	count, _ := x.Count(new(Notice))
	return count
}

// Notices returns number of notices in given page.
func Notices(page, pageSize int) ([]*Notice, error) {
	notices := make([]*Notice, 0, pageSize)
	return notices, x.Limit(pageSize, (page-1)*pageSize).Desc("id").Find(&notices)
}

// DeleteNotice deletes a system notice by given ID.
func DeleteNotice(id int64) error {
	_, err := x.Id(id).Delete(new(Notice))
	return err
}

// DeleteNotices deletes all notices with ID from start to end (inclusive).
func DeleteNotices(start, end int64) error {
	sess := x.Where("id >= ?", start)
	if end > 0 {
		sess.And("id <= ?", end)
	}
	_, err := sess.Delete(new(Notice))
	return err
}

// DeleteNoticesByIDs deletes notices by given IDs.
func DeleteNoticesByIDs(ids []int64) error {
	if len(ids) == 0 {
		return nil
	}
	_, err := x.Where("id IN (" + strings.Join(base.Int64sToStrings(ids), ",") + ")").Delete(new(Notice))
	return err
}
<commit_msg>Skip deletion for temporary data when not exist on Windows (#4069)<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Unknwon/com"
	"github.com/go-xorm/xorm"

	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

type NoticeType int

const (
	NOTICE_REPOSITORY NoticeType = iota + 1
)

// Notice represents a system notice for admin.
type Notice struct {
	ID          int64 `xorm:"pk autoincr"`
	Type        NoticeType
	Description string    `xorm:"TEXT"`
	Created     time.Time `xorm:"-"`
	CreatedUnix int64
}

func (n *Notice) BeforeInsert() {
	n.CreatedUnix = time.Now().Unix()
}

func (n *Notice) AfterSet(colName string, _ xorm.Cell) {
	switch colName {
	case "created_unix":
		n.Created = time.Unix(n.CreatedUnix, 0).Local()
	}
}

// TrStr returns a translation format string.
func (n *Notice) TrStr() string {
	return "admin.notices.type_" + com.ToStr(n.Type)
}

// CreateNotice creates new system notice.
func CreateNotice(tp NoticeType, desc string) error {
	// prevent panic if database connection is not available at this point
	if x == nil {
		return fmt.Errorf("Could not save notice due database connection not being available: %d %s", tp, desc)
	}

	n := &Notice{
		Type:        tp,
		Description: desc,
	}
	_, err := x.Insert(n)
	return err
}

// CreateRepositoryNotice creates new system notice with type NOTICE_REPOSITORY.
func CreateRepositoryNotice(desc string) error {
	return CreateNotice(NOTICE_REPOSITORY, desc)
}

// RemoveAllWithNotice removes all directories in given path and
// creates a system notice when error occurs.
func RemoveAllWithNotice(title, path string) {
	var err error
	// workaround for Go not being able to remove read-only files/folders: https://github.com/golang/go/issues/9606
	// this bug should be fixed on Go 1.7, so the workaround should be removed when Gogs don't support Go 1.6 anymore:
	// https://github.com/golang/go/commit/2ffb3e5d905b5622204d199128dec06cefd57790
	// Note: Windows complains when delete target does not exist, therefore we can skip deletion in such cases.
	if setting.IsWindows && com.IsExist(path) {
		// converting "/" to "\" in path on Windows
		path = strings.Replace(path, "/", "\\", -1)
		err = exec.Command("cmd", "/C", "rmdir", "/S", "/Q", path).Run()
	} else {
		err = os.RemoveAll(path)
	}

	if err != nil {
		desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
		log.Warn(desc)
		if err = CreateRepositoryNotice(desc); err != nil {
			log.Error(4, "CreateRepositoryNotice: %v", err)
		}
	}
}

// CountNotices returns number of notices.
func CountNotices() int64 {
	count, _ := x.Count(new(Notice))
	return count
}

// Notices returns number of notices in given page.
func Notices(page, pageSize int) ([]*Notice, error) {
	notices := make([]*Notice, 0, pageSize)
	return notices, x.Limit(pageSize, (page-1)*pageSize).Desc("id").Find(&notices)
}

// DeleteNotice deletes a system notice by given ID.
func DeleteNotice(id int64) error {
	_, err := x.Id(id).Delete(new(Notice))
	return err
}

// DeleteNotices deletes all notices with ID from start to end (inclusive).
func DeleteNotices(start, end int64) error {
	sess := x.Where("id >= ?", start)
	if end > 0 {
		sess.And("id <= ?", end)
	}
	_, err := sess.Delete(new(Notice))
	return err
}

// DeleteNoticesByIDs deletes notices by given IDs.
func DeleteNoticesByIDs(ids []int64) error {
	if len(ids) == 0 {
		return nil
	}
	_, err := x.Where("id IN (" + strings.Join(base.Int64sToStrings(ids), ",") + ")").Delete(new(Notice))
	return err
}
<|endoftext|>


<commit_before>package models

import (
	"github.com/eirka/eirka-libs/db"
	"time"
)

// NewModel holds the parameters from the request and also the key for the cache
type StatisticsModel struct {
	Ib     uint
	Result StatisticsType
}

type StatisticsType struct {
	Labels []time.Time `json:"labels"`
	Series []Series    `json:"series"`
}

type Series struct {
	Name string `json:"name"`
	Data []uint `json:"data"`
}

// Get will gather the information from the database and return it as JSON serialized data
func (i *StatisticsModel) Get() (err error) {

	// Initialize response header
	response := StatisticsType{}

	visitors := Series{
		Name: "Visitors",
	}

	hits := Series{
		Name: "Hits",
	}

	// Get Database handle
	dbase, err := db.GetDb()
	if err != nil {
		return
	}

	ps1, err := dbase.Prepare(`SELECT (now() - interval ? hour) as time,
    COUNT(DISTINCT request_ip) as visitors, COUNT(request_itemkey) as hits
    FROM analytics
    WHERE request_time BETWEEN (now() - interval ? hour) AND (now() - interval ? hour) AND ib_id = ?`)
	if err != nil {
		return
	}
	defer ps1.Close()

	// loop through every two hours
	for hour := 24; hour >= 2; hour-- {
		if hour%2 == 0 {

			var label time.Time
			var visitor_count, hit_count uint

			// period minus two hours
			previous := (hour - 2)

			err := ps1.QueryRow(hour, hour, previous, i.Ib).Scan(&label, &visitor_count, &hit_count)
			if err != nil {
				return err
			}

			response.Labels = append(response.Labels, label)
			visitors.Data = append(visitors.Data, visitor_count)
			hits.Data = append(hits.Data, hit_count)

		}
	}

	response.Series = append(response.Series, visitors, hits)

	// This is the data we will serialize
	i.Result = response

	return

}
<commit_msg>change to four hours<commit_after>package models

import (
	"github.com/eirka/eirka-libs/db"
	"time"
)

// NewModel holds the parameters from the request and also the key for the cache
type StatisticsModel struct {
	Ib     uint
	Result StatisticsType
}

type StatisticsType struct {
	Labels []time.Time `json:"labels"`
	Series []Series    `json:"series"`
}

type Series struct {
	Name string `json:"name"`
	Data []uint `json:"data"`
}

// Get will gather the information from the database and return it as JSON serialized data
func (i *StatisticsModel) Get() (err error) {

	// Initialize response header
	response := StatisticsType{}

	// holds visitors info
	visitors := Series{
		Name: "Visitors",
	}

	// holds count of hits
	hits := Series{
		Name: "Hits",
	}

	// Get Database handle
	dbase, err := db.GetDb()
	if err != nil {
		return
	}

	ps1, err := dbase.Prepare(`SELECT (now() - interval ? hour) as time,
    COUNT(DISTINCT request_ip) as visitors, COUNT(request_itemkey) as hits
    FROM analytics
    WHERE request_time BETWEEN (now() - interval ? hour) AND (now() - interval ? hour) AND ib_id = ?`)
	if err != nil {
		return
	}
	defer ps1.Close()

	// loop through every four hours
	for hour := 24; hour >= 4; hour-- {
		if hour%4 == 0 {

			var label time.Time
			var visitor_count, hit_count uint

			// period minus two hours
			previous := (hour - 4)

			err := ps1.QueryRow(hour, hour, previous, i.Ib).Scan(&label, &visitor_count, &hit_count)
			if err != nil {
				return err
			}

			response.Labels = append(response.Labels, label)
			visitors.Data = append(visitors.Data, visitor_count)
			hits.Data = append(hits.Data, hit_count)

		}
	}

	response.Series = append(response.Series, visitors, hits)

	// This is the data we will serialize
	i.Result = response

	return

}
<|endoftext|>


<commit_before>package models

import (
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/strava/go.strava"
)

// Friend struct handles the MongoDB schema for each users friends
type Friend struct {
	ID             int64  `bson:"_id" json:"id"`
	FirstName      string `bson:"firstname" json:"firstName"`
	LastName       string `bson:"lastname" json:"lastName"`
	FullName       string `bson:"fullName" json:"fullName"`
	Photo          string `bson:"photo" json:"photo"`
	ChallengeCount int    `bson:"challengeCount" json:"challengeCount"`
	Wins           int    `bson:"wins" json:"wins"`
	Losses         int    `bson:"losses" json:"losses"`
}

// UserSegment struct handles the MongoDB schema for each users segments
type UserSegment struct {
	ID           int64  `bson:"_id" json:"id"`
	Name         string `bson:"name" json:"name"`
	Count        int    `bson:"count" json:"count"`
	ActivityType string `bson:"activityType" json:"activityType"`
}

// User struct handles the MongoDB schema for a user
type User struct {
	ID             int64          `bson:"_id" json:"id"`
	FirstName      string         `bson:"firstname" json:"firstName"`
	LastName       string         `bson:"lastname" json:"lastName"`
	FullName       string         `bson:"fullname" json:"fullName"`
	City           string         `bson:"city" json:"city"`
	State          string         `bson:"state" json:"state"`
	Country        string         `bson:"country" json:"country"`
	Gender         string         `bson:"gender" json:"gender"`
	Token          string         `bson:"token" json:"token"`
	Photo          string         `bson:"photo" json:"photo"`
	Email          string         `bson:"email" json:"email"`
	Friends        []*Friend      `bson:"friends" json:"friends"`
	Segments       []*UserSegment `bson:"segments" json:"segments"`
	Wins           int            `bson:"wins" json:"wins"`
	Losses         int            `bson:"losses" json:"losses"`
	ChallengeCount int            `bson:"challengeCount" json:"challengeCount"`
	CreatedAt      time.Time      `bson:"createdAt" json:"createdAt,omitempty"`
	UpdatedAt      time.Time      `bson:"updatedAt" json:"updatedAt,omitempty"`
	DeletedAt      *time.Time     `bson:"deletedAt" json:"deletedAt,omitempty"`
}

// GetUserByID gets a single stored user from MongoDB
func GetUserByID(id int64) (*User, error) {
	s := session.Copy()
	defer s.Close()

	var u User

	if err := s.DB(name).C("users").FindId(id).One(&u); err != nil {
		log.WithField("USER ID", id).Errorf("Unable to find user with id:\n%v", err)
		return nil, err
	}

	log.WithField("USER ID", u.ID).Infof("user found %d", u.ID)

	return &u, nil
}

// CreateUser creates user in MongoDB
func CreateUser(auth *strava.AuthorizationResponse) (*User, error) {
	s := session.Copy()
	defer s.Close()

	user := User{
		ID:        auth.Athlete.Id,
		FirstName: auth.Athlete.FirstName,
		LastName:  auth.Athlete.LastName,
		FullName:  auth.Athlete.FirstName + " " + auth.Athlete.LastName,
		City:      auth.Athlete.City,
		State:     auth.Athlete.State,
		Country:   auth.Athlete.Country,
		Gender:    string(auth.Athlete.Gender),
		Token:     auth.AccessToken,
		Photo:     auth.Athlete.Profile,
		Email:     auth.Athlete.Email,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	if err := s.DB(name).C("users").Insert(&user); err != nil {
		log.WithField("ID", user.ID).Errorf("Unable to create user with id:\n %v", err)
		return nil, err
	}

	return &user, nil
}

// UpdateUser updates user in MongoDB
func (u User) UpdateUser(auth *strava.AuthorizationResponse) (*User, error) {
	s := session.Copy()
	defer s.Close()

	u.ID = auth.Athlete.Id
	u.FirstName = auth.Athlete.FirstName
	u.LastName = auth.Athlete.LastName
	u.FullName = auth.Athlete.FirstName + " " + auth.Athlete.LastName
	u.City = auth.Athlete.City
	u.State = auth.Athlete.State
	u.Country = auth.Athlete.Country
	u.Gender = string(auth.Athlete.Gender)
	u.Token = auth.AccessToken
	u.Photo = auth.Athlete.Profile
	u.Email = auth.Athlete.Email
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Errorf("Unable to update user:\n %v", err)
		return nil, err
	}

	return &u, nil
}

// RegisterUser creates a user in MongoDB
func RegisterUser(auth *strava.AuthorizationResponse) (*User, error) {
	u, err := GetUserByID(auth.Athlete.Id)
	if err != nil {
		log.WithField("USER ID", auth.Athlete.Id).Infof("Unable to find user with id %v creating user", auth.Athlete.Id)
		user, err := CreateUser(auth)
		if err != nil {
			return nil, err
		}
		return user, nil
	}
	log.WithField("USER ID", u.ID).Infof("Found user with id %v updating user", u.ID)
	user, err := u.UpdateUser(auth)
	if err != nil {
		return nil, err
	}
	return user, nil
}

// UpdateAthlete updates user in MongoDB
func (u User) UpdateAthlete(athlete *strava.AthleteDetailed) (*User, error) {
	s := session.Copy()
	defer s.Close()

	u.ID = athlete.Id
	u.FirstName = athlete.FirstName
	u.LastName = athlete.LastName
	u.FullName = athlete.FirstName + " " + athlete.LastName
	u.City = athlete.City
	u.State = athlete.State
	u.Country = athlete.Country
	u.Gender = string(athlete.Gender)
	u.Photo = athlete.Profile
	u.Email = athlete.Email
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Errorf("Unable to update user %v:\n %v", u.ID, err)
		return nil, err
	}
	log.WithField("USER ID", u.ID).Infof("user %d updated from Strava", u.ID)
	return &u, nil
}

// SaveUserFriends save user friends
func (u User) SaveUserFriends(friends []*Friend) error {
	s := session.Copy()
	defer s.Close()
	u.Friends = friends
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user friends")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("stored %v friends", len(friends))
	return nil
}

// SaveUserSegments save user segments
func (u User) SaveUserSegments(segments []*UserSegment) error {
	s := session.Copy()
	defer s.Close()
	u.Segments = segments
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Error("unable to save user segments")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("stored %v segments in db for user %v", len(segments), u.ID)
	return nil
}

// IncrementWins increment wins and challenge count for a particular user by id
func (u User) IncrementWins(id int64) error {
	s := session.Copy()
	defer s.Close()
	u.Wins = u.Wins + 1
	u.ChallengeCount = u.ChallengeCount + 1

	// loop over friends for user to find id
	for _, friend := range u.Friends {
		if friend.ID == id {
			// found friend.. increment count and wins
			log.Infof("incrementing count and wins for friend %d", friend.ID)
			friend.ChallengeCount = friend.ChallengeCount + 1
			friend.Wins = friend.Wins + 1
			break
		}
	}

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user increment wins")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented wins for challenge %d", id)
	return nil
}

// IncrementLosses increment losses and challenge count for a particular user by id
func (u User) IncrementLosses(id int64) error {
	s := session.Copy()
	defer s.Close()
	u.Losses = u.Losses + 1
	u.ChallengeCount = u.ChallengeCount + 1

	// loop over friends for user to find id
	for _, friend := range u.Friends {
		if friend.ID == id {
			// found friend.. increment count and losses
			log.Infof("incrementing count and losses for friend %d", friend.ID)
			friend.ChallengeCount = friend.ChallengeCount + 1
			friend.Losses = friend.Losses + 1
			break
		}
	}

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user increment losses")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented losses for challenge %d", id)
	return nil
}

// IncrementSegments increment segment count for a particular user by id
func (u User) IncrementSegments(id int64) error {
	s := session.Copy()
	defer s.Close()

	// loop over segments for user to find id
	for _, segment := range u.Segments {
		if segment.ID == id {
			// found segment.. increment count
			log.Infof("incrementing count for segment %d", segment.ID)
			segment.Count = segment.Count + 1
			break
		}
	}

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user segments increment")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented count for segment %d", id)
	return nil
}

// GetAllUsers returns all users from the DB
func GetAllUsers() ([]User, error) {
	s := session.Copy()
	defer s.Close()

	var users []User

	if err := s.DB(name).C("users").Find(nil).Sort("updatedAt").All(&users); err != nil {
		log.WithError(err).Error("Unable to return users")
		return nil, err
	}

	return users, nil
}

// RemoveUser deletes user from DB
func RemoveUser(ID int64) error {
	sess := session.Copy()
	defer sess.Close()

	if err := sess.DB(name).C("users").RemoveId(ID); err != nil {
		log.WithField("USER ID", ID).Errorf("Unable to remove user:\n %v", err)
		return err
	}

	return nil
}
<commit_msg>increment wins and losses<commit_after>package models

import (
	"time"

	log "github.com/sirupsen/logrus"
	"github.com/strava/go.strava"
)

// Friend struct handles the MongoDB schema for each users friends
type Friend struct {
	ID             int64  `bson:"_id" json:"id"`
	FirstName      string `bson:"firstname" json:"firstName"`
	LastName       string `bson:"lastname" json:"lastName"`
	FullName       string `bson:"fullName" json:"fullName"`
	Photo          string `bson:"photo" json:"photo"`
	ChallengeCount int    `bson:"challengeCount" json:"challengeCount"`
	Wins           int    `bson:"wins" json:"wins"`
	Losses         int    `bson:"losses" json:"losses"`
}

// UserSegment struct handles the MongoDB schema for each users segments
type UserSegment struct {
	ID           int64  `bson:"_id" json:"id"`
	Name         string `bson:"name" json:"name"`
	Count        int    `bson:"count" json:"count"`
	ActivityType string `bson:"activityType" json:"activityType"`
}

// User struct handles the MongoDB schema for a user
type User struct {
	ID             int64          `bson:"_id" json:"id"`
	FirstName      string         `bson:"firstname" json:"firstName"`
	LastName       string         `bson:"lastname" json:"lastName"`
	FullName       string         `bson:"fullname" json:"fullName"`
	City           string         `bson:"city" json:"city"`
	State          string         `bson:"state" json:"state"`
	Country        string         `bson:"country" json:"country"`
	Gender         string         `bson:"gender" json:"gender"`
	Token          string         `bson:"token" json:"token"`
	Photo          string         `bson:"photo" json:"photo"`
	Email          string         `bson:"email" json:"email"`
	Friends        []*Friend      `bson:"friends" json:"friends"`
	Segments       []*UserSegment `bson:"segments" json:"segments"`
	Wins           int            `bson:"wins" json:"wins"`
	Losses         int            `bson:"losses" json:"losses"`
	ChallengeCount int            `bson:"challengeCount" json:"challengeCount"`
	CreatedAt      time.Time      `bson:"createdAt" json:"createdAt,omitempty"`
	UpdatedAt      time.Time      `bson:"updatedAt" json:"updatedAt,omitempty"`
	DeletedAt      *time.Time     `bson:"deletedAt" json:"deletedAt,omitempty"`
}

// GetUserByID gets a single stored user from MongoDB
func GetUserByID(id int64) (*User, error) {
	s := session.Copy()
	defer s.Close()

	var u User

	if err := s.DB(name).C("users").FindId(id).One(&u); err != nil {
		log.WithField("USER ID", id).Errorf("Unable to find user with id:\n%v", err)
		return nil, err
	}

	log.WithField("USER ID", u.ID).Infof("user found %d", u.ID)

	return &u, nil
}

// CreateUser creates user in MongoDB
func CreateUser(auth *strava.AuthorizationResponse) (*User, error) {
	s := session.Copy()
	defer s.Close()

	user := User{
		ID:        auth.Athlete.Id,
		FirstName: auth.Athlete.FirstName,
		LastName:  auth.Athlete.LastName,
		FullName:  auth.Athlete.FirstName + " " + auth.Athlete.LastName,
		City:      auth.Athlete.City,
		State:     auth.Athlete.State,
		Country:   auth.Athlete.Country,
		Gender:    string(auth.Athlete.Gender),
		Token:     auth.AccessToken,
		Photo:     auth.Athlete.Profile,
		Email:     auth.Athlete.Email,
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	if err := s.DB(name).C("users").Insert(&user); err != nil {
		log.WithField("ID", user.ID).Errorf("Unable to create user with id:\n %v", err)
		return nil, err
	}

	return &user, nil
}

// UpdateUser updates user in MongoDB
func (u User) UpdateUser(auth *strava.AuthorizationResponse) (*User, error) {
	s := session.Copy()
	defer s.Close()

	u.ID = auth.Athlete.Id
	u.FirstName = auth.Athlete.FirstName
	u.LastName = auth.Athlete.LastName
	u.FullName = auth.Athlete.FirstName + " " + auth.Athlete.LastName
	u.City = auth.Athlete.City
	u.State = auth.Athlete.State
	u.Country = auth.Athlete.Country
	u.Gender = string(auth.Athlete.Gender)
	u.Token = auth.AccessToken
	u.Photo = auth.Athlete.Profile
	u.Email = auth.Athlete.Email
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Errorf("Unable to update user:\n %v", err)
		return nil, err
	}

	return &u, nil
}

// RegisterUser creates a user in MongoDB
func RegisterUser(auth *strava.AuthorizationResponse) (*User, error) {
	u, err := GetUserByID(auth.Athlete.Id)
	if err != nil {
		log.WithField("USER ID", auth.Athlete.Id).Infof("Unable to find user with id %v creating user", auth.Athlete.Id)
		user, err := CreateUser(auth)
		if err != nil {
			return nil, err
		}
		return user, nil
	}
	log.WithField("USER ID", u.ID).Infof("Found user with id %v updating user", u.ID)
	user, err := u.UpdateUser(auth)
	if err != nil {
		return nil, err
	}
	return user, nil
}

// UpdateAthlete updates user in MongoDB
func (u User) UpdateAthlete(athlete *strava.AthleteDetailed) (*User, error) {
	s := session.Copy()
	defer s.Close()

	u.ID = athlete.Id
	u.FirstName = athlete.FirstName
	u.LastName = athlete.LastName
	u.FullName = athlete.FirstName + " " + athlete.LastName
	u.City = athlete.City
	u.State = athlete.State
	u.Country = athlete.Country
	u.Gender = string(athlete.Gender)
	u.Photo = athlete.Profile
	u.Email = athlete.Email
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Errorf("Unable to update user %v:\n %v", u.ID, err)
		return nil, err
	}
	log.WithField("USER ID", u.ID).Infof("user %d updated from Strava", u.ID)
	return &u, nil
}

// SaveUserFriends save user friends
func (u User) SaveUserFriends(friends []*Friend) error {
	s := session.Copy()
	defer s.Close()
	u.Friends = friends
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user friends")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("stored %v friends", len(friends))
	return nil
}

// SaveUserSegments save user segments
func (u User) SaveUserSegments(segments []*UserSegment) error {
	s := session.Copy()
	defer s.Close()
	u.Segments = segments
	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.WithField("USER ID", u.ID).Error("unable to save user segments")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("stored %v segments in db for user %v", len(segments), u.ID)
	return nil
}

// IncrementWins increment wins and challenge count for a particular user by id
func (u *User) IncrementWins(id int64) error {
	s := session.Copy()
	defer s.Close()
	u.Wins++
	u.ChallengeCount++

	// loop over friends for user to find id
	for _, friend := range u.Friends {
		if friend.ID == id {
			// found friend.. increment count and wins
			log.Infof("incrementing count and wins for friend %d", friend.ID)
			friend.ChallengeCount = friend.ChallengeCount + 1
			friend.Wins = friend.Wins + 1
			break
		}
	}

	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user increment wins")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented wins for challenge %d", id)
	return nil
}

// IncrementLosses increment losses and challenge count for a particular user by id
func (u *User) IncrementLosses(id int64) error {
	s := session.Copy()
	defer s.Close()
	u.Losses++
	u.ChallengeCount++

	// loop over friends for user to find id
	for _, friend := range u.Friends {
		if friend.ID == id {
			// found friend.. increment count and losses
			log.Infof("incrementing count and losses for friend %d", friend.ID)
			friend.ChallengeCount = friend.ChallengeCount + 1
			friend.Losses = friend.Losses + 1
			break
		}
	}

	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user increment losses")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented losses for challenge %d", id)
	return nil
}

// IncrementSegments increment segment count for a particular user by id
func (u *User) IncrementSegments(id int64) error {
	s := session.Copy()
	defer s.Close()

	// loop over segments for user to find id
	for _, segment := range u.Segments {
		if segment.ID == id {
			// found segment.. increment count
			log.Infof("incrementing count for segment %d", segment.ID)
			segment.Count = segment.Count + 1
			break
		}
	}

	u.UpdatedAt = time.Now()

	if err := s.DB(name).C("users").UpdateId(u.ID, &u); err != nil {
		log.Error("unable to save user segments increment")
		return err
	}
	log.WithField("USER ID", u.ID).Infof("incremented count for segment %d", id)
	return nil
}

// GetAllUsers returns all users from the DB
func GetAllUsers() ([]User, error) {
	s := session.Copy()
	defer s.Close()

	var users []User

	if err := s.DB(name).C("users").Find(nil).Sort("updatedAt").All(&users); err != nil {
		log.WithError(err).Error("Unable to return users")
		return nil, err
	}

	return users, nil
}

// RemoveUser deletes user from DB
func RemoveUser(ID int64) error {
	sess := session.Copy()
	defer sess.Close()

	if err := sess.DB(name).C("users").RemoveId(ID); err != nil {
		log.WithField("USER ID", ID).Errorf("Unable to remove user:\n %v", err)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package themes

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/nlopes/slack"
	"github.com/tuna-timer/tuna-timer-api/models"
	"github.com/tuna-timer/tuna-timer-api/utils"
	"time"
)

// DefaultSlackMessageTheme - the basic UI theme for messages that go back from us to Slack users
type DefaultSlackMessageTheme struct {
	themeConfig
	ctx context.Context
}

var defaultThemeConfig = themeConfig{
	MarkdownEnabledFor:     []string{"text", "pretext"},
	SummaryAttachmentColor: "#000000",
	FooterIcon:             "http://icons.iconarchive.com/icons/martin-berube/flat-animal/48/tuna-icon.png",
	StartCommandThumbURL:   "/assets/themes/default/ic_current.png",
	StartCommandColor:      "F5A623",
	StopCommandThumbURL:    "/assets/themes/default/ic_completed.png",
	StopCommandColor:       "#4A90E2",
	StatusCommandThumbURL:  "/assets/themes/default/ic_status.png",
	StatusCommandColor:     "#9B9B9B",
}

func NewDefaultSlackMessageTheme(ctx context.Context) *DefaultSlackMessageTheme {
	return &DefaultSlackMessageTheme{
		themeConfig: defaultThemeConfig,
		ctx:         ctx,
	}
}

func (t *DefaultSlackMessageTheme) FormatStatusCommand(data *models.StatusCommandReport) string {

	tpl := slackThemeTemplate{
		Text:        fmt.Sprintf("Your status for %s", data.PeriodName),
		Attachments: []slack.Attachment{},
	}

	summaryAttachmentVisible := len(data.Tasks) > 0 || data.AlreadyStartedTimer != nil

	if len(data.Tasks) > 0 {
		statusAttachment := t.defaultAttachment()
		statusAttachment.ThumbURL = t.asset(t.StopCommandThumbURL)
		statusAttachment.Color = t.StopCommandColor
		statusAttachment.AuthorName = "Completed:"

		var buffer bytes.Buffer

		for _, task := range data.Tasks {

			displayProjectLink := task.ProjectExternalID != data.Project.ExternalProjectID

			if data.AlreadyStartedTimer == nil || data.AlreadyStartedTimer.TaskHash != task.TaskHash {
				if displayProjectLink {
					buffer.WriteString(t.taskWithProject(task.Name, task.Minutes, task.ProjectExternalID, task.ProjectExternalName))
				} else {
					buffer.WriteString(t.task(task.Name, task.Minutes))
				}
			}
		}

		if buffer.Len() > 0 {
			statusAttachment.Text = buffer.String()
			statusAttachment.Footer = "<http://www.foo.com|Open in Application>"
			tpl.Attachments = append(tpl.Attachments, statusAttachment)
		}
	}

	if data.AlreadyStartedTimer != nil {
		sa := t.attachmentForCurrentTask(data.AlreadyStartedTimer, data.AlreadyStartedTimerTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if summaryAttachmentVisible {
		tpl.Attachments = append(tpl.Attachments, t.summaryAttachment(data.PeriodName, data.UserTotalForPeriod))
	} else {
		tpl.Text = fmt.Sprintf("You have no tasks completed %s", data.PeriodName)
	}

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) FormatStopCommand(data *models.StopCommandReport) string {
	tpl := slackThemeTemplate{
		Attachments: []slack.Attachment{},
	}

	if data.StoppedTimer != nil {
		sa := t.attachmentForStoppedTask(data.StoppedTimer, data.StoppedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	tpl.Attachments = append(tpl.Attachments, t.summaryAttachment("today", data.UserTotalForToday))

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) FormatStartCommand(data *models.StartCommandReport) string {
	tpl := slackThemeTemplate{
		Attachments: []slack.Attachment{},
	}

	if data.StoppedTimer != nil {
		sa := t.attachmentForStoppedTask(data.StoppedTimer, data.StoppedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if data.StartedTimer != nil {
		sa := t.attachmentForNewTask(data.StartedTimer, data.StartedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if data.AlreadyStartedTimer != nil {
		sa := t.attachmentForNewTask(data.AlreadyStartedTimer, data.AlreadyStartedTimerTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	tpl.Attachments = append(tpl.Attachments, t.summaryAttachment("today", data.UserTotalForToday))

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) attachmentForNewTask(timer *models.Timer, taskTotalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.Text = t.task(timer.TaskName, taskTotalForToday) //fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(taskTotalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StartCommandThumbURL)
	sa.Color = t.StartCommandColor
	sa.AuthorName = "Started:"

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Edit in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	return sa
}

func (t *DefaultSlackMessageTheme) attachmentForCurrentTask(timer *models.Timer, totalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.Text = t.task(timer.TaskName, totalForToday) //fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(totalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StartCommandThumbURL)
	sa.Color = t.StartCommandColor
	sa.AuthorName = "Current:"

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Open in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	sa.Fields = []slack.AttachmentField{}
	return sa
}

func (t *DefaultSlackMessageTheme) attachmentForStoppedTask(timer *models.Timer, totalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.AuthorName = "Completed:"

	sa.Text = t.task(timer.TaskName, totalForToday) // fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(totalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StopCommandThumbURL)
	sa.Color = t.StopCommandColor

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Open in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	sa.Fields = []slack.AttachmentField{}
	return sa
}

func (t *DefaultSlackMessageTheme) summaryAttachment(period string, minutes int) slack.Attachment {
	result := slack.Attachment{}
	result.Text = fmt.Sprintf("*Your total for %s is %s*",
		period,
		utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))))

	result.Color = t.SummaryAttachmentColor
	result.MarkdownIn = t.MarkdownEnabledFor
	return result
}

func (t *DefaultSlackMessageTheme) defaultAttachment() slack.Attachment {
	result := slack.Attachment{}
	result.MarkdownIn = t.MarkdownEnabledFor
	return result
}

func (t *DefaultSlackMessageTheme) asset(assetPath string) string {
	return utils.GetSelfBaseURLFromContext(t.ctx) + assetPath
}

func (t *DefaultSlackMessageTheme) channelLinkForTimer(timer *models.Timer) string {
	return t.channelLink(timer.ProjectExternalID, timer.ProjectExternalName)
}

func (t *DefaultSlackMessageTheme) channelLink(channelID, channelName string) string {
	return fmt.Sprintf("<#%s|%s>", channelID, channelName)
}

func (t *DefaultSlackMessageTheme) task(text string, minutes int) string {
	return fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))), text)
}

func (t *DefaultSlackMessageTheme) taskWithProject(text string, minutes int, projectID, projectName string) string {
	return fmt.Sprintf("•  *%s*%s %s\n",
		utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))),
		t.channelLink(projectID, projectName),
		text)
}
<commit_msg>more work on message formatting<commit_after>package themes

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/nlopes/slack"
	"github.com/tuna-timer/tuna-timer-api/models"
	"github.com/tuna-timer/tuna-timer-api/utils"
	"time"
)

// DefaultSlackMessageTheme - the basic UI theme for messages that go back from us to Slack users
type DefaultSlackMessageTheme struct {
	themeConfig
	ctx context.Context
}

var defaultThemeConfig = themeConfig{
	MarkdownEnabledFor:     []string{"text", "pretext"},
	SummaryAttachmentColor: "#000000",
	FooterIcon:             "http://icons.iconarchive.com/icons/martin-berube/flat-animal/48/tuna-icon.png",
	StartCommandThumbURL:   "/assets/themes/default/ic_current.png",
	StartCommandColor:      "F5A623",
	StopCommandThumbURL:    "/assets/themes/default/ic_completed.png",
	StopCommandColor:       "#4A90E2",
	StatusCommandThumbURL:  "/assets/themes/default/ic_status.png",
	StatusCommandColor:     "#9B9B9B",
}

func NewDefaultSlackMessageTheme(ctx context.Context) *DefaultSlackMessageTheme {
	return &DefaultSlackMessageTheme{
		themeConfig: defaultThemeConfig,
		ctx:         ctx,
	}
}

func (t *DefaultSlackMessageTheme) FormatStatusCommand(data *models.StatusCommandReport) string {

	tpl := slackThemeTemplate{
		Text:        fmt.Sprintf("Your status for %s", data.PeriodName),
		Attachments: []slack.Attachment{},
	}

	summaryAttachmentVisible := len(data.Tasks) > 0 || data.AlreadyStartedTimer != nil

	if len(data.Tasks) > 0 {
		statusAttachment := t.defaultAttachment()
		statusAttachment.ThumbURL = t.asset(t.StopCommandThumbURL)
		statusAttachment.Color = t.StopCommandColor
		statusAttachment.AuthorName = "Completed:"

		var buffer bytes.Buffer

		for _, task := range data.Tasks {

			displayProjectLink := task.ProjectExternalID != data.Project.ExternalProjectID

			if data.AlreadyStartedTimer == nil || data.AlreadyStartedTimer.TaskHash != task.TaskHash {
				if displayProjectLink {
					buffer.WriteString(t.taskWithProject(task.Name, task.Minutes, task.ProjectExternalID, task.ProjectExternalName))
				} else {
					buffer.WriteString(t.task(task.Name, task.Minutes))
				}
			}
		}

		if buffer.Len() > 0 {
			statusAttachment.Text = buffer.String()
			statusAttachment.Footer = "<http://www.foo.com|Open in Application>"
			tpl.Attachments = append(tpl.Attachments, statusAttachment)
		}
	}

	if data.AlreadyStartedTimer != nil {
		sa := t.attachmentForCurrentTask(data.AlreadyStartedTimer, data.AlreadyStartedTimerTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if summaryAttachmentVisible {
		tpl.Attachments = append(tpl.Attachments, t.summaryAttachment(data.PeriodName, data.UserTotalForPeriod))
	} else {
		tpl.Text = fmt.Sprintf("You have no tasks completed %s", data.PeriodName)
	}

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) FormatStopCommand(data *models.StopCommandReport) string {
	tpl := slackThemeTemplate{
		Attachments: []slack.Attachment{},
	}

	if data.StoppedTimer != nil {
		sa := t.attachmentForStoppedTask(data.StoppedTimer, data.StoppedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	tpl.Attachments = append(tpl.Attachments, t.summaryAttachment("today", data.UserTotalForToday))

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) FormatStartCommand(data *models.StartCommandReport) string {
	tpl := slackThemeTemplate{
		Attachments: []slack.Attachment{},
	}

	if data.StoppedTimer != nil {
		sa := t.attachmentForStoppedTask(data.StoppedTimer, data.StoppedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if data.StartedTimer != nil {
		sa := t.attachmentForNewTask(data.StartedTimer, data.StartedTaskTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	if data.AlreadyStartedTimer != nil {
		sa := t.attachmentForNewTask(data.AlreadyStartedTimer, data.AlreadyStartedTimerTotalForToday)
		tpl.Attachments = append(tpl.Attachments, sa)
	}

	tpl.Attachments = append(tpl.Attachments, t.summaryAttachment("today", data.UserTotalForToday))

	result, err := json.Marshal(tpl)
	if err != nil {
		// todo return { "text": err.String() }
	}

	return string(result)
}

func (t *DefaultSlackMessageTheme) attachmentForNewTask(timer *models.Timer, taskTotalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.Text = t.task(timer.TaskName, taskTotalForToday) //fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(taskTotalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StartCommandThumbURL)
	sa.Color = t.StartCommandColor
	sa.AuthorName = "Started:"

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Edit in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	return sa
}

func (t *DefaultSlackMessageTheme) attachmentForCurrentTask(timer *models.Timer, totalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.Text = t.task(timer.TaskName, totalForToday) //fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(totalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StartCommandThumbURL)
	sa.Color = t.StartCommandColor
	sa.AuthorName = "Current:"

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Open in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	sa.Fields = []slack.AttachmentField{}
	return sa
}

func (t *DefaultSlackMessageTheme) attachmentForStoppedTask(timer *models.Timer, totalForToday int) slack.Attachment {
	sa := t.defaultAttachment()
	sa.AuthorName = "Completed:"

	sa.Text = t.task(timer.TaskName, totalForToday) // fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(totalForToday)*int64(time.Minute))), timer.TaskName)
	sa.ThumbURL = t.asset(t.StopCommandThumbURL)
	sa.Color = t.StopCommandColor

	sa.Footer = fmt.Sprintf(
		"Project: %s > Task: %s > <http://www.google.com|Open in Application>", t.channelLinkForTimer(timer), timer.TaskHash)

	sa.Fields = []slack.AttachmentField{}
	return sa
}

func (t *DefaultSlackMessageTheme) summaryAttachment(period string, minutes int) slack.Attachment {
	result := slack.Attachment{}
	result.Text = fmt.Sprintf("*Your total for %s is %s*",
		period,
		utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))))

	result.Color = t.SummaryAttachmentColor
	result.MarkdownIn = t.MarkdownEnabledFor
	return result
}

func (t *DefaultSlackMessageTheme) defaultAttachment() slack.Attachment {
	result := slack.Attachment{}
	result.MarkdownIn = t.MarkdownEnabledFor
	return result
}

func (t *DefaultSlackMessageTheme) asset(assetPath string) string {
	return utils.GetSelfBaseURLFromContext(t.ctx) + assetPath
}

func (t *DefaultSlackMessageTheme) channelLinkForTimer(timer *models.Timer) string {
	return t.channelLink(timer.ProjectExternalID, timer.ProjectExternalName)
}

func (t *DefaultSlackMessageTheme) channelLink(channelID, channelName string) string {
	return fmt.Sprintf("<#%s|%s>", channelID, channelName)
}

func (t *DefaultSlackMessageTheme) task(text string, minutes int) string {
	return fmt.Sprintf("•  *%s*  %s\n", utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))), text)
}

func (t *DefaultSlackMessageTheme) taskWithProject(text string, minutes int, projectID, projectName string) string {
	return fmt.Sprintf("•  *%s  *%s  %s\n",
		utils.FormatDuration(time.Duration(int64(minutes)*int64(time.Minute))),
		t.channelLink(projectID, projectName),
		text)
}
<|endoftext|>