<commit_before>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package kafka

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/Shopify/sarama"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/common/flogging"
	mockconfig "github.com/hyperledger/fabric/common/mocks/config"
	localconfig "github.com/hyperledger/fabric/orderer/common/localconfig"
	"github.com/hyperledger/fabric/orderer/consensus"
	mockmultichannel "github.com/hyperledger/fabric/orderer/mocks/common/multichannel"
	cb "github.com/hyperledger/fabric/protos/common"
	ab "github.com/hyperledger/fabric/protos/orderer"
	"github.com/hyperledger/fabric/protos/utils"
	"github.com/stretchr/testify/assert"
)

var mockRetryOptions = localconfig.Retry{
	ShortInterval: 50 * time.Millisecond,
	ShortTotal:    100 * time.Millisecond,
	LongInterval:  60 * time.Millisecond,
	LongTotal:     120 * time.Millisecond,
	NetworkTimeouts: localconfig.NetworkTimeouts{
		DialTimeout:  40 * time.Millisecond,
		ReadTimeout:  40 * time.Millisecond,
		WriteTimeout: 40 * time.Millisecond,
	},
	Metadata: localconfig.Metadata{
		RetryMax:     2,
		RetryBackoff: 40 * time.Millisecond,
	},
	Producer: localconfig.Producer{
		RetryMax:     2,
		RetryBackoff: 40 * time.Millisecond,
	},
	Consumer: localconfig.Consumer{
		RetryBackoff: 40 * time.Millisecond,
	},
}

func init() {
	mockLocalConfig = newMockLocalConfig(false, mockRetryOptions, false)
	mockBrokerConfig = newMockBrokerConfig(mockLocalConfig.General.TLS, mockLocalConfig.Kafka.Retry, mockLocalConfig.Kafka.Version, defaultPartition)
	mockConsenter = newMockConsenter(mockBrokerConfig, mockLocalConfig.General.TLS, mockLocalConfig.Kafka.Retry, mockLocalConfig.Kafka.Version)
	setupTestLogging("ERROR")
}

func TestNew(t *testing.T) {
	_ = consensus.Consenter(New(mockLocalConfig.Kafka))
}

func TestHandleChain(t *testing.T) {
	consenter := consensus.Consenter(New(mockLocalConfig.Kafka))

	oldestOffset := int64(0)
	newestOffset := int64(5)
	message := sarama.StringEncoder("messageFoo")

	mockChannel := newChannel(channelNameForTest(t), defaultPartition)

	mockBroker := sarama.NewMockBroker(t, 0)
	mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{
		"MetadataRequest": sarama.NewMockMetadataResponse(t).
			SetBroker(mockBroker.Addr(), mockBroker.BrokerID()).
			SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()),
		"ProduceRequest": sarama.NewMockProduceResponse(t).
			SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError),
		"OffsetRequest": sarama.NewMockOffsetResponse(t).
			SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset).
			SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset),
		"FetchRequest": sarama.NewMockFetchResponse(t, 1).
			SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message),
	})

	mockSupport := &mockmultichannel.ConsenterSupport{
		ChainIDVal: mockChannel.topic(),
		SharedConfigVal: &mockconfig.Orderer{
			KafkaBrokersVal: []string{mockBroker.Addr()},
		},
	}

	mockMetadata := &cb.Metadata{Value: utils.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: newestOffset - 1})}

	_, err := consenter.HandleChain(mockSupport, mockMetadata)
	assert.NoError(t, err, "Expected the HandleChain call to return without errors")
}

// Test helper functions and mock objects defined here

var mockConsenter commonConsenter
var mockLocalConfig *localconfig.TopLevel
var mockBrokerConfig *sarama.Config

func extractEncodedOffset(marshalledOrdererMetadata []byte) int64 {
	omd := &cb.Metadata{}
	_ = proto.Unmarshal(marshalledOrdererMetadata, omd)
	kmd := &ab.KafkaMetadata{}
	_ = proto.Unmarshal(omd.GetValue(), kmd)
	return kmd.LastOffsetPersisted
}

func newMockBrokerConfig(tlsConfig localconfig.TLS, retryOptions localconfig.Retry, kafkaVersion sarama.KafkaVersion, chosenStaticPartition int32) *sarama.Config {
	brokerConfig := newBrokerConfig(tlsConfig, retryOptions, kafkaVersion, chosenStaticPartition)
	brokerConfig.ClientID = "test"
	return brokerConfig
}

func newMockConsenter(brokerConfig *sarama.Config, tlsConfig localconfig.TLS, retryOptions localconfig.Retry, kafkaVersion sarama.KafkaVersion) *consenterImpl {
	return &consenterImpl{
		brokerConfigVal: brokerConfig,
		tlsConfigVal:    tlsConfig,
		retryOptionsVal: retryOptions,
		kafkaVersionVal: kafkaVersion,
	}
}

func newMockConsumerMessage(wrappedMessage *ab.KafkaMessage) *sarama.ConsumerMessage {
	return &sarama.ConsumerMessage{
		Value: sarama.ByteEncoder(utils.MarshalOrPanic(wrappedMessage)),
	}
}

func newMockEnvelope(content string) *cb.Envelope {
	return &cb.Envelope{Payload: utils.MarshalOrPanic(&cb.Payload{
		Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(&cb.ChannelHeader{ChannelId: "foo"})},
		Data:   []byte(content),
	})}
}

func newMockLocalConfig(enableTLS bool, retryOptions localconfig.Retry, verboseLog bool) *localconfig.TopLevel {
	return &localconfig.TopLevel{
		General: localconfig.General{
			TLS: localconfig.TLS{
				Enabled: enableTLS,
			},
		},
		Kafka: localconfig.Kafka{
			TLS: localconfig.TLS{
				Enabled: enableTLS,
			},
			Retry:   retryOptions,
			Verbose: verboseLog,
			Version: sarama.V0_9_0_1, // sarama.MockBroker only produces messages compatible with version < 0.10
		},
	}
}

func setupTestLogging(logLevel string) {
	// This call allows us to (a) get the logging backend initialization that
	// takes place in the `flogging` package, and (b) adjust the verbosity of
	// the logs when running tests on this package.
	flogging.SetModuleLevel(pkgLogID, logLevel)
	flogging.SetModuleLevel(saramaLogID, logLevel)
}

func tamperBytes(original []byte) []byte {
	byteCount := len(original)
	return original[:byteCount-1]
}

func channelNameForTest(t *testing.T) string {
	name := strings.Split(fmt.Sprint(t), " ")[18] // w/golang 1.8, use t.Name()
	return fmt.Sprintf("%s.channel", strings.Replace(strings.ToLower(name), "/", ".", -1))
}
<commit_msg>[FAB-7443] Use t.Name() to get test name<commit_after>/*
Copyright IBM Corp. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package kafka

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/Shopify/sarama"
	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/common/flogging"
	mockconfig "github.com/hyperledger/fabric/common/mocks/config"
	localconfig "github.com/hyperledger/fabric/orderer/common/localconfig"
	"github.com/hyperledger/fabric/orderer/consensus"
	mockmultichannel "github.com/hyperledger/fabric/orderer/mocks/common/multichannel"
	cb "github.com/hyperledger/fabric/protos/common"
	ab "github.com/hyperledger/fabric/protos/orderer"
	"github.com/hyperledger/fabric/protos/utils"
	"github.com/stretchr/testify/assert"
)

var mockRetryOptions = localconfig.Retry{
	ShortInterval: 50 * time.Millisecond,
	ShortTotal:    100 * time.Millisecond,
	LongInterval:  60 * time.Millisecond,
	LongTotal:     120 * time.Millisecond,
	NetworkTimeouts: localconfig.NetworkTimeouts{
		DialTimeout:  40 * time.Millisecond,
		ReadTimeout:  40 * time.Millisecond,
		WriteTimeout: 40 * time.Millisecond,
	},
	Metadata: localconfig.Metadata{
		RetryMax:     2,
		RetryBackoff: 40 * time.Millisecond,
	},
	Producer: localconfig.Producer{
		RetryMax:     2,
		RetryBackoff: 40 * time.Millisecond,
	},
	Consumer: localconfig.Consumer{
		RetryBackoff: 40 * time.Millisecond,
	},
}

func init() {
	mockLocalConfig = newMockLocalConfig(false, mockRetryOptions, false)
	mockBrokerConfig = newMockBrokerConfig(mockLocalConfig.General.TLS, mockLocalConfig.Kafka.Retry, mockLocalConfig.Kafka.Version, defaultPartition)
	mockConsenter = newMockConsenter(mockBrokerConfig, mockLocalConfig.General.TLS, mockLocalConfig.Kafka.Retry, mockLocalConfig.Kafka.Version)
	setupTestLogging("ERROR")
}

func TestNew(t *testing.T) {
	_ = consensus.Consenter(New(mockLocalConfig.Kafka))
}

func TestHandleChain(t *testing.T) {
	consenter := consensus.Consenter(New(mockLocalConfig.Kafka))

	oldestOffset := int64(0)
	newestOffset := int64(5)
	message := sarama.StringEncoder("messageFoo")

	mockChannel := newChannel(channelNameForTest(t), defaultPartition)

	mockBroker := sarama.NewMockBroker(t, 0)
	mockBroker.SetHandlerByMap(map[string]sarama.MockResponse{
		"MetadataRequest": sarama.NewMockMetadataResponse(t).
			SetBroker(mockBroker.Addr(), mockBroker.BrokerID()).
			SetLeader(mockChannel.topic(), mockChannel.partition(), mockBroker.BrokerID()),
		"ProduceRequest": sarama.NewMockProduceResponse(t).
			SetError(mockChannel.topic(), mockChannel.partition(), sarama.ErrNoError),
		"OffsetRequest": sarama.NewMockOffsetResponse(t).
			SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetOldest, oldestOffset).
			SetOffset(mockChannel.topic(), mockChannel.partition(), sarama.OffsetNewest, newestOffset),
		"FetchRequest": sarama.NewMockFetchResponse(t, 1).
			SetMessage(mockChannel.topic(), mockChannel.partition(), newestOffset, message),
	})

	mockSupport := &mockmultichannel.ConsenterSupport{
		ChainIDVal: mockChannel.topic(),
		SharedConfigVal: &mockconfig.Orderer{
			KafkaBrokersVal: []string{mockBroker.Addr()},
		},
	}

	mockMetadata := &cb.Metadata{Value: utils.MarshalOrPanic(&ab.KafkaMetadata{LastOffsetPersisted: newestOffset - 1})}

	_, err := consenter.HandleChain(mockSupport, mockMetadata)
	assert.NoError(t, err, "Expected the HandleChain call to return without errors")
}

// Test helper functions and mock objects defined here

var mockConsenter commonConsenter
var mockLocalConfig *localconfig.TopLevel
var mockBrokerConfig *sarama.Config

func extractEncodedOffset(marshalledOrdererMetadata []byte) int64 {
	omd := &cb.Metadata{}
	_ = proto.Unmarshal(marshalledOrdererMetadata, omd)
	kmd := &ab.KafkaMetadata{}
	_ = proto.Unmarshal(omd.GetValue(), kmd)
	return kmd.LastOffsetPersisted
}

func newMockBrokerConfig(tlsConfig localconfig.TLS, retryOptions localconfig.Retry, kafkaVersion sarama.KafkaVersion, chosenStaticPartition int32) *sarama.Config {
	brokerConfig := newBrokerConfig(tlsConfig, retryOptions, kafkaVersion, chosenStaticPartition)
	brokerConfig.ClientID = "test"
	return brokerConfig
}

func newMockConsenter(brokerConfig *sarama.Config, tlsConfig localconfig.TLS, retryOptions localconfig.Retry, kafkaVersion sarama.KafkaVersion) *consenterImpl {
	return &consenterImpl{
		brokerConfigVal: brokerConfig,
		tlsConfigVal:    tlsConfig,
		retryOptionsVal: retryOptions,
		kafkaVersionVal: kafkaVersion,
	}
}

func newMockConsumerMessage(wrappedMessage *ab.KafkaMessage) *sarama.ConsumerMessage {
	return &sarama.ConsumerMessage{
		Value: sarama.ByteEncoder(utils.MarshalOrPanic(wrappedMessage)),
	}
}

func newMockEnvelope(content string) *cb.Envelope {
	return &cb.Envelope{Payload: utils.MarshalOrPanic(&cb.Payload{
		Header: &cb.Header{ChannelHeader: utils.MarshalOrPanic(&cb.ChannelHeader{ChannelId: "foo"})},
		Data:   []byte(content),
	})}
}

func newMockLocalConfig(enableTLS bool, retryOptions localconfig.Retry, verboseLog bool) *localconfig.TopLevel {
	return &localconfig.TopLevel{
		General: localconfig.General{
			TLS: localconfig.TLS{
				Enabled: enableTLS,
			},
		},
		Kafka: localconfig.Kafka{
			TLS: localconfig.TLS{
				Enabled: enableTLS,
			},
			Retry:   retryOptions,
			Verbose: verboseLog,
			Version: sarama.V0_9_0_1, // sarama.MockBroker only produces messages compatible with version < 0.10
		},
	}
}

func setupTestLogging(logLevel string) {
	// This call allows us to (a) get the logging backend initialization that
	// takes place in the `flogging` package, and (b) adjust the verbosity of
	// the logs when running tests on this package.
	flogging.SetModuleLevel(pkgLogID, logLevel)
	flogging.SetModuleLevel(saramaLogID, logLevel)
}

func tamperBytes(original []byte) []byte {
	byteCount := len(original)
	return original[:byteCount-1]
}

func channelNameForTest(t *testing.T) string {
	return fmt.Sprintf("%s.channel", strings.Replace(strings.ToLower(t.Name()), "/", ".", -1))
}
<|endoftext|>


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

package compilercommon

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

// NewPositionMapper returns a pointer to a struct that can be used for cached, faster mapping
// of byte positions in input source files to their associated 0-indexed line number and
// column positions. Note that this struct is *not* safe for concurrent access.
func NewPositionMapper() *PositionMapper {
	return &PositionMapper{
		sources: map[InputSource]SourcePositionMapper{},
	}
}

// PositionMapper defines a helper struct for cached, faster lookup of byte position ->
// (line, column) for a set of source files.
type PositionMapper struct {
	// sources maps from a particular input source to its associated mapper.
	sources map[InputSource]SourcePositionMapper
}

// Map returns the line number and column position of the byte position in the given source,
// if any.
func (pm *PositionMapper) Map(source InputSource, bytePosition int) (int, int, error) {
	if existing, ok := pm.sources[source]; ok {
		return existing.Map(bytePosition)
	}

	mapper, err := newSourcePositionMapper(source)
	if err != nil {
		return -1, -1, err
	}

	pm.sources[source] = mapper
	return mapper.Map(bytePosition)
}

// SourcePositionMapper defines a helper struct for cached, faster lookup of byte position ->
// (line, column) for a specific source file.
type SourcePositionMapper struct {
	lineLengths []int // The lengths of each of the lines in the source file.
}

// newSourcePositionMapper creates a new source position mapper for the given source file.
func newSourcePositionMapper(source InputSource) (SourcePositionMapper, error) {
	contents, err := ioutil.ReadFile(string(source))
	if err != nil {
		return SourcePositionMapper{}, err
	}

	return CreateSourcePositionMapper(contents), nil
}

// CreateSourcePositionMapper returns a source position mapper for the contents of a source file.
func CreateSourcePositionMapper(contents []byte) SourcePositionMapper {
	lines := strings.Split(string(contents), "\n")
	lineLengths := make([]int, len(lines))

	for index, line := range lines {
		lineLengths[index] = len(line)
	}

	return SourcePositionMapper{lineLengths}
}

// Map returns the line number and column position of the byte position in the mapper's source,
// if any.
func (spm SourcePositionMapper) Map(bytePosition int) (int, int, error) {
	var currentPosition = 0
	for index, lineLength := range spm.lineLengths {
		nextPosition := currentPosition + lineLength + 1
		if bytePosition < nextPosition {
			return index, bytePosition - currentPosition, nil
		}

		if bytePosition == nextPosition {
			return index + 1, 0, nil
		}

		currentPosition = nextPosition
	}

	return -1, -1, fmt.Errorf("Unknown position %v in source file", bytePosition)
}
<commit_msg>Change position mapper to use a range tree<commit_after>// Copyright 2015 The Serulian Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package compilercommon

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/emirpasic/gods/trees/redblacktree"
)

// NewPositionMapper returns a pointer to a struct that can be used for cached, faster mapping
// of byte positions in input source files to their associated 0-indexed line number and
// column positions. Note that this struct is *not* safe for concurrent access.
func NewPositionMapper() *PositionMapper {
	return &PositionMapper{
		sources: map[InputSource]SourcePositionMapper{},
	}
}

// PositionMapper defines a helper struct for cached, faster lookup of byte position ->
// (line, column) for a set of source files.
type PositionMapper struct {
	// sources maps from a particular input source to its associated mapper.
	sources map[InputSource]SourcePositionMapper
}

// Map returns the line number and column position of the byte position in the given source,
// if any.
func (pm *PositionMapper) Map(source InputSource, bytePosition int) (int, int, error) {
	if existing, ok := pm.sources[source]; ok {
		return existing.Map(bytePosition)
	}

	mapper, err := newSourcePositionMapper(source)
	if err != nil {
		return -1, -1, err
	}

	pm.sources[source] = mapper
	return mapper.Map(bytePosition)
}

// SourcePositionMapper defines a helper struct for cached, faster lookup of byte position ->
// (line, column) for a specific source file.
type SourcePositionMapper struct {
	rangeTree *redblacktree.Tree
}

// newSourcePositionMapper creates a new source position mapper for the given source file.
func newSourcePositionMapper(source InputSource) (SourcePositionMapper, error) {
	contents, err := ioutil.ReadFile(string(source))
	if err != nil {
		return SourcePositionMapper{}, err
	}

	return CreateSourcePositionMapper(contents), nil
}

type inclusiveRange struct {
	start int
	end   int
}

type lineAndStart struct {
	lineNumber    int
	startPosition int
}

func inclusiveComparator(a, b interface{}) int {
	i1 := a.(inclusiveRange)
	i2 := b.(inclusiveRange)

	if i1.start >= i2.start && i1.end <= i2.end {
		return 0
	}

	diff := i1.start - i2.start

	if diff < 0 {
		return -1
	}
	if diff > 0 {
		return 1
	}
	return 0
}

// CreateSourcePositionMapper returns a source position mapper for the contents of a source file.
func CreateSourcePositionMapper(contents []byte) SourcePositionMapper {
	lines := strings.Split(string(contents), "\n")
	rangeTree := redblacktree.NewWith(inclusiveComparator)

	var currentStart = 0
	for index, line := range lines {
		lineEnd := currentStart + len(line)
		rangeTree.Put(inclusiveRange{currentStart, lineEnd}, lineAndStart{index, currentStart})
		currentStart = lineEnd + 1
	}

	return SourcePositionMapper{rangeTree}
}

// Map returns the line number and column position of the byte position in the mapper's source,
// if any.
func (spm SourcePositionMapper) Map(bytePosition int) (int, int, error) {
	ls, found := spm.rangeTree.Get(inclusiveRange{bytePosition, bytePosition})
	if !found {
		return -1, -1, fmt.Errorf("Unknown position %v in source file", bytePosition)
	}

	las := ls.(lineAndStart)
	return las.lineNumber, bytePosition - las.startPosition, nil
}
<|endoftext|>


<commit_before>/*
Dependencies:
  go get github.com/alixaxel/pagerank

WARNING: This package is not thread safe, so you cannot use *Bag from many goroutines.
*/
package tldr

import (
	"errors"
	"github.com/alixaxel/pagerank"
	"sort"
	"strings"
	"unicode"
)

type Bag struct {
	BagOfWordsPerSentence [][]string
	OriginalSentences     []string
	Dict                  map[string]int
	Nodes                 []*Node
	Edges                 []*Edge
	Ranks                 []int

	MaxCharacters              int
	Algorithm                  string // "centrality" or "pagerank" or "custom"
	Weighing                   string // "hamming" or "jaccard" or "custom"
	Damping                    float64
	Tolerance                  float64
	Threshold                  float64
	SentencesDistanceThreshold float64

	customAlgorithm func(e []*Edge) []int
	customWeighing  func(src, dst []int) float64

	vectorLength int
}

// The default values of each settings
const (
	VERSION                              = "0.4.4"
	DEFAULT_ALGORITHM                    = "pagerank"
	DEFAULT_WEIGHING                     = "hamming"
	DEFAULT_DAMPING                      = 0.85
	DEFAULT_TOLERANCE                    = 0.0001
	DEFAULT_THRESHOLD                    = 0.001
	DEFAULT_MAX_CHARACTERS               = 0
	DEFAULT_SENTENCES_DISTANCE_THRESHOLD = 0.95
)

// Create new summarizer
func New() *Bag {
	return &Bag{
		MaxCharacters:              DEFAULT_MAX_CHARACTERS,
		Algorithm:                  DEFAULT_ALGORITHM,
		Weighing:                   DEFAULT_WEIGHING,
		Damping:                    DEFAULT_DAMPING,
		Tolerance:                  DEFAULT_TOLERANCE,
		Threshold:                  DEFAULT_THRESHOLD,
		SentencesDistanceThreshold: DEFAULT_SENTENCES_DISTANCE_THRESHOLD,
	}
}

// Set max characters, damping, tolerance, threshold, sentences distance threshold, algorithm, and weighing
func (bag *Bag) Set(m int, d, t, th, sth float64, alg, w string) {
	bag.MaxCharacters = m
	bag.Damping = d
	bag.Tolerance = t
	bag.Threshold = th
	bag.Algorithm = alg
	bag.Weighing = w
	bag.SentencesDistanceThreshold = sth
}

// Useful if you already have your own dictionary (example: from your database)
// Dictionary is a map[string]int where the key is the word and int is the position in vector, starting from 1
func (bag *Bag) SetDictionary(dict map[string]int) {
	bag.Dict = dict
}

func (bag *Bag) SetCustomAlgorithm(f func(e []*Edge) []int) {
	bag.customAlgorithm = f
}

func (bag *Bag) SetCustomWeighing(f func(src, dst []int) float64) {
	bag.customWeighing = f
}

// Summarize the text to num sentences
func (bag *Bag) Summarize(text string, num int) (string, error) {
	if len(text) < 1 {
		return "", nil
	}

	bag.createSentences(text)

	// If user already provide dictionary, pass creating dictionary
	if len(bag.Dict) < 1 {
		bag.createDictionary(text)
	}

	bag.createNodes()
	bag.createEdges()

	switch bag.Algorithm {
	case "centrality":
		bag.centrality()
		break
	case "pagerank":
		bag.pageRank()
		break
	case "custom":
		bag.Ranks = bag.customAlgorithm(bag.Edges)
		break
	default:
		bag.pageRank()
	}

	// if no ranks, return error
	if len(bag.Ranks) == 0 {
		return "", errors.New("Ranks is empty")
	}

	// guard so it won't crash but return only the highest rank sentence
	// if num is invalid
	if num > (len(bag.Ranks)-1) || num < 1 {
		num = 1
	}

	// get only top num of ranks
	idx := bag.Ranks[:num]
	// sort it ascending by how the sentences appeared on the original text
	sort.Ints(idx)
	var res string
	for i, _ := range idx {
		res += (bag.OriginalSentences[idx[i]] + " ")
		res += "\n\n"
	}

	// trim it from spaces
	res = strings.TrimSpace(res)

	// Truncate if it has more than n characters
	// Note this is not bytes length
	if bag.MaxCharacters > 0 {
		// turn into runes
		r := []rune(res)
		// cut
		max := len(r)
		if max > bag.MaxCharacters {
			max = bag.MaxCharacters
		}
		r = r[:max]
		// then turn back to string
		res = string(r)
	}

	return res, nil
}

type Rank struct {
	idx   int
	score float64
}

func (bag *Bag) centrality() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// sort them by weight descending
	sort.Sort(ByWeight(newEdges))
	ReverseEdge(newEdges)
	rankBySrc := make([]int, len(newEdges))
	for i, v := range newEdges {
		rankBySrc[i] = v.src
	}
	// uniq it without disturbing the order
	m := make(map[int]bool)
	var uniq []int
	for _, v := range rankBySrc {
		if m[v] {
			continue
		}
		uniq = append(uniq, v)
		m[v] = true
	}
	bag.Ranks = uniq
}

func (bag *Bag) pageRank() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// then page rank them
	graph := pagerank.NewGraph()
	defer graph.Reset()
	for _, edge := range newEdges {
		graph.Link(uint32(edge.src), uint32(edge.dst), edge.weight)
	}
	var ranks []*Rank
	graph.Rank(bag.Damping, bag.Tolerance, func(sentenceIndex uint32, rank float64) {
		ranks = append(ranks, &Rank{int(sentenceIndex), rank})
	})
	// sort ranks into an array of sentence index, by score descending
	sort.Sort(ByScore(ranks))
	ReverseRank(ranks)
	idx := make([]int, len(ranks))
	for i, v := range ranks {
		idx[i] = v.idx
	}

	bag.Ranks = idx
}

type Edge struct {
	src    int     // index of node
	dst    int     // index of node
	weight float64 // weight of the similarity between two sentences
}

func (bag *Bag) createEdges() {
	for i, src := range bag.Nodes {
		for j, dst := range bag.Nodes {
			// don't compare same node
			if i != j {
				var weight float64
				switch bag.Weighing {
				case "jaccard":
					commonElements := Intersection(src.vector, dst.vector)
					weight = 1.0 - float64(len(commonElements))/((float64(bag.vectorLength)*2)-float64(len(commonElements)))
					break
				case "hamming":
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
					break
				case "custom":
					weight = bag.customWeighing(src.vector, dst.vector)
					break
				default:
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
				}
				edge := &Edge{i, j, weight}
				bag.Edges = append(bag.Edges, edge)
			}
		}
	}
}

type Node struct {
	sentenceIndex int   // index of sentence from the bag
	vector        []int // map of word count in respect with dict, should we use map instead of slice?
	// for example :
	/*
		dict = {
			i : 1
			am : 2
			the : 3
			shit : 4
		}
		str = "I am not shit, you effin shit"
		vector = [1, 1, 0, 2] => [1, 1, 0, 1] because should be binary
	*/
}

func (bag *Bag) createNodes() {
	bag.vectorLength = len(bag.Dict)
	for i, sentence := range bag.BagOfWordsPerSentence {
		// vector length is len(dict)
		vector := make([]int, bag.vectorLength)
		// word for word now
		for _, word := range sentence {
			// check word dict position, if doesn't exist, skip
			if bag.Dict[word] == 0 {
				continue
			}
			// minus 1, because array started from 0 and lowest dict is 1
			pos := bag.Dict[word] - 1
			// set 1 to the position
			vector[pos] = 1
		}
		// vector is now created, put it into the node
		node := &Node{i, vector}
		// node is now completed, put into the bag
		bag.Nodes = append(bag.Nodes, node)
	}
}

func (bag *Bag) createSentences(text string) {
	// trim all spaces
	text = strings.TrimSpace(text)
	// tokenize text as sentences
	// sentence is a group of words separated by whitespaces or punctuation other than !?.
	bag.OriginalSentences = TokenizeSentences(text)

	// from original sentences, explode each sentences into bag of words
	bag.BagOfWordsPerSentence = [][]string{}
	for _, sentence := range bag.OriginalSentences {
		words := strings.Fields(sentence)
		// then sanitize each word
		for i, word := range words {
			words[i] = SanitizeWord(word)
		}
		bag.BagOfWordsPerSentence = append(bag.BagOfWordsPerSentence, words)
	}

	// then uniq it
	UniqSentences(bag.BagOfWordsPerSentence, bag.SentencesDistanceThreshold)
}

func (bag *Bag) createDictionary(text string) {
	// trim all spaces
	text = strings.TrimSpace(text)
	// lowercase the text
	text = strings.ToLower(text)
	// remove all non alphanumerics but spaces
	var prev rune
	text = strings.Map(func(r rune) rune {
		if r == '-' && (unicode.IsDigit(prev) || unicode.IsLetter(prev)) {
			return r
		}
		if !unicode.IsDigit(r) && !unicode.IsLetter(r) && !unicode.IsSpace(r) {
			return -1
		}
		prev = r
		return r
	}, text)
	// turn it into bag of words
	words := strings.Fields(text)
	// turn it into dictionary
	dict := make(map[string]int)
	i := 1
	for _, word := range words {
		if dict[word] == 0 {
			dict[word] = i
			i++
		}
	}
	bag.Dict = dict
}
<commit_msg>Allow OriginalSentences to be passed in, with text==""<commit_after>/*
Dependencies:
  go get github.com/alixaxel/pagerank

WARNING: This package is not thread safe, so you cannot use *Bag from many goroutines.
*/
package tldr

import (
	"errors"
	"sort"
	"strings"
	"unicode"

	"github.com/alixaxel/pagerank"
)

type Bag struct {
	BagOfWordsPerSentence [][]string
	OriginalSentences     []string
	Dict                  map[string]int
	Nodes                 []*Node
	Edges                 []*Edge
	Ranks                 []int

	MaxCharacters              int
	Algorithm                  string // "centrality" or "pagerank" or "custom"
	Weighing                   string // "hamming" or "jaccard" or "custom"
	Damping                    float64
	Tolerance                  float64
	Threshold                  float64
	SentencesDistanceThreshold float64

	customAlgorithm func(e []*Edge) []int
	customWeighing  func(src, dst []int) float64

	vectorLength int
}

// The default values of each settings
const (
	VERSION                              = "0.4.4"
	DEFAULT_ALGORITHM                    = "pagerank"
	DEFAULT_WEIGHING                     = "hamming"
	DEFAULT_DAMPING                      = 0.85
	DEFAULT_TOLERANCE                    = 0.0001
	DEFAULT_THRESHOLD                    = 0.001
	DEFAULT_MAX_CHARACTERS               = 0
	DEFAULT_SENTENCES_DISTANCE_THRESHOLD = 0.95
)

// Create new summarizer
func New() *Bag {
	return &Bag{
		MaxCharacters:              DEFAULT_MAX_CHARACTERS,
		Algorithm:                  DEFAULT_ALGORITHM,
		Weighing:                   DEFAULT_WEIGHING,
		Damping:                    DEFAULT_DAMPING,
		Tolerance:                  DEFAULT_TOLERANCE,
		Threshold:                  DEFAULT_THRESHOLD,
		SentencesDistanceThreshold: DEFAULT_SENTENCES_DISTANCE_THRESHOLD,
	}
}

// Set max characters, damping, tolerance, threshold, sentences distance threshold, algorithm, and weighing
func (bag *Bag) Set(m int, d, t, th, sth float64, alg, w string) {
	bag.MaxCharacters = m
	bag.Damping = d
	bag.Tolerance = t
	bag.Threshold = th
	bag.Algorithm = alg
	bag.Weighing = w
	bag.SentencesDistanceThreshold = sth
}

// Useful if you already have your own dictionary (example: from your database)
// Dictionary is a map[string]int where the key is the word and int is the position in vector, starting from 1
func (bag *Bag) SetDictionary(dict map[string]int) {
	bag.Dict = dict
}

func (bag *Bag) SetCustomAlgorithm(f func(e []*Edge) []int) {
	bag.customAlgorithm = f
}

func (bag *Bag) SetCustomWeighing(f func(src, dst []int) float64) {
	bag.customWeighing = f
}

// Summarize the text to num sentences
func (bag *Bag) Summarize(text string, num int) (string, error) {
	text = strings.TrimSpace(text)
	if len(text) < 1 && len(bag.OriginalSentences) == 0 {
		return "", nil
	}

	bag.createSentences(text) // only actually creates sentences if no OrignalSentences

	// If user already provide dictionary, pass creating dictionary
	if len(bag.Dict) < 1 {
		if text == "" {
			text = strings.TrimSpace(strings.Join(bag.OriginalSentences, " "))
		}
		bag.createDictionary(text)
	}

	bag.createNodes()
	bag.createEdges()

	switch bag.Algorithm {
	case "centrality":
		bag.centrality()
		break
	case "pagerank":
		bag.pageRank()
		break
	case "custom":
		bag.Ranks = bag.customAlgorithm(bag.Edges)
		break
	default:
		bag.pageRank()
	}

	// if no ranks, return error
	if len(bag.Ranks) == 0 {
		return "", errors.New("Ranks is empty")
	}

	// guard so it won't crash but return only the highest rank sentence
	// if num is invalid
	if num > (len(bag.Ranks)-1) || num < 1 {
		num = 1
	}

	// get only top num of ranks
	idx := bag.Ranks[:num]
	// sort it ascending by how the sentences appeared on the original text
	sort.Ints(idx)
	var res string
	for i, _ := range idx {
		res += (bag.OriginalSentences[idx[i]] + " ")
		res += "\n\n"
	}

	// trim it from spaces
	res = strings.TrimSpace(res)

	// Truncate if it has more than n characters
	// Note this is not bytes length
	if bag.MaxCharacters > 0 {
		// turn into runes
		r := []rune(res)
		// cut
		max := len(r)
		if max > bag.MaxCharacters {
			max = bag.MaxCharacters
		}
		r = r[:max]
		// then turn back to string
		res = string(r)
	}

	return res, nil
}

type Rank struct {
	idx   int
	score float64
}

func (bag *Bag) centrality() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// sort them by weight descending
	sort.Sort(ByWeight(newEdges))
	ReverseEdge(newEdges)
	rankBySrc := make([]int, len(newEdges))
	for i, v := range newEdges {
		rankBySrc[i] = v.src
	}
	// uniq it without disturbing the order
	m := make(map[int]bool)
	var uniq []int
	for _, v := range rankBySrc {
		if m[v] {
			continue
		}
		uniq = append(uniq, v)
		m[v] = true
	}
	bag.Ranks = uniq
}

func (bag *Bag) pageRank() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// then page rank them
	graph := pagerank.NewGraph()
	defer graph.Reset()
	for _, edge := range newEdges {
		graph.Link(uint32(edge.src), uint32(edge.dst), edge.weight)
	}
	var ranks []*Rank
	graph.Rank(bag.Damping, bag.Tolerance, func(sentenceIndex uint32, rank float64) {
		ranks = append(ranks, &Rank{int(sentenceIndex), rank})
	})
	// sort ranks into an array of sentence index, by score descending
	sort.Sort(ByScore(ranks))
	ReverseRank(ranks)
	idx := make([]int, len(ranks))
	for i, v := range ranks {
		idx[i] = v.idx
	}

	bag.Ranks = idx
}

type Edge struct {
	src    int     // index of node
	dst    int     // index of node
	weight float64 // weight of the similarity between two sentences
}

func (bag *Bag) createEdges() {
	for i, src := range bag.Nodes {
		for j, dst := range bag.Nodes {
			// don't compare same node
			if i != j {
				var weight float64
				switch bag.Weighing {
				case "jaccard":
					commonElements := Intersection(src.vector, dst.vector)
					weight = 1.0 - float64(len(commonElements))/((float64(bag.vectorLength)*2)-float64(len(commonElements)))
					break
				case "hamming":
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
					break
				case "custom":
					weight = bag.customWeighing(src.vector, dst.vector)
					break
				default:
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
				}
				edge := &Edge{i, j, weight}
				bag.Edges = append(bag.Edges, edge)
			}
		}
	}
}

type Node struct {
	sentenceIndex int   // index of sentence from the bag
	vector        []int // map of word count in respect with dict, should we use map instead of slice?
	// for example :
	/*
		dict = {
			i : 1
			am : 2
			the : 3
			shit : 4
		}
		str = "I am not shit, you effin shit"
		vector = [1, 1, 0, 2] => [1, 1, 0, 1] because should be binary
	*/
}

func (bag *Bag) createNodes() {
	bag.vectorLength = len(bag.Dict)
	for i, sentence := range bag.BagOfWordsPerSentence {
		// vector length is len(dict)
		vector := make([]int, bag.vectorLength)
		// word for word now
		for _, word := range sentence {
			// check word dict position, if doesn't exist, skip
			if bag.Dict[word] == 0 {
				continue
			}
			// minus 1, because array started from 0 and lowest dict is 1
			pos := bag.Dict[word] - 1
			// set 1 to the position
			vector[pos] = 1
		}
		// vector is now created, put it into the node
		node := &Node{i, vector}
		// node is now completed, put into the bag
		bag.Nodes = append(bag.Nodes, node)
	}
}

func (bag *Bag) createSentences(text string) {
	if len(bag.OriginalSentences) == 0 {
		// trim all spaces
		// done by calling func: text = strings.TrimSpace(text)
		// tokenize text as sentences
		// sentence is a group of words separated by whitespaces or punctuation other than !?.
		bag.OriginalSentences = TokenizeSentences(text)
	}

	// from original sentences, explode each sentences into bag of words
	bag.BagOfWordsPerSentence = [][]string{}
	for _, sentence := range bag.OriginalSentences {
		words := strings.Fields(sentence)
		// then sanitize each word
		for i, word := range words {
			words[i] = SanitizeWord(word)
		}
		bag.BagOfWordsPerSentence = append(bag.BagOfWordsPerSentence, words)
	}

	// then uniq it
	UniqSentences(bag.BagOfWordsPerSentence, bag.SentencesDistanceThreshold)
}

func (bag *Bag) createDictionary(text string) {
	// trim all spaces
	// this already done by calling func:	text = strings.TrimSpace(text)
	// lowercase the text
	text = strings.ToLower(text)
	// remove all non alphanumerics but spaces
	var prev rune
	text = strings.Map(func(r rune) rune {
		if r == '-' && (unicode.IsDigit(prev) || unicode.IsLetter(prev)) {
			return r
		}
		if !unicode.IsDigit(r) && !unicode.IsLetter(r) && !unicode.IsSpace(r) {
			return -1
		}
		prev = r
		return r
	}, text)
	// turn it into bag of words
	words := strings.Fields(text)
	// turn it into dictionary
	dict := make(map[string]int)
	i := 1
	for _, word := range words {
		if dict[word] == 0 {
			dict[word] = i
			i++
		}
	}
	bag.Dict = dict
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"golang.org/x/crypto/bcrypt"

	"github.com/dchest/uniuri"
	"github.com/gorilla/mux"
)

const (
	PORT   = ":8080"
	LENGTH = 12
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	ID      int    `json:"id"`
	Title   string `json:"title"`
	Task    string `json:"task"`
	Created string `json:"created"`
	DueDate string `json:"duedate"`
	Email   string `json:"email"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err := db.QueryRow("select name from tasks where name=?", name)
	if err != sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}
func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err := cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	email := cookieValue["email"]
	if email != "" {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err := cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	query, err := db.Prepare("select name, title, task, created, duedate from tasks where email=? order by duedate desc")
	checkErr(err)

	email := getEmail(r)
	rows, err := query.Exec(email)
	b := Page{Tasks: Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]
	p := Page{}
	err := templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		name := genName()

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email)")
		err := query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01 15:12:52"), email)
		checkErr(err)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	todo := vars["id"]

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email := getEmail(r)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("pass")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("pass")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		_, err := db.QueryRow("select email from users where email=?", html.EscapeString(email))
		checkErr(err)
		if err == sql.ErrNoRows {
			query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
			checkErr(err)
			hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
			checkErr(err)

			_, err = query.Exec(html.EscapeString(email), hashedPassword)
			checkErr(err)
			http.Redirect(w, r, "/login", 302)

		}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/todo/add", addHandler)
	router.HandleFunc("/todo/edit/{id}", editHandler)
	router.HandleFunc("/todo/del/{id}", delHandler)

	router.HandleFunc("/todo/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/{id}", userHandler)
	router.HandleFunc("/user/del/{id}", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<commit_msg>Add loggedin check<commit_after>package main

import (
	"database/sql"
	"html"
	"html/template"
	"log"
	"net/http"
	"time"

	"golang.org/x/crypto/bcrypt"

	"github.com/dchest/uniuri"
	"github.com/gorilla/mux"
)

const (
	PORT   = ":8080"
	LENGTH = 12
)

var templates = template.Must(template.ParseFiles("static/index.html", "static/login.html", "static/register.html", "static/todo.html", "static/edit.html", "static/add.html"))

type User struct {
	ID       int
	Email    string
	Password string
}

type Tasks struct {
	ID      int    `json:"id"`
	Title   string `json:"title"`
	Task    string `json:"task"`
	Created string `json:"created"`
	DueDate string `json:"duedate"`
	Email   string `json:"email"`
}

type Page struct {
	Tasks []Tasks `json:"tasks"`
}

func checkErr(err error) {
	if err != nil {
		log.Println(err)
	}
}

func genName() string {
	name := uniuri.NewLen(LENGTH)
	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	_, err := db.QueryRow("select name from tasks where name=?", name)
	if err != sql.ErrNoRows {
		genName()
	}
	checkErr(err)
	return name
}
func loggedIn(r *http.Request) bool {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return false
	}
	err := cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return false
	}
	email := cookieValue["email"]
	if email != "" {
		return false
	}
	return true

}

func getEmail(r *http.Request) (string, error) {
	cookie, err := r.Cookie("session")
	cookieValue := make(map[string]string)
	if err != nil {
		return "", err
	}
	err = cookieHandler.Decode("session", cookie.Value, &cookieValue)
	if err != nil {
		return "", err
	}
	return cookieValue["email"], nil

}

func rootHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}

	db, err := sql.Open("mysql", DATABASE)
	checkErr(err)

	query, err := db.Prepare("select name, title, task, created, duedate from tasks where email=? order by duedate desc")
	checkErr(err)

	email := getEmail(r)
	rows, err := query.Exec(email)
	b := Page{Tasks: Tasks{}}

	for rows.Next() {
		res := Tasks{}
		rows.scan(&res.Name, &res.Title, &res.Task, &res.Created, &res.DueDate)

		b.Tasks = append(b.Tasks, res)
	}

	checkErr(err)

	err = templates.ExecuteTemplate(w, "index.html", &b)
	checkErr(err)

}

func todoHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]
	p := Page{}
	err := templates.ExecuteTemplate(w, "todo.html", &p)
	checkErr(err)

}

func addHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "add.html", "")
		checkErr(err)

	case "POST":
		title := r.FormValue("title")
		task := r.FormValue("task")
		duedate := r.FormValue("duedate")
		name := genName()

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)
		query, err := db.Prepare("insert into tasks(name, title, task, duedate, created, email)")
		err = query.Exec(name, html.EscapeString(title), html.EscapeString(task), html.EscapeString(duedate), time.Now().Format("2016-02-01 15:12:52"), email)
		checkErr(err)

	}

}

func editHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]

}

func delHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]

}

func finishHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	todo := vars["id"]

}

func userHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	vars := mux.Vars(r)
	id := vars["id"]

}

func userDelHandler(w http.ResponseWriter, r *http.Request) {
	if loggedIn(r) != true {
		http.Redirect(w, r, "/login", 302)
	}
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "deluser.html", "")
		checkErr(err)
	case "POST", "DEL":
		pass := r.FormValue("password")

		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		query, err := db.Prepare("delete from users where email=? and password=?")
		checkErr(err)

		email := getEmail(r)
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
		checkErr(err)

		_, err = query.Exec(email, hashedPassword)
		checkErr(err)

	}

}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("pass")
		// open db connection
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()

		// declare variables for database results
		var hashedPassword []byte
		// read hashedPassword, name and level into variables
		err = db.QueryRow("select password from users where email=?", html.EscapeString(email)).Scan(&hashedPassword)
		if err == sql.ErrNoRows {
			http.Redirect(w, r, "/login", 303)
			return
		}
		checkErr(err)

		// compare bcrypt hash to userinput password
		err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password))
		if err == nil {
			// prepare cookie
			value := map[string]string{
				"email": email,
			}
			// encode variables into cookie
			if encoded, err := cookieHandler.Encode("session", value); err == nil {
				cookie := &http.Cookie{
					Name:  "session",
					Value: encoded,
					Path:  "/",
				}
				// set user cookie
				http.SetCookie(w, cookie)
			}
			// Redirect to home page
			http.Redirect(w, r, "/", 302)
		}
		// Redirect to login page
		http.Redirect(w, r, "/login", 302)

	}

}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		err := templates.ExecuteTemplate(w, "login.html", "")
		checkErr(err)
	case "POST":
		email := r.FormValue("email")
		pass := r.FormValue("pass")
		db, err := sql.Open("mysql", DATABASE)
		checkErr(err)

		defer db.Close()
		_, err := db.QueryRow("select email from users where email=?", html.EscapeString(email))
		checkErr(err)
		if err == sql.ErrNoRows {
			query, err := db.Prepare("INSERT into users(email, password) values(?, ?)")
			checkErr(err)
			hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
			checkErr(err)

			_, err = query.Exec(html.EscapeString(email), hashedPassword)
			checkErr(err)
			http.Redirect(w, r, "/login", 302)

		}
		http.Redirect(w, r, "/register", 302)

	}

}

func logoutHandler(w http.ResponseWriter, r *http.Request) {
	cookie := &http.Cookie{
		Name:   "session",
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}
	http.SetCookie(w, cookie)
	http.Redirect(w, r, "/", 301)

}

func resetHandler(w http.ResponseWriter, r *http.Request) {

}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/", rootHandler)

	router.HandleFunc("/todo", todoHandler)
	router.HandleFunc("/todo/{id}", todoHandler)
	router.HandleFunc("/todo/add", addHandler)
	router.HandleFunc("/todo/edit/{id}", editHandler)
	router.HandleFunc("/todo/del/{id}", delHandler)

	router.HandleFunc("/todo/finish/{id}", finishHandler)

	router.HandleFunc("/user", userHandler)
	router.HandleFunc("/user/{id}", userHandler)
	router.HandleFunc("/user/del/{id}", userDelHandler)

	router.HandleFunc("/register", registerHandler)
	router.HandleFunc("/login", loginHandler)
	router.HandleFunc("/logout", logoutHandler)
	router.HandleFunc("/resetpass", resetHandler)
	err := http.ListenAndServe(PORT, router)
	if err != nil {
		log.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package broadcast

import (
	"fmt"

	"github.com/silenceper/wechat/v2/officialaccount/context"
	"github.com/silenceper/wechat/v2/util"
)

const (
	sendURLByTag      = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"
	sendURLByOpenID   = "https://api.weixin.qq.com/cgi-bin/message/mass/send"
	deleteSendURL     = "https://api.weixin.qq.com/cgi-bin/message/mass/delete"
	previewSendURL    = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"
	massStatusSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/get"
	getSpeedSendURL   = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get"
	setSpeedSendURL   = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set"
)

//MsgType 发送消息类型
type MsgType string

const (
	//MsgTypeNews 图文消息
	MsgTypeNews MsgType = "mpnews"
	//MsgTypeText 文本
	MsgTypeText MsgType = "text"
	//MsgTypeVoice 语音/音频
	MsgTypeVoice MsgType = "voice"
	//MsgTypeImage 图片
	MsgTypeImage MsgType = "image"
	//MsgTypeVideo 视频
	MsgTypeVideo MsgType = "mpvideo"
	//MsgTypeWxCard 卡券
	MsgTypeWxCard MsgType = "wxcard"
)

//Broadcast 群发消息
type Broadcast struct {
	*context.Context
	preview bool
}

//NewBroadcast new
func NewBroadcast(ctx *context.Context) *Broadcast {
	return &Broadcast{ctx, false}
}

//User 发送的用户
type User struct {
	TagID  int64
	OpenID []string
}

//Result 群发返回结果
type Result struct {
	util.CommonError
	MsgID     int64  `json:"msg_id"`
	MsgDataID int64  `json:"msg_data_id"`
	MsgStatus string `json:"msg_status"`
}

//SpeedResult 群发速度返回结果
type SpeedResult struct {
	util.CommonError
	Speed     int64 `json:"speed"`
	RealSpeed int64 `json:"realspeed"`
}

//sendRequest 发送请求的数据
type sendRequest struct {
	//根据tag获全部发送
	Filter map[string]interface{} `json:"filter,omitempty"`
	//根据OpenID发送
	ToUser interface{} `json:"touser,omitempty"`
	//发送文本
	Text map[string]interface{} `json:"text,omitempty"`
	//发送图文消息
	Mpnews map[string]interface{} `json:"mpnews,omitempty"`
	//发送语音
	Voice map[string]interface{} `json:"voice,omitempty"`
	//发送图片
	Images *Image `json:"images,omitempty"`
	//发送卡券
	WxCard            map[string]interface{} `json:"wxcard,omitempty"`
	MsgType           MsgType                `json:"msgtype"`
	SendIgnoreReprint int32                  `json:"send_ignore_reprint,omitempty"`
}

//Image 发送图片
type Image struct {
	MediaIDs           []string `json:"media_ids"`
	Recommend          string   `json:"recommend"`
	NeedOpenComment    int32    `json:"need_open_comment"`
	OnlyFansCanComment int32    `json:"only_fans_can_comment"`
}

//SendText 群发文本
//user 为nil，表示全员发送
//&User{TagID:2} 根据tag发送
//&User{OpenID:[]string("xxx","xxx")} 根据openid发送
func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeText,
	}
	req.Text = map[string]interface{}{
		"content": content,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendText")
	return res, err
}

//SendNews 发送图文
func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeNews,
	}
	if ignoreReprint {
		req.SendIgnoreReprint = 1
	}
	req.Mpnews = map[string]interface{}{
		"media_id": mediaID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendNews")
	return res, err
}

//SendVoice 发送语音
func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeVoice,
	}
	req.Voice = map[string]interface{}{
		"media_id": mediaID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendVoice")
	return res, err
}

//SendImage 发送图片
func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeImage,
	}
	req.Images = images
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendImage")
	return res, err
}

//SendVideo 发送视频
func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeVideo,
	}
	req.Voice = map[string]interface{}{
		"media_id":    mediaID,
		"title":       title,
		"description": description,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendVideo")
	return res, err
}

//SendWxCard 发送卡券
func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeWxCard,
	}
	req.WxCard = map[string]interface{}{
		"card_id": cardID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendWxCard")
	return res, err
}

//Delete 删除群发消息
func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return err
	}
	req := map[string]interface{}{
		"msg_id":      msgID,
		"article_idx": articleIDx,
	}
	url := fmt.Sprintf("%s?access_token=%s", deleteSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return err
	}
	return util.DecodeWithCommonError(data, "Delete")
}

// Preview 预览
func (broadcast *Broadcast) Preview() *Broadcast {
	broadcast.preview = true
	return broadcast
}

// GetMassStatus 获取群发状态
func (broadcast *Broadcast) GetMassStatus(msgID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{
		"msg_id": msgID,
	}
	url := fmt.Sprintf("%s?access_token=%s", massStatusSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "GetMassStatus")
	return res, err
}

// GetSpeed 获取群发速度
func (broadcast *Broadcast) GetSpeed() (*SpeedResult, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{}
	url := fmt.Sprintf("%s?access_token=%s", getSpeedSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &SpeedResult{}
	err = util.DecodeWithError(data, res, "GetSpeed")
	return res, err
}

// SetSpeed 设置群发速度
func (broadcast *Broadcast) SetSpeed(speed int) (*SpeedResult, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{
		"speed": speed,
	}
	url := fmt.Sprintf("%s?access_token=%s", setSpeedSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &SpeedResult{}
	err = util.DecodeWithError(data, res, "SetSpeed")
	return res, err
}

func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret *sendRequest, url string) {
	sendURL := ""
	if user == nil {
		req.Filter = map[string]interface{}{
			"is_to_all": true,
		}
		sendURL = sendURLByTag
	} else {
		if broadcast.preview {
			// 预览
			req.ToUser = user.OpenID
			sendURL = previewSendURL
		} else {
			if user.TagID != 0 {
				req.Filter = map[string]interface{}{
					"is_to_all": false,
					"tag_id":    user.TagID,
				}
				sendURL = sendURLByTag
			}
			if len(user.OpenID) != 0 {
				req.ToUser = user.OpenID
				sendURL = sendURLByOpenID
			}
		}
	}
	return req, sendURL
}
<commit_msg>fix: 微信群发预览接口: 只支持单个用户,默认发给第一个 (#392)<commit_after>package broadcast

import (
	"fmt"

	"github.com/silenceper/wechat/v2/officialaccount/context"
	"github.com/silenceper/wechat/v2/util"
)

const (
	sendURLByTag      = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall"
	sendURLByOpenID   = "https://api.weixin.qq.com/cgi-bin/message/mass/send"
	deleteSendURL     = "https://api.weixin.qq.com/cgi-bin/message/mass/delete"
	previewSendURL    = "https://api.weixin.qq.com/cgi-bin/message/mass/preview"
	massStatusSendURL = "https://api.weixin.qq.com/cgi-bin/message/mass/get"
	getSpeedSendURL   = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/get"
	setSpeedSendURL   = "https://api.weixin.qq.com/cgi-bin/message/mass/speed/set"
)

//MsgType 发送消息类型
type MsgType string

const (
	//MsgTypeNews 图文消息
	MsgTypeNews MsgType = "mpnews"
	//MsgTypeText 文本
	MsgTypeText MsgType = "text"
	//MsgTypeVoice 语音/音频
	MsgTypeVoice MsgType = "voice"
	//MsgTypeImage 图片
	MsgTypeImage MsgType = "image"
	//MsgTypeVideo 视频
	MsgTypeVideo MsgType = "mpvideo"
	//MsgTypeWxCard 卡券
	MsgTypeWxCard MsgType = "wxcard"
)

//Broadcast 群发消息
type Broadcast struct {
	*context.Context
	preview bool
}

//NewBroadcast new
func NewBroadcast(ctx *context.Context) *Broadcast {
	return &Broadcast{ctx, false}
}

//User 发送的用户
type User struct {
	TagID  int64
	OpenID []string
}

//Result 群发返回结果
type Result struct {
	util.CommonError
	MsgID     int64  `json:"msg_id"`
	MsgDataID int64  `json:"msg_data_id"`
	MsgStatus string `json:"msg_status"`
}

//SpeedResult 群发速度返回结果
type SpeedResult struct {
	util.CommonError
	Speed     int64 `json:"speed"`
	RealSpeed int64 `json:"realspeed"`
}

//sendRequest 发送请求的数据
type sendRequest struct {
	//根据tag获全部发送
	Filter map[string]interface{} `json:"filter,omitempty"`
	//根据OpenID发送
	ToUser interface{} `json:"touser,omitempty"`
	//发送文本
	Text map[string]interface{} `json:"text,omitempty"`
	//发送图文消息
	Mpnews map[string]interface{} `json:"mpnews,omitempty"`
	//发送语音
	Voice map[string]interface{} `json:"voice,omitempty"`
	//发送图片
	Images *Image `json:"images,omitempty"`
	//发送卡券
	WxCard            map[string]interface{} `json:"wxcard,omitempty"`
	MsgType           MsgType                `json:"msgtype"`
	SendIgnoreReprint int32                  `json:"send_ignore_reprint,omitempty"`
}

//Image 发送图片
type Image struct {
	MediaIDs           []string `json:"media_ids"`
	Recommend          string   `json:"recommend"`
	NeedOpenComment    int32    `json:"need_open_comment"`
	OnlyFansCanComment int32    `json:"only_fans_can_comment"`
}

//SendText 群发文本
//user 为nil，表示全员发送
//&User{TagID:2} 根据tag发送
//&User{OpenID:[]string("xxx","xxx")} 根据openid发送
func (broadcast *Broadcast) SendText(user *User, content string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeText,
	}
	req.Text = map[string]interface{}{
		"content": content,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendText")
	return res, err
}

//SendNews 发送图文
func (broadcast *Broadcast) SendNews(user *User, mediaID string, ignoreReprint bool) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeNews,
	}
	if ignoreReprint {
		req.SendIgnoreReprint = 1
	}
	req.Mpnews = map[string]interface{}{
		"media_id": mediaID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendNews")
	return res, err
}

//SendVoice 发送语音
func (broadcast *Broadcast) SendVoice(user *User, mediaID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeVoice,
	}
	req.Voice = map[string]interface{}{
		"media_id": mediaID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendVoice")
	return res, err
}

//SendImage 发送图片
func (broadcast *Broadcast) SendImage(user *User, images *Image) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeImage,
	}
	req.Images = images
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendImage")
	return res, err
}

//SendVideo 发送视频
func (broadcast *Broadcast) SendVideo(user *User, mediaID string, title, description string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeVideo,
	}
	req.Voice = map[string]interface{}{
		"media_id":    mediaID,
		"title":       title,
		"description": description,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendVideo")
	return res, err
}

//SendWxCard 发送卡券
func (broadcast *Broadcast) SendWxCard(user *User, cardID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := &sendRequest{
		ToUser:  nil,
		MsgType: MsgTypeWxCard,
	}
	req.WxCard = map[string]interface{}{
		"card_id": cardID,
	}
	req, sendURL := broadcast.chooseTagOrOpenID(user, req)
	url := fmt.Sprintf("%s?access_token=%s", sendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "SendWxCard")
	return res, err
}

//Delete 删除群发消息
func (broadcast *Broadcast) Delete(msgID int64, articleIDx int64) error {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return err
	}
	req := map[string]interface{}{
		"msg_id":      msgID,
		"article_idx": articleIDx,
	}
	url := fmt.Sprintf("%s?access_token=%s", deleteSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return err
	}
	return util.DecodeWithCommonError(data, "Delete")
}

// Preview 预览
func (broadcast *Broadcast) Preview() *Broadcast {
	broadcast.preview = true
	return broadcast
}

// GetMassStatus 获取群发状态
func (broadcast *Broadcast) GetMassStatus(msgID string) (*Result, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{
		"msg_id": msgID,
	}
	url := fmt.Sprintf("%s?access_token=%s", massStatusSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &Result{}
	err = util.DecodeWithError(data, res, "GetMassStatus")
	return res, err
}

// GetSpeed 获取群发速度
func (broadcast *Broadcast) GetSpeed() (*SpeedResult, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{}
	url := fmt.Sprintf("%s?access_token=%s", getSpeedSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &SpeedResult{}
	err = util.DecodeWithError(data, res, "GetSpeed")
	return res, err
}

// SetSpeed 设置群发速度
func (broadcast *Broadcast) SetSpeed(speed int) (*SpeedResult, error) {
	ak, err := broadcast.GetAccessToken()
	if err != nil {
		return nil, err
	}
	req := map[string]interface{}{
		"speed": speed,
	}
	url := fmt.Sprintf("%s?access_token=%s", setSpeedSendURL, ak)
	data, err := util.PostJSON(url, req)
	if err != nil {
		return nil, err
	}
	res := &SpeedResult{}
	err = util.DecodeWithError(data, res, "SetSpeed")
	return res, err
}

func (broadcast *Broadcast) chooseTagOrOpenID(user *User, req *sendRequest) (ret *sendRequest, url string) {
	sendURL := ""
	if user == nil {
		req.Filter = map[string]interface{}{
			"is_to_all": true,
		}
		sendURL = sendURLByTag
	} else {
		if broadcast.preview {
			// 预览 默认发给第一个用户
			if len(user.OpenID) != 0 {
				req.ToUser = user.OpenID[0]
				sendURL = previewSendURL
			}
		} else {
			if user.TagID != 0 {
				req.Filter = map[string]interface{}{
					"is_to_all": false,
					"tag_id":    user.TagID,
				}
				sendURL = sendURLByTag
			}
			if len(user.OpenID) != 0 {
				req.ToUser = user.OpenID
				sendURL = sendURLByOpenID
			}
		}
	}
	return req, sendURL
}
<|endoftext|>


<commit_before>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || "set" == resp.Action && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJson, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	_, err = b.Client.Set(servicePath(name, addr), string(attrsJson), HeartbeatIntervalSecs+MissedHearbeatTTL)
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<commit_msg>Fix leader election<commit_after>package agent

import (
	"encoding/json"
	"strings"
	"sync"

	"github.com/coreos/go-etcd/etcd"
)

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, _ := b.getCurrentState(name)
	go func() {
		if response != nil {
			for _, n := range response.Node.Nodes {
				if update := b.responseToUpdate(response, &n); update != nil {
					stream.ch <- update
				}
			}
		}
		stream.ch <- &ServiceUpdate{}
		for resp := range watch {
			if update := b.responseToUpdate(resp, resp.Node); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, node *etcd.Node) *ServiceUpdate {
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(node.Key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || "set" == resp.Action && (resp.PrevNode == nil || node.Value != resp.PrevNode.Value) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(node.Value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:    serviceName,
			Addr:    serviceAddr,
			Online:  true,
			Attrs:   serviceAttrs,
			Created: uint(node.CreatedIndex),
		}
	} else if "delete" == resp.Action || "expire" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJSON, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	attrsString := string(attrsJSON)
	path := servicePath(name, addr)
	ttl := uint64(HeartbeatIntervalSecs + MissedHearbeatTTL)

	_, err = b.Client.Update(path, attrsString, ttl)
	if e, ok := err.(*etcd.EtcdError); ok && e.ErrorCode == 100 {
		// This is a workaround for etcd issue #407: https://github.com/coreos/etcd/issues/407
		// If we just do a Set and don't try to Update first, createdIndex will get incremented
		// on each heartbeat, breaking leader election.
		_, err = b.Client.Set(path, attrsString, ttl)
	}
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<|endoftext|>


<commit_before>package log

// Standard log field names.
const (
	FnTopic          = "topic"
	FnLoggedAt       = "logged_at"
	FnSeverity       = "severity"
	FnUtsname        = "utsname"
	FnMessage        = "message"
	FnSecret         = "secret"
	FnType           = "type"
	FnRequestID      = "request_id"
	FnResponseTime   = "response_time"
	FnRemoteAddress  = "remote_ipaddr"
	FnURL            = "url"
	FnProtocol       = "protocol"
	FnHTTPMethod     = "http_method"
	FnHTTPVersion    = "http_version"
	FnHTTPHost       = "http_host"
	FnHTTPStatusCode = "http_status_code"
	FnHTTPReferer    = "http_referer"
	FnHTTPUserAgent  = "http_user_agent"
	FnRequestSize    = "request_size"
	FnResponseSize   = "response_size"
	FnDomain         = "domain"
	FnService        = "service"
	FnTrackingCookie = "tracking_cookie"
	FnBrowser        = "browser"
	FnServiceSet     = "serviceset"
	FnStartAt        = "start_at"
)

// Severities a.k.a log levels.
const (
	LvCritical = 2
	LvError    = 3
	LvWarn     = 4
	LvInfo     = 6
	LvDebug    = 7
)

const (
	// The maximum length of a formatted log message.
	maxLogSize = 1 << 20

	// The maximum length of topic.
	maxTopicLength = 128
)

const (
	// RFC3339Micro is for time.Time.Format().
	RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
)
<commit_msg>Add FnError field constant.<commit_after>package log

// Standard log field names.
const (
	FnTopic          = "topic"
	FnLoggedAt       = "logged_at"
	FnSeverity       = "severity"
	FnUtsname        = "utsname"
	FnMessage        = "message"
	FnSecret         = "secret"
	FnType           = "type"
	FnRequestID      = "request_id"
	FnResponseTime   = "response_time"
	FnRemoteAddress  = "remote_ipaddr"
	FnURL            = "url"
	FnProtocol       = "protocol"
	FnHTTPMethod     = "http_method"
	FnHTTPVersion    = "http_version"
	FnHTTPHost       = "http_host"
	FnHTTPStatusCode = "http_status_code"
	FnHTTPReferer    = "http_referer"
	FnHTTPUserAgent  = "http_user_agent"
	FnRequestSize    = "request_size"
	FnResponseSize   = "response_size"
	FnDomain         = "domain"
	FnService        = "service"
	FnTrackingCookie = "tracking_cookie"
	FnBrowser        = "browser"
	FnServiceSet     = "serviceset"
	FnStartAt        = "start_at"
	FnError          = "error"
)

// Severities a.k.a log levels.
const (
	LvCritical = 2
	LvError    = 3
	LvWarn     = 4
	LvInfo     = 6
	LvDebug    = 7
)

const (
	// The maximum length of a formatted log message.
	maxLogSize = 1 << 20

	// The maximum length of topic.
	maxTopicLength = 128
)

const (
	// RFC3339Micro is for time.Time.Format().
	RFC3339Micro = "2006-01-02T15:04:05.000000Z07:00"
)
<|endoftext|>


<commit_before>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package testutil

import (
	"crypto/rand"
	"flag"
	"fmt"
	"reflect"
	"regexp"
	"runtime"
	"testing"

	"github.com/op/go-logging"
	"github.com/openblockchain/obc-peer/openchain/util"
	"github.com/spf13/viper"
)

func SetupTestConfig() {
	viper.AddConfigPath(".")
	viper.SetConfigName("test")
	err := viper.ReadInConfig()
	if err != nil { // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}

	var formatter = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} [%{module}] %{shortfunc} [%{shortfile}] -> %{level:.4s} %{id:03x}%{color:reset} %{message}`,
	)
	logging.SetFormatter(formatter)
}

func SetLogLevel(level logging.Level, module string) {
	logging.SetLevel(level, module)
}

func ParseTestParams() []string {
	testParams := flag.String("testParams", "", "Test specific parameters")
	flag.Parse()
	regex, err := regexp.Compile(",(\\s+)?")
	if err != nil {
		panic(fmt.Errorf("err = %s\n", err))
	}
	paramsArray := regex.Split(*testParams, -1)
	return paramsArray
}

func AssertNil(t testing.TB, value interface{}) {
	if !isNil(value) {
		t.Fatalf("Value not nil. value=[%#v]\n %s", value, getCallerInfo())
	}
}

func AssertNotNil(t testing.TB, value interface{}) {
	if isNil(value) {
		t.Fatalf("Values is nil. %s", getCallerInfo())
	}
}

func AssertSame(t testing.TB, actual interface{}, expected interface{}) {
	t.Logf("%s: AssertSame [%#v] and [%#v]", getCallerInfo(), actual, expected)
	if actual != expected {
		t.Fatalf("Values actual=[%#v] and expected=[%#v] do not point to same object. %s", actual, expected, getCallerInfo())
	}
}

func AssertEquals(t testing.TB, actual interface{}, expected interface{}) {
	t.Logf("%s: AssertEquals [%#v] and [%#v]", getCallerInfo(), actual, expected)
	if expected == nil && isNil(actual) {
		return
	}
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("Values are not equal.\n Actual=[%#v], \n Expected=[%#v]\n %s", actual, expected, getCallerInfo())
	}
}

func AssertNotEquals(t testing.TB, actual interface{}, expected interface{}) {
	if reflect.DeepEqual(actual, expected) {
		t.Fatalf("Values are not supposed to be equal. Actual=[%#v], Expected=[%#v]\n %s", actual, expected, getCallerInfo())
	}
}

func AssertError(t testing.TB, err error, message string) {
	if err == nil {
		t.Fatalf("%s\n %s", message, getCallerInfo())
	}
}

func AssertNoError(t testing.TB, err error, message string) {
	if err != nil {
		t.Fatalf("%s - Error: %s\n %s", message, err, getCallerInfo())
	}
}

func AssertContains(t testing.TB, slice interface{}, value interface{}) {
	if reflect.TypeOf(slice).Kind() != reflect.Slice && reflect.TypeOf(slice).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'slice' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(slice), getCallerInfo())
	}

	if !contains(slice, value) {
		t.Fatalf("Expected value [%s] not found in slice %s\n %s", value, slice, getCallerInfo())
	}
}

func AssertContainsAll(t testing.TB, sliceActual interface{}, sliceExpected interface{}) {
	if reflect.TypeOf(sliceActual).Kind() != reflect.Slice && reflect.TypeOf(sliceActual).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'sliceActual' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(sliceActual), getCallerInfo())
	}

	if reflect.TypeOf(sliceExpected).Kind() != reflect.Slice && reflect.TypeOf(sliceExpected).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'sliceExpected' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(sliceExpected), getCallerInfo())
	}

	array := reflect.ValueOf(sliceExpected)
	for i := 0; i < array.Len(); i++ {
		element := array.Index(i).Interface()
		if !contains(sliceActual, element) {
			t.Fatalf("Expected value [%s] not found in slice %s\n %s", element, sliceActual, getCallerInfo())
		}
	}
}

func AssertPanic(t testing.TB, msg string) {
	x := recover()
	if x == nil {
		t.Fatal(msg)
	} else {
		t.Logf("A panic was caught successfully. Actual msg = %s", x)
	}
}

func ComputeCryptoHash(content ...[]byte) []byte {
	return util.ComputeCryptoHash(AppendAll(content...))
}

func AppendAll(content ...[]byte) []byte {
	combinedContent := []byte{}
	for _, b := range content {
		combinedContent = append(combinedContent, b...)
	}
	return combinedContent
}

func GenerateUUID(t *testing.T) string {
	uuid := util.GenerateUUID()
	return uuid
}

func ConstructRandomBytes(t testing.TB, size int) []byte {
	value := make([]byte, size)
	_, err := rand.Read(value)
	if err != nil {
		t.Fatalf("Error while generating random bytes: %s", err)
	}
	return value
}

func contains(slice interface{}, value interface{}) bool {
	array := reflect.ValueOf(slice)
	for i := 0; i < array.Len(); i++ {
		element := array.Index(i).Interface()
		if value == element || reflect.DeepEqual(element, value) {
			return true
		}
	}
	return false
}

func isNil(in interface{}) bool {
	return in == nil || reflect.ValueOf(in).IsNil() || (reflect.TypeOf(in).Kind() == reflect.Slice && reflect.ValueOf(in).Len() == 0)
}

func getCallerInfo() string {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		return "Could not retrieve caller's info"
	}
	return fmt.Sprintf("CallerInfo = [%s:%d]", file, line)
}
<commit_msg>minor changes for perf tests<commit_after>/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package testutil

import (
	"crypto/rand"
	"flag"
	"fmt"
	"reflect"
	"regexp"
	"runtime"
	"strings"
	"testing"

	"github.com/op/go-logging"
	"github.com/openblockchain/obc-peer/openchain/util"
	"github.com/spf13/viper"
)

func SetupTestConfig() {
	viper.AddConfigPath(".")
	viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
	viper.SetConfigName("test")
	err := viper.ReadInConfig()
	if err != nil { // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
	viper.AutomaticEnv()

	var formatter = logging.MustStringFormatter(
		`%{color}%{time:15:04:05.000} [%{module}] %{shortfunc} [%{shortfile}] -> %{level:.4s} %{id:03x}%{color:reset} %{message}`,
	)
	logging.SetFormatter(formatter)
}

func SetLogLevel(level logging.Level, module string) {
	logging.SetLevel(level, module)
}

func ParseTestParams() []string {
	testParams := flag.String("testParams", "", "Test specific parameters")
	flag.Parse()
	regex, err := regexp.Compile(",(\\s+)?")
	if err != nil {
		panic(fmt.Errorf("err = %s\n", err))
	}
	paramsArray := regex.Split(*testParams, -1)
	return paramsArray
}

func AssertNil(t testing.TB, value interface{}) {
	if !isNil(value) {
		t.Fatalf("Value not nil. value=[%#v]\n %s", value, getCallerInfo())
	}
}

func AssertNotNil(t testing.TB, value interface{}) {
	if isNil(value) {
		t.Fatalf("Values is nil. %s", getCallerInfo())
	}
}

func AssertSame(t testing.TB, actual interface{}, expected interface{}) {
	t.Logf("%s: AssertSame [%#v] and [%#v]", getCallerInfo(), actual, expected)
	if actual != expected {
		t.Fatalf("Values actual=[%#v] and expected=[%#v] do not point to same object. %s", actual, expected, getCallerInfo())
	}
}

func AssertEquals(t testing.TB, actual interface{}, expected interface{}) {
	t.Logf("%s: AssertEquals [%#v] and [%#v]", getCallerInfo(), actual, expected)
	if expected == nil && isNil(actual) {
		return
	}
	if !reflect.DeepEqual(actual, expected) {
		t.Fatalf("Values are not equal.\n Actual=[%#v], \n Expected=[%#v]\n %s", actual, expected, getCallerInfo())
	}
}

func AssertNotEquals(t testing.TB, actual interface{}, expected interface{}) {
	if reflect.DeepEqual(actual, expected) {
		t.Fatalf("Values are not supposed to be equal. Actual=[%#v], Expected=[%#v]\n %s", actual, expected, getCallerInfo())
	}
}

func AssertError(t testing.TB, err error, message string) {
	if err == nil {
		t.Fatalf("%s\n %s", message, getCallerInfo())
	}
}

func AssertNoError(t testing.TB, err error, message string) {
	if err != nil {
		t.Fatalf("%s - Error: %s\n %s", message, err, getCallerInfo())
	}
}

func AssertContains(t testing.TB, slice interface{}, value interface{}) {
	if reflect.TypeOf(slice).Kind() != reflect.Slice && reflect.TypeOf(slice).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'slice' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(slice), getCallerInfo())
	}

	if !contains(slice, value) {
		t.Fatalf("Expected value [%s] not found in slice %s\n %s", value, slice, getCallerInfo())
	}
}

func AssertContainsAll(t testing.TB, sliceActual interface{}, sliceExpected interface{}) {
	if reflect.TypeOf(sliceActual).Kind() != reflect.Slice && reflect.TypeOf(sliceActual).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'sliceActual' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(sliceActual), getCallerInfo())
	}

	if reflect.TypeOf(sliceExpected).Kind() != reflect.Slice && reflect.TypeOf(sliceExpected).Kind() != reflect.Array {
		t.Fatalf("Type of argument 'sliceExpected' is expected to be a slice/array, found =[%s]\n %s", reflect.TypeOf(sliceExpected), getCallerInfo())
	}

	array := reflect.ValueOf(sliceExpected)
	for i := 0; i < array.Len(); i++ {
		element := array.Index(i).Interface()
		if !contains(sliceActual, element) {
			t.Fatalf("Expected value [%s] not found in slice %s\n %s", element, sliceActual, getCallerInfo())
		}
	}
}

func AssertPanic(t testing.TB, msg string) {
	x := recover()
	if x == nil {
		t.Fatal(msg)
	} else {
		t.Logf("A panic was caught successfully. Actual msg = %s", x)
	}
}

func ComputeCryptoHash(content ...[]byte) []byte {
	return util.ComputeCryptoHash(AppendAll(content...))
}

func AppendAll(content ...[]byte) []byte {
	combinedContent := []byte{}
	for _, b := range content {
		combinedContent = append(combinedContent, b...)
	}
	return combinedContent
}

func GenerateUUID(t *testing.T) string {
	uuid := util.GenerateUUID()
	return uuid
}

func ConstructRandomBytes(t testing.TB, size int) []byte {
	value := make([]byte, size)
	_, err := rand.Read(value)
	if err != nil {
		t.Fatalf("Error while generating random bytes: %s", err)
	}
	return value
}

func contains(slice interface{}, value interface{}) bool {
	array := reflect.ValueOf(slice)
	for i := 0; i < array.Len(); i++ {
		element := array.Index(i).Interface()
		if value == element || reflect.DeepEqual(element, value) {
			return true
		}
	}
	return false
}

func isNil(in interface{}) bool {
	return in == nil || reflect.ValueOf(in).IsNil() || (reflect.TypeOf(in).Kind() == reflect.Slice && reflect.ValueOf(in).Len() == 0)
}

func getCallerInfo() string {
	_, file, line, ok := runtime.Caller(2)
	if !ok {
		return "Could not retrieve caller's info"
	}
	return fmt.Sprintf("CallerInfo = [%s:%d]", file, line)
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"
	"testing"
)

func TestParseMetrics(t *testing.T) {
	r, err := calcMetrics("{ sec = 1455448176, usec = 0 } Sun Feb 14 20:09:36 2016\n", 1456242880)
	if err != nil {
		t.Errorf("error should be nil but: %s", err)
	}
	if !reflect.DeepEqual(r, map[string]interface{}{"seconds": float64(794704)}) {
		t.Errorf("something went wrong. failed to parse uptime")
	}
}
<commit_msg>add +build tag to uptime_bsd_test.go<commit_after>// +build freebsd netbsd darwin

package main

import (
	"reflect"
	"testing"
)

func TestParseMetrics(t *testing.T) {
	r, err := calcMetrics("{ sec = 1455448176, usec = 0 } Sun Feb 14 20:09:36 2016\n", 1456242880)
	if err != nil {
		t.Errorf("error should be nil but: %s", err)
	}
	if !reflect.DeepEqual(r, map[string]interface{}{"seconds": float64(794704)}) {
		t.Errorf("something went wrong. failed to parse uptime")
	}
}
<|endoftext|>


<commit_before>package falconPortal

import (
	"fmt"

	"github.com/Cepave/fe/g"
	"github.com/astaxie/beego/orm"
)

func GetEventCases(startTime int64, endTime int64, priority int, status string, limit int, elimit int, username string) (result []EventCases, err error) {
	config := g.Config()
	if limit == 0 || limit > config.FalconPortal.Limit {
		limit = config.FalconPortal.Limit
	}

	isadmin, tplids, err := GetCasePermission(username)
	if tplids == "" {
		tplids = "-1"
	}
	q := orm.NewOrm()
	q.Using("falcon_portal")
	flag := false
	queryTmp := ""
	if startTime != 0 && endTime != 0 {
		flag = true
		queryTmp = fmt.Sprintf(" %v update_at >= %d and  update_at <= %d", queryTmp, startTime, endTime)
	}
	if priority != -1 {
		if flag {
			queryTmp = fmt.Sprintf("%v and priority = %d", queryTmp, priority)
		} else {
			flag = true
			queryTmp = fmt.Sprintf("%v priority = %d", queryTmp, priority)
		}
	}
	if status != "ALL" {
		if flag {
			queryTmp = fmt.Sprintf("%v and (status = '%s' or status = 'OK')", queryTmp, status)
		} else {
			flag = true
			queryTmp = fmt.Sprintf("%v (status = '%s' or status = 'OK')", queryTmp, status)
		}
	}
	if queryTmp != "" && !isadmin {
		_, err = q.Raw(fmt.Sprintf("SELECT * FROM `event_cases` WHERE (tpl_creator = '%s' OR template_id in (%s)) AND %v order by update_at DESC limit %d", username, tplids, queryTmp, limit)).QueryRows(&result)
	} else {
		_, err = q.Raw(fmt.Sprintf("SELECT * FROM `event_cases` WHERE %v order by update_at DESC limit %d", queryTmp, limit)).QueryRows(&result)
	}

	if len(result) == 0 {
		result = []EventCases{}
	} else {
		var eventLimit int
		if eventLimit = elimit; elimit == 0 {
			eventLimit = 10
		}
		for indx, event := range result {
			var eventArr []*Events
			q.Raw(fmt.Sprintf("SELECT * FROM `events` WHERE event_caseId = '%s' order by timestamp DESC Limit %d", event.Id, eventLimit)).QueryRows(&eventArr)
			if len(eventArr) != 0 {
				event.Events = eventArr
			} else {
				event.Events = []*Events{}
			}
			result[indx] = event
		}
	}
	return
}

func GetEvents(startTime int64, endTime int64, limit int) (result []EventsRsp, err error) {
	config := g.Config()
	if limit == 0 || limit > config.FalconPortal.Limit {
		limit = config.FalconPortal.Limit
	}

	q := orm.NewOrm()
	q.Using("falcon_portal")
	queryTmp := ""
	if startTime != 0 && endTime != 0 {
		queryTmp = fmt.Sprintf(" %v events.timestamp >= %d and  events.timestamp <= %d", queryTmp, startTime, endTime)
	}
	if queryTmp != "" {
		_, err = q.Raw(fmt.Sprintf(`SELECT events.id as id,
					events.step as step,
					events.cond as cond,
					events.timestamp as timestamp,
					events.event_caseId as eid,
					event_cases.tpl_creator as tpl_creator,
					event_cases.metric as metric,
					event_cases.endpoint as endpoint
					FROM events LEFT JOIN event_cases on event_cases.id = events.event_caseId
					WHERE %v ORDER BY events.timestamp DESC limit %d`, queryTmp, limit)).QueryRows(&result)
	} else {
		_, err = q.Raw(fmt.Sprintf(`SELECT
					events.id as id,
					events.step as step,
					events.cond as cond,
					events.timestamp as timestamp,
					events.event_caseId as eid,
					event_cases.tpl_creator as tpl_creator,
					event_cases.metric as metric,
					event_cases.endpoint as endpoint
					FROM events LEFT JOIN event_cases on event_cases.id = events.event_caseId
					ORDER BY events.timestamp DESC limit %d`, limit)).QueryRows(&result)
	}
	if len(result) == 0 {
		result = []EventsRsp{}
	}
	return
}

func CountNumOfTlp() (c int, err error) {
	var h []Tpl
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw("select * from `tpl`").QueryRows(&h)
	c = len(h)
	return
}

func GetNotes(event_caseId string, limit int) (enotes []EventNote, err error) {
	if limit == 0 {
		limit = 10
	}
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw(fmt.Sprintf("select * from event_note where event_caseId = '%s' ORDER BY timestamp DESC limit %d", event_caseId, limit)).QueryRows(&enotes)
	return
}
<commit_msg>supoort mutiple status query for event_cases query<commit_after>package falconPortal

import (
	"fmt"

	"strings"

	"github.com/Cepave/fe/g"
	"github.com/astaxie/beego/orm"
)

func GetEventCases(startTime int64, endTime int64, priority int, status string, limit int, elimit int, username string) (result []EventCases, err error) {
	config := g.Config()
	if limit == 0 || limit > config.FalconPortal.Limit {
		limit = config.FalconPortal.Limit
	}

	isadmin, tplids, err := GetCasePermission(username)
	if tplids == "" {
		tplids = "-1"
	}
	q := orm.NewOrm()
	q.Using("falcon_portal")
	flag := false
	queryTmp := ""
	if startTime != 0 && endTime != 0 {
		flag = true
		queryTmp = fmt.Sprintf(" %v update_at >= %d and  update_at <= %d", queryTmp, startTime, endTime)
	}
	if priority != -1 {
		if flag {
			queryTmp = fmt.Sprintf("%v and priority = %d", queryTmp, priority)
		} else {
			flag = true
			queryTmp = fmt.Sprintf("%v priority = %d", queryTmp, priority)
		}
	}
	if status == "DEFAULT" {
		if flag {
			queryTmp = fmt.Sprintf("%v and (status = '%s' or status = 'OK')", queryTmp, status)
		} else {
			flag = true
			queryTmp = fmt.Sprintf("%v (status = '%s' or status = 'OK')", queryTmp, status)
		}
	} else if status != "ALL" {
		//support mutiple status qeuery.
		if flag {
			queryTmp = fmt.Sprintf("%v and ", queryTmp)
		}
		flag = true
		queryTmp = fmt.Sprintf("%v (", queryTmp)
		flag = true
		status_list := strings.Split(status, ",")
		for idx, s := range status_list {
			if idx == len(status_list)-1 {
				queryTmp = fmt.Sprintf("%v status = '%s'", queryTmp, s)
			} else {
				queryTmp = fmt.Sprintf("%v status = '%s' or", queryTmp, s)
			}
		}
		queryTmp = fmt.Sprintf("%v )", queryTmp)
	}
	if queryTmp != "" && !isadmin {
		_, err = q.Raw(fmt.Sprintf("SELECT * FROM `event_cases` WHERE (tpl_creator = '%s' OR template_id in (%s)) AND %v order by update_at DESC limit %d", username, tplids, queryTmp, limit)).QueryRows(&result)
	} else {
		_, err = q.Raw(fmt.Sprintf("SELECT * FROM `event_cases` WHERE %v order by update_at DESC limit %d", queryTmp, limit)).QueryRows(&result)
	}

	if len(result) == 0 {
		result = []EventCases{}
	} else {
		var eventLimit int
		if eventLimit = elimit; elimit == 0 {
			eventLimit = 10
		}
		for indx, event := range result {
			var eventArr []*Events
			q.Raw(fmt.Sprintf("SELECT * FROM `events` WHERE event_caseId = '%s' order by timestamp DESC Limit %d", event.Id, eventLimit)).QueryRows(&eventArr)
			if len(eventArr) != 0 {
				event.Events = eventArr
			} else {
				event.Events = []*Events{}
			}
			result[indx] = event
		}
	}
	return
}

func GetEvents(startTime int64, endTime int64, limit int) (result []EventsRsp, err error) {
	config := g.Config()
	if limit == 0 || limit > config.FalconPortal.Limit {
		limit = config.FalconPortal.Limit
	}

	q := orm.NewOrm()
	q.Using("falcon_portal")
	queryTmp := ""
	if startTime != 0 && endTime != 0 {
		queryTmp = fmt.Sprintf(" %v events.timestamp >= %d and  events.timestamp <= %d", queryTmp, startTime, endTime)
	}
	if queryTmp != "" {
		_, err = q.Raw(fmt.Sprintf(`SELECT events.id as id,
					events.step as step,
					events.cond as cond,
					events.timestamp as timestamp,
					events.event_caseId as eid,
					event_cases.tpl_creator as tpl_creator,
					event_cases.metric as metric,
					event_cases.endpoint as endpoint
					FROM events LEFT JOIN event_cases on event_cases.id = events.event_caseId
					WHERE %v ORDER BY events.timestamp DESC limit %d`, queryTmp, limit)).QueryRows(&result)
	} else {
		_, err = q.Raw(fmt.Sprintf(`SELECT
					events.id as id,
					events.step as step,
					events.cond as cond,
					events.timestamp as timestamp,
					events.event_caseId as eid,
					event_cases.tpl_creator as tpl_creator,
					event_cases.metric as metric,
					event_cases.endpoint as endpoint
					FROM events LEFT JOIN event_cases on event_cases.id = events.event_caseId
					ORDER BY events.timestamp DESC limit %d`, limit)).QueryRows(&result)
	}
	if len(result) == 0 {
		result = []EventsRsp{}
	}
	return
}

func CountNumOfTlp() (c int, err error) {
	var h []Tpl
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw("select * from `tpl`").QueryRows(&h)
	c = len(h)
	return
}

func GetNotes(event_caseId string, limit int) (enotes []EventNote, err error) {
	if limit == 0 {
		limit = 10
	}
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw(fmt.Sprintf("select * from event_note where event_caseId = '%s' ORDER BY timestamp DESC limit %d", event_caseId, limit)).QueryRows(&enotes)
	return
}
<|endoftext|>