<commit_before>package logbuf

import (
	"bufio"
	"container/ring"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"syscall"
	"time"
)

const (
	dirPerms  = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP
	filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP

	timeLayout = "2006-01-02:15:04:05.999"
)

func newLogBuffer(length uint, dirname string, quota uint64) *LogBuffer {
	logBuffer := &LogBuffer{
		buffer: ring.New(int(length)),
		logDir: dirname,
		quota:  quota}
	if err := logBuffer.setupFileLogging(); err != nil {
		fmt.Fprintln(logBuffer, err)
	}
	logBuffer.addHttpHandlers()
	return logBuffer
}

func (lb *LogBuffer) setupFileLogging() error {
	if lb.logDir == "" {
		return nil
	}
	if err := lb.createLogDirectory(); err != nil {
		return err
	}
	writeNotifier := make(chan struct{}, 1)
	lb.writeNotifier = writeNotifier
	go lb.flushWhenIdle(writeNotifier)
	return nil
}

func (lb *LogBuffer) createLogDirectory() error {
	if fi, err := os.Stat(lb.logDir); err != nil {
		if err := os.Mkdir(lb.logDir, dirPerms); err != nil {
			return fmt.Errorf("error creating: %s: %s", lb.logDir, err)
		}
		fi, err = os.Stat(lb.logDir)
	} else if !fi.IsDir() {
		return errors.New(lb.logDir + ": is not a directory")
	}
	return lb.enforceQuota()
}

func (lb *LogBuffer) dump(writer io.Writer, prefix, postfix string,
	recentFirst bool) error {
	entries := lb.getEntries()
	if recentFirst {
		reverseEntries(entries)
	}
	for _, entry := range entries {
		writer.Write([]byte(prefix))
		writer.Write(entry)
		writer.Write([]byte(postfix))
	}
	return nil
}

func (lb *LogBuffer) flush() error {
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	if lb.writer != nil {
		return lb.writer.Flush()
	}
	return nil
}

func (lb *LogBuffer) write(p []byte) (n int, err error) {
	if *alsoLogToStderr {
		os.Stderr.Write(p)
	}
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	lb.writeToLogFile(p)
	val := make([]byte, len(p))
	copy(val, p)
	lb.buffer.Value = val
	lb.buffer = lb.buffer.Next()
	return len(p), nil
}

// This should be called with the lock held.
func (lb *LogBuffer) writeToLogFile(p []byte) {
	if lb.writer == nil {
		return
	}
	lb.writer.Write(p)
	lb.writeNotifier <- struct{}{}
	lb.usage += uint64(len(p))
	if lb.usage <= lb.quota {
		return
	}
	lb.enforceQuota()
}

// This should be called with the lock held.
func (lb *LogBuffer) enforceQuota() error {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return err
	}
	sort.Strings(names)
	var usage uint64
	deletedLatestFile := false
	deleteRemainingFiles := false
	latestFile := true
	for index := len(names) - 1; index >= 0; index-- {
		filename := path.Join(lb.logDir, names[index])
		fi, err := os.Lstat(filename)
		if err == os.ErrNotExist {
			continue
		}
		if err != nil {
			return err
		}
		if fi.Mode().IsRegular() {
			size := uint64(fi.Size())
			if size < lb.quota>>10 {
				size = lb.quota >> 10 // Limit number of files to 1024.
			}
			if size+usage > lb.quota || deleteRemainingFiles {
				os.Remove(filename)
				deleteRemainingFiles = true
				if latestFile {
					deletedLatestFile = true
				}
			} else {
				usage += size
			}
			latestFile = false
		}
	}
	lb.usage = usage
	if deletedLatestFile && lb.file != nil {
		lb.writer.Flush()
		lb.writer = nil
		lb.file.Close()
		lb.file = nil
	}
	if lb.file == nil {
		filename := time.Now().Format(timeLayout)
		file, err := os.OpenFile(path.Join(lb.logDir, filename),
			os.O_CREATE|os.O_WRONLY, filePerms)
		if err != nil {
			return err
		}
		if !*alsoLogToStderr {
			syscall.Dup2(int(file.Fd()), int(os.Stdout.Fd()))
			syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd()))
		}
		lb.file = file
		lb.writer = bufio.NewWriter(file)
		symlink := path.Join(lb.logDir, "latest")
		tmpSymlink := symlink + "~"
		os.Remove(tmpSymlink)
		os.Symlink(filename, tmpSymlink)
		os.Rename(tmpSymlink, symlink)
	}
	return nil
}

func (lb *LogBuffer) flushWhenIdle(writeNotifier <-chan struct{}) {
	flushTimer := time.NewTimer(time.Second)
	idleMarkDuration := *idleMarkTimeout
	if idleMarkDuration < 1 {
		idleMarkDuration = time.Hour * 24 * 365 * 280 // Far in the future.
	}
	idleMarkTimer := time.NewTimer(idleMarkDuration)
	for {
		select {
		case <-writeNotifier:
			flushTimer.Reset(time.Second)
			idleMarkTimer.Reset(idleMarkDuration)
		case <-flushTimer.C:
			lb.flush()
		case <-idleMarkTimer.C:
			lb.writeMark()
			flushTimer.Reset(time.Second)
			idleMarkTimer.Reset(idleMarkDuration)
		}
	}
}

func (lb *LogBuffer) getEntries() [][]byte {
	lb.rwMutex.RLock()
	defer lb.rwMutex.RUnlock()
	entries := make([][]byte, 0, lb.buffer.Len())
	lb.buffer.Do(func(p interface{}) {
		if p != nil {
			entries = append(entries, p.([]byte))
		}
	})
	return entries
}

func (lb *LogBuffer) dumpSince(writer io.Writer, name string,
	earliestTime time.Time, prefix, postfix string, recentFirst bool) error {
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		return err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	lines := make([]string, 0)
	timeFormat := "2006/01/02 15:04:05"
	minLength := len(timeFormat) + 2
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) < minLength {
			continue
		}
		timeString := line[:minLength-2]
		timeStamp, err := time.ParseInLocation(timeFormat, timeString,
			time.Local)
		if err == nil && timeStamp.Before(earliestTime) {
			continue
		}
		if recentFirst {
			lines = append(lines, line)
		} else {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	if recentFirst {
		reverseStrings(lines)
		for _, line := range lines {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	return nil
}

func (lb *LogBuffer) writeMark() {
	now := time.Now()
	year, month, day := now.Date()
	hour, minute, second := now.Clock()
	str := fmt.Sprintf("%d/%02d/%02d %02d:%02d:%02d MARK\n",
		year, month, day, hour, minute, second)
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	lb.writeToLogFile([]byte(str))
}

func reverseEntries(entries [][]byte) {
	length := len(entries)
	for index := 0; index < length/2; index++ {
		entries[index], entries[length-1-index] =
			entries[length-1-index], entries[index]
	}
}

func reverseStrings(strings []string) {
	length := len(strings)
	for index := 0; index < length/2; index++ {
		strings[index], strings[length-1-index] =
			strings[length-1-index], strings[index]
	}
}
<commit_msg>Fix occasional deadlock in lib/logbuf: write to channel with lock held.<commit_after>package logbuf

import (
	"bufio"
	"container/ring"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"sort"
	"syscall"
	"time"
)

const (
	dirPerms  = syscall.S_IRWXU | syscall.S_IRGRP | syscall.S_IXGRP
	filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP

	timeLayout = "2006-01-02:15:04:05.999"
)

func newLogBuffer(length uint, dirname string, quota uint64) *LogBuffer {
	logBuffer := &LogBuffer{
		buffer: ring.New(int(length)),
		logDir: dirname,
		quota:  quota}
	if err := logBuffer.setupFileLogging(); err != nil {
		fmt.Fprintln(logBuffer, err)
	}
	logBuffer.addHttpHandlers()
	return logBuffer
}

func (lb *LogBuffer) setupFileLogging() error {
	if lb.logDir == "" {
		return nil
	}
	if err := lb.createLogDirectory(); err != nil {
		return err
	}
	writeNotifier := make(chan struct{}, 1)
	lb.writeNotifier = writeNotifier
	go lb.flushWhenIdle(writeNotifier)
	return nil
}

func (lb *LogBuffer) createLogDirectory() error {
	if fi, err := os.Stat(lb.logDir); err != nil {
		if err := os.Mkdir(lb.logDir, dirPerms); err != nil {
			return fmt.Errorf("error creating: %s: %s", lb.logDir, err)
		}
		fi, err = os.Stat(lb.logDir)
	} else if !fi.IsDir() {
		return errors.New(lb.logDir + ": is not a directory")
	}
	return lb.enforceQuota()
}

func (lb *LogBuffer) dump(writer io.Writer, prefix, postfix string,
	recentFirst bool) error {
	entries := lb.getEntries()
	if recentFirst {
		reverseEntries(entries)
	}
	for _, entry := range entries {
		writer.Write([]byte(prefix))
		writer.Write(entry)
		writer.Write([]byte(postfix))
	}
	return nil
}

func (lb *LogBuffer) flush() error {
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	if lb.writer != nil {
		return lb.writer.Flush()
	}
	return nil
}

func (lb *LogBuffer) write(p []byte) (n int, err error) {
	if *alsoLogToStderr {
		os.Stderr.Write(p)
	}
	val := make([]byte, len(p))
	copy(val, p)
	lb.rwMutex.Lock()
	sendNotify := lb.writeToLogFile(p)
	lb.buffer.Value = val
	lb.buffer = lb.buffer.Next()
	lb.rwMutex.Unlock()
	if sendNotify {
		lb.writeNotifier <- struct{}{}
	}
	return len(p), nil
}

// This should be called with the lock held.
func (lb *LogBuffer) writeToLogFile(p []byte) bool {
	if lb.writer == nil {
		return false
	}
	lb.writer.Write(p)
	lb.usage += uint64(len(p))
	if lb.usage <= lb.quota {
		return true
	}
	lb.enforceQuota()
	return true
}

// This should be called with the lock held.
func (lb *LogBuffer) enforceQuota() error {
	file, err := os.Open(lb.logDir)
	if err != nil {
		return err
	}
	names, err := file.Readdirnames(-1)
	file.Close()
	if err != nil {
		return err
	}
	sort.Strings(names)
	var usage uint64
	deletedLatestFile := false
	deleteRemainingFiles := false
	latestFile := true
	for index := len(names) - 1; index >= 0; index-- {
		filename := path.Join(lb.logDir, names[index])
		fi, err := os.Lstat(filename)
		if err == os.ErrNotExist {
			continue
		}
		if err != nil {
			return err
		}
		if fi.Mode().IsRegular() {
			size := uint64(fi.Size())
			if size < lb.quota>>10 {
				size = lb.quota >> 10 // Limit number of files to 1024.
			}
			if size+usage > lb.quota || deleteRemainingFiles {
				os.Remove(filename)
				deleteRemainingFiles = true
				if latestFile {
					deletedLatestFile = true
				}
			} else {
				usage += size
			}
			latestFile = false
		}
	}
	lb.usage = usage
	if deletedLatestFile && lb.file != nil {
		lb.writer.Flush()
		lb.writer = nil
		lb.file.Close()
		lb.file = nil
	}
	if lb.file == nil {
		filename := time.Now().Format(timeLayout)
		file, err := os.OpenFile(path.Join(lb.logDir, filename),
			os.O_CREATE|os.O_WRONLY, filePerms)
		if err != nil {
			return err
		}
		if !*alsoLogToStderr {
			syscall.Dup2(int(file.Fd()), int(os.Stdout.Fd()))
			syscall.Dup2(int(file.Fd()), int(os.Stderr.Fd()))
		}
		lb.file = file
		lb.writer = bufio.NewWriter(file)
		symlink := path.Join(lb.logDir, "latest")
		tmpSymlink := symlink + "~"
		os.Remove(tmpSymlink)
		os.Symlink(filename, tmpSymlink)
		os.Rename(tmpSymlink, symlink)
	}
	return nil
}

func (lb *LogBuffer) flushWhenIdle(writeNotifier <-chan struct{}) {
	flushTimer := time.NewTimer(time.Second)
	idleMarkDuration := *idleMarkTimeout
	if idleMarkDuration < 1 {
		idleMarkDuration = time.Hour * 24 * 365 * 280 // Far in the future.
	}
	idleMarkTimer := time.NewTimer(idleMarkDuration)
	for {
		select {
		case <-writeNotifier:
			flushTimer.Reset(time.Second)
			idleMarkTimer.Reset(idleMarkDuration)
		case <-flushTimer.C:
			lb.flush()
		case <-idleMarkTimer.C:
			lb.writeMark()
			flushTimer.Reset(time.Second)
			idleMarkTimer.Reset(idleMarkDuration)
		}
	}
}

func (lb *LogBuffer) getEntries() [][]byte {
	lb.rwMutex.RLock()
	defer lb.rwMutex.RUnlock()
	entries := make([][]byte, 0, lb.buffer.Len())
	lb.buffer.Do(func(p interface{}) {
		if p != nil {
			entries = append(entries, p.([]byte))
		}
	})
	return entries
}

func (lb *LogBuffer) dumpSince(writer io.Writer, name string,
	earliestTime time.Time, prefix, postfix string, recentFirst bool) error {
	file, err := os.Open(path.Join(lb.logDir, path.Base(path.Clean(name))))
	if err != nil {
		return err
	}
	defer file.Close()
	scanner := bufio.NewScanner(file)
	lines := make([]string, 0)
	timeFormat := "2006/01/02 15:04:05"
	minLength := len(timeFormat) + 2
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) < minLength {
			continue
		}
		timeString := line[:minLength-2]
		timeStamp, err := time.ParseInLocation(timeFormat, timeString,
			time.Local)
		if err == nil && timeStamp.Before(earliestTime) {
			continue
		}
		if recentFirst {
			lines = append(lines, line)
		} else {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	if err := scanner.Err(); err != nil {
		return err
	}
	if recentFirst {
		reverseStrings(lines)
		for _, line := range lines {
			writer.Write([]byte(prefix))
			writer.Write([]byte(line))
			writer.Write([]byte(postfix))
		}
	}
	return nil
}

func (lb *LogBuffer) writeMark() {
	now := time.Now()
	year, month, day := now.Date()
	hour, minute, second := now.Clock()
	str := fmt.Sprintf("%d/%02d/%02d %02d:%02d:%02d MARK\n",
		year, month, day, hour, minute, second)
	lb.rwMutex.Lock()
	defer lb.rwMutex.Unlock()
	lb.writeToLogFile([]byte(str))
}

func reverseEntries(entries [][]byte) {
	length := len(entries)
	for index := 0; index < length/2; index++ {
		entries[index], entries[length-1-index] =
			entries[length-1-index], entries[index]
	}
}

func reverseStrings(strings []string) {
	length := len(strings)
	for index := 0; index < length/2; index++ {
		strings[index], strings[length-1-index] =
			strings[length-1-index], strings[index]
	}
}
<|endoftext|>


<commit_before>package module

import (
	"fmt"
	"github.com/davidscholberg/irkbot/lib/configure"
	"github.com/davidscholberg/irkbot/lib/message"
	"io/ioutil"
	"math/rand"
	"os"
	"strings"
	"time"
)

var adjectives []string
var nouns []string

func ConfigSlam(cfg *configure.Config) {
	// initialize word arrays
	adjectiveBytes, err := ioutil.ReadFile(cfg.Modules["slam"]["adjective_file"])
	if err != nil {
		// TODO: use logger here
		fmt.Fprintln(os.Stderr, err)
		return
	}
	nounBytes, err := ioutil.ReadFile(cfg.Modules["slam"]["noun_file"])
	if err != nil {
		// TODO: use logger here
		fmt.Fprintln(os.Stderr, err)
		return
	}
	adjectives = strings.Split(string(adjectiveBytes), "\n")
	nouns = strings.Split(string(nounBytes), "\n")

	// seed rng
	rand.Seed(time.Now().UnixNano())
}

func HelpSlam() []string {
	s := "slam [victim] - give the victim a verbal smackdown (or self if no victim" +
		" specified)"
	return []string{s}
}

func Slam(cfg *configure.Config, in *message.InboundMsg, actions *Actions) {
	if len(adjectives) == 0 || len(nouns) == 0 {
		actions.Say("error: no smackdowns loaded")
		return
	}

	victim := in.Event.Nick
	if len(in.MsgArgs) > 1 {
		victim = strings.TrimSpace(strings.Join(in.MsgArgs[1:], " "))
	}

	response := fmt.Sprintf(
		"%s: u %s %s",
		victim,
		adjectives[rand.Intn(len(adjectives))],
		nouns[rand.Intn(len(nouns))])

	actions.Say(response)
}
<commit_msg>converted slam module data from flat file to sqlite<commit_after>package module

import (
	"fmt"
	"github.com/davidscholberg/irkbot/lib/configure"
	"github.com/davidscholberg/irkbot/lib/message"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/sqlite"
	"math/rand"
	"os"
	"strings"
	"time"
)

type Adjective struct {
	ID   uint   `gorm:"primary_key"`
	Word string `gorm:"unique_index:idx_adjective_word"`
}

type Noun struct {
	ID   uint   `gorm:"primary_key"`
	Word string `gorm:"unique_index:idx_noun_word"`
}

var adjectives []Adjective
var nouns []Noun

func ConfigSlam(cfg *configure.Config) {
	dbFile := cfg.Modules["slam"]["db_file"]

	db, err := gorm.Open("sqlite3", dbFile)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	defer db.Close()

	db.AutoMigrate(&Adjective{})
	db.AutoMigrate(&Noun{})

	db.Find(&adjectives)
	db.Find(&nouns)

	// seed rng
	rand.Seed(time.Now().UnixNano())
}

func HelpSlam() []string {
	s := "slam [victim] - give the victim a verbal smackdown (or self if no victim" +
		" specified)"
	return []string{s}
}

func Slam(cfg *configure.Config, in *message.InboundMsg, actions *Actions) {
	if len(adjectives) == 0 || len(nouns) == 0 {
		actions.Say("error: no smackdowns loaded")
		return
	}

	victim := in.Event.Nick
	if len(in.MsgArgs) > 1 {
		victim = strings.TrimSpace(strings.Join(in.MsgArgs[1:], " "))
	}

	response := fmt.Sprintf(
		"%s: u %s %s",
		victim,
		adjectives[rand.Intn(len(adjectives))].Word,
		nouns[rand.Intn(len(nouns))].Word)

	actions.Say(response)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
)

var jsonText = []byte(`
{
	"id": "foobar",
	"verbose": false,
	"level": 10
}`)

type Options struct {
	Id      *string `json:"id,omitempty"`
	Verbose *bool   `json:"verbose,omitempty"`
	Level   *int    `json:"level,omitempty"`
	Power   *int    `json:"power,omitempty"`
}

func Bool(v bool) *bool { return &v }

func Int(v int) *int { return &v }

func String(v string) *string { return &v }

func main() {
	var opts Options
	if err := json.Unmarshal(jsonText, &opts); err != nil {
		log.Fatal(err)
	}

	if opts.Level != nil {
		fmt.Printf("Level specified: %d\n", *opts.Level)
	}

	if opts.Power != nil {
		fmt.Printf("Power specified: %d\n", *opts.Power)
	}
}
<commit_msg>Show how to set values using helper functions<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
)

var jsonText = []byte(`
{
	"id": "foobar",
	"verbose": false,
	"level": 10
}`)

type Options struct {
	Id      *string `json:"id,omitempty"`
	Verbose *bool   `json:"verbose,omitempty"`
	Level   *int    `json:"level,omitempty"`
	Power   *int    `json:"power,omitempty"`
}

func main() {
	var opts Options
	if err := json.Unmarshal(jsonText, &opts); err != nil {
		log.Fatal(err)
	}

	if opts.Level != nil {
		fmt.Printf("Level specified: %d\n", *opts.Level)
	}

	if opts.Power != nil {
		fmt.Printf("Power specified: %d\n", *opts.Power)
	}

	opts2 := Options{
		Id:    String("joe"),
		Power: Int(100),
	}

	jsonOut, _ := json.MarshalIndent(opts2, "", "  ")
	fmt.Println(string(jsonOut))
}

func Bool(v bool) *bool       { return &v }
func Int(v int) *int          { return &v }
func String(v string) *string { return &v }
<|endoftext|>


<commit_before>package dna

import "testing"

func (h Histogram) Equal(o Histogram) bool {
	return h.sameLength(o) && h.sameMappings(o)
}

func (h Histogram) sameLength(o Histogram) bool {
	return len(h) == len(o)
}

func (h Histogram) sameMappings(o Histogram) (res bool) {
	res = true
	for k, _ := range h {
		if h[k] != o[k] {
			res = false
		}
	}
	return
}

func assertEqual(t *testing.T, in int, out int) {
	if in != out {
		t.Errorf("Got \"%v\", expected \"%v\"", out, in)
	}
}

type tallyTest struct {
	Strand    string
	Candidate string
	Expected  int
}

var tallyTests = []tallyTest{
	{"", "A", 0},
	{"CCCCC", "C", 5},
	{"GGGGGTAACCCGG", "T", 1},
}

func TestNucleotideCounts(t *testing.T) {
	for _, test := range tallyTests {
		dna := DNA{test.Strand}
		count, _ := dna.Count(test.Candidate)
		assertEqual(t, test.Expected, count)
	}
}

func TestHasErrorForInvalidNucleotides(t *testing.T) {
	dna := DNA{"GATTACA"}
	count, err := dna.Count("X")
	assertEqual(t, 0, count)
	if err == nil {
		t.Errorf("X is an invalid nucleotide, but no error was raised")
	}
}

// Not sure why this test is here as Count is a query method.
func TestCountingDoesntChangeCount(t *testing.T) {
	dna := DNA{"CGATTGGG"}
	dna.Count("T")
	count, _ := dna.Count("T")
	assertEqual(t, 2, count)
}

type histogramTest struct {
	Strand   string
	Expected Histogram
}

var histogramTests = []histogramTest{
	{
		Strand:   "",
		Expected: Histogram{"A": 0, "C": 0, "T": 0, "G": 0},
	},
	{
		Strand:   "GGGGGGGG",
		Expected: Histogram{"A": 0, "C": 0, "T": 0, "G": 8},
	},
	{
		Strand:   "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC",
		Expected: Histogram{"A": 20, "C": 12, "T": 21, "G": 17},
	},
}

func TestSequenceHistograms(t *testing.T) {
	for _, test := range histogramTests {
		dna := DNA{test.Strand}
		if !dna.Counts().Equal(test.Expected) {
			t.Errorf("DNA{ \"%v\" }: Got \"%v\", expected \"%v\"", test.Strand, dna.Counts(), test.Expected)
		}
	}
}
<commit_msg>Update go:nucleotide-count test suite<commit_after>package dna

import "testing"

func (h Histogram) Equal(o Histogram) bool {
	return h.sameLength(o) && h.sameMappings(o)
}

func (h Histogram) sameLength(o Histogram) bool {
	return len(h) == len(o)
}

func (h Histogram) sameMappings(o Histogram) (res bool) {
	res = true
	for k, _ := range h {
		if h[k] != o[k] {
			res = false
		}
	}
	return
}

func assertEqual(t *testing.T, in int, out int) {
	if in != out {
		t.Errorf("Got \"%v\", expected \"%v\"", out, in)
	}
}

type tallyTest struct {
	Strand    string
	Candidate string
	Expected  int
}

var tallyTests = []tallyTest{
	{"", "A", 0},
	{"CCCCC", "C", 5},
	{"GGGGGTAACCCGG", "T", 1},
}

func TestNucleotideCounts(t *testing.T) {
	for _, test := range tallyTests {
		dna := DNA{test.Strand}
		count, _ := dna.Count(test.Candidate)
		assertEqual(t, test.Expected, count)
	}
}

func TestHasErrorForInvalidNucleotides(t *testing.T) {
	dna := DNA{"GATTACA"}
	count, err := dna.Count("X")
	assertEqual(t, 0, count)
	if err == nil {
		t.Errorf("X is an invalid nucleotide, but no error was raised")
	}
}

func TestUracilIsAValidNucleotide(t *testing.T) {
	dna := DNA{"GATTACA"}
	count, err := dna.Count("U")
	assertEqual(t, 0, count)
	if err != nil {
		t.Errorf("U is a valid nucleotide that never occurs in DNA.")
	}
}

// In most cases, this test is pointless.
// Very occasionally it matters.
// Just roll with it.
func TestCountingDoesntChangeCount(t *testing.T) {
	dna := DNA{"CGATTGGG"}
	dna.Count("T")
	count, _ := dna.Count("T")
	assertEqual(t, 2, count)
}

type histogramTest struct {
	Strand   string
	Expected Histogram
}

var histogramTests = []histogramTest{
	{
		Strand:   "",
		Expected: Histogram{"A": 0, "C": 0, "T": 0, "G": 0},
	},
	{
		Strand:   "GGGGGGGG",
		Expected: Histogram{"A": 0, "C": 0, "T": 0, "G": 8},
	},
	{
		Strand:   "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC",
		Expected: Histogram{"A": 20, "C": 12, "T": 21, "G": 17},
	},
}

func TestSequenceHistograms(t *testing.T) {
	for _, test := range histogramTests {
		dna := DNA{test.Strand}
		if !dna.Counts().Equal(test.Expected) {
			t.Errorf("DNA{ \"%v\" }: Got \"%v\", expected \"%v\"", test.Strand, dna.Counts(), test.Expected)
		}
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/structure"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsCloudWatchDashboard() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCloudWatchDashboardPut,
		Read:   resourceAwsCloudWatchDashboardRead,
		Update: resourceAwsCloudWatchDashboardPut,
		Delete: resourceAwsCloudWatchDashboardDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		// Note that we specify both the `dashboard_body` and
		// the `dashboard_name` as being required, even though
		// according to the REST API documentation both are
		// optional: http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_PutDashboard.html#API_PutDashboard_RequestParameters
		Schema: map[string]*schema.Schema{
			"dashboard_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"dashboard_body": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validation.ValidateJsonString,
				StateFunc: func(v interface{}) string {
					json, _ := structure.NormalizeJsonString(v)
					return json
				},
				DiffSuppressFunc: suppressEquivalentJsonDiffs,
			},
			"dashboard_name": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateCloudWatchDashboardName,
			},
		},
	}
}

func resourceAwsCloudWatchDashboardRead(d *schema.ResourceData, meta interface{}) error {
	dashboardName := d.Get("dashboard_name").(string)
	log.Printf("[DEBUG] Reading CloudWatch Dashboard: %s", dashboardName)
	conn := meta.(*AWSClient).cloudwatchconn

	params := cloudwatch.GetDashboardInput{
		DashboardName: aws.String(d.Id()),
	}

	resp, err := conn.GetDashboard(&params)
	if err != nil {
		if isCloudWatchDashboardNotFoundErr(err) {
			log.Printf("[WARN] CloudWatch Dashboard %q not found, removing", dashboardName)
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Reading dashboard failed: %s", err)
	}

	d.Set("dashboard_arn", resp.DashboardArn)
	d.Set("dashboard_name", resp.DashboardName)
	d.Set("dashboard_body", resp.DashboardBody)
	return nil
}

func resourceAwsCloudWatchDashboardPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatchconn
	params := cloudwatch.PutDashboardInput{
		DashboardBody: aws.String(d.Get("dashboard_body").(string)),
		DashboardName: aws.String(d.Get("dashboard_name").(string)),
	}

	log.Printf("[DEBUG] Putting CloudWatch Dashboard: %#v", params)

	_, err := conn.PutDashboard(&params)
	if err != nil {
		return fmt.Errorf("Putting dashboard failed: %s", err)
	}
	d.SetId(d.Get("dashboard_name").(string))
	log.Println("[INFO] CloudWatch Dashboard put finished")

	return resourceAwsCloudWatchDashboardRead(d, meta)
}

func resourceAwsCloudWatchDashboardDelete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] Deleting CloudWatch Dashboard %s", d.Id())
	conn := meta.(*AWSClient).cloudwatchconn
	params := cloudwatch.DeleteDashboardsInput{
		DashboardNames: []*string{aws.String(d.Id())},
	}

	if _, err := conn.DeleteDashboards(&params); err != nil {
		if isCloudWatchDashboardNotFoundErr(err) {
			return nil
		}
		return fmt.Errorf("Error deleting CloudWatch Dashboard: %s", err)
	}
	log.Printf("[INFO] CloudWatch Dashboard %s deleted", d.Id())

	return nil
}

func isCloudWatchDashboardNotFoundErr(err error) bool {
	return isAWSErr(
		err,
		"ResourceNotFound",
		"does not exist")
}
<commit_msg>Set ForceNew true on dashboard name<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/structure"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsCloudWatchDashboard() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsCloudWatchDashboardPut,
		Read:   resourceAwsCloudWatchDashboardRead,
		Update: resourceAwsCloudWatchDashboardPut,
		Delete: resourceAwsCloudWatchDashboardDelete,

		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		// Note that we specify both the `dashboard_body` and
		// the `dashboard_name` as being required, even though
		// according to the REST API documentation both are
		// optional: http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_PutDashboard.html#API_PutDashboard_RequestParameters
		Schema: map[string]*schema.Schema{
			"dashboard_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"dashboard_body": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validation.ValidateJsonString,
				StateFunc: func(v interface{}) string {
					json, _ := structure.NormalizeJsonString(v)
					return json
				},
				DiffSuppressFunc: suppressEquivalentJsonDiffs,
			},
			"dashboard_name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateCloudWatchDashboardName,
			},
		},
	}
}

func resourceAwsCloudWatchDashboardRead(d *schema.ResourceData, meta interface{}) error {
	dashboardName := d.Get("dashboard_name").(string)
	log.Printf("[DEBUG] Reading CloudWatch Dashboard: %s", dashboardName)
	conn := meta.(*AWSClient).cloudwatchconn

	params := cloudwatch.GetDashboardInput{
		DashboardName: aws.String(d.Id()),
	}

	resp, err := conn.GetDashboard(&params)
	if err != nil {
		if isCloudWatchDashboardNotFoundErr(err) {
			log.Printf("[WARN] CloudWatch Dashboard %q not found, removing", dashboardName)
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Reading dashboard failed: %s", err)
	}

	d.Set("dashboard_arn", resp.DashboardArn)
	d.Set("dashboard_name", resp.DashboardName)
	d.Set("dashboard_body", resp.DashboardBody)
	return nil
}

func resourceAwsCloudWatchDashboardPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).cloudwatchconn
	params := cloudwatch.PutDashboardInput{
		DashboardBody: aws.String(d.Get("dashboard_body").(string)),
		DashboardName: aws.String(d.Get("dashboard_name").(string)),
	}

	log.Printf("[DEBUG] Putting CloudWatch Dashboard: %#v", params)

	_, err := conn.PutDashboard(&params)
	if err != nil {
		return fmt.Errorf("Putting dashboard failed: %s", err)
	}
	d.SetId(d.Get("dashboard_name").(string))
	log.Println("[INFO] CloudWatch Dashboard put finished")

	return resourceAwsCloudWatchDashboardRead(d, meta)
}

func resourceAwsCloudWatchDashboardDelete(d *schema.ResourceData, meta interface{}) error {
	log.Printf("[INFO] Deleting CloudWatch Dashboard %s", d.Id())
	conn := meta.(*AWSClient).cloudwatchconn
	params := cloudwatch.DeleteDashboardsInput{
		DashboardNames: []*string{aws.String(d.Id())},
	}

	if _, err := conn.DeleteDashboards(&params); err != nil {
		if isCloudWatchDashboardNotFoundErr(err) {
			return nil
		}
		return fmt.Errorf("Error deleting CloudWatch Dashboard: %s", err)
	}
	log.Printf("[INFO] CloudWatch Dashboard %s deleted", d.Id())

	return nil
}

func isCloudWatchDashboardNotFoundErr(err error) bool {
	return isAWSErr(
		err,
		"ResourceNotFound",
		"does not exist")
}
<|endoftext|>


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

package dmltest

import (
	"database/sql"
	"io"
	"os"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/pkg/sql/dml"
)

// EnvDSN is the name of the environment variable
const EnvDSN = "CS_DSN"

func getDSN(env string) (string, error) {
	dsn := os.Getenv(env)
	if dsn == "" {
		return "", errors.NotFound.Newf("DSN in environment variable %q not found.", EnvDSN)
	}
	return dsn, nil
}

// MustGetDSN returns the data source name from an environment variable or
// panics on error.
func MustGetDSN(t testing.TB) string {
	d, err := getDSN(EnvDSN)
	if err != nil {
		t.Skip(err)
	}
	return d
}

// MustConnectDB is a helper function that creates a new database connection
// using a DSN from an environment variable found in the constant csdb.EnvDSN.
// If the DSN environment variable has not been set it skips the test.
// Argument t specified usually the *testing.T/B struct.
func MustConnectDB(t testing.TB, opts ...dml.ConnPoolOption) *dml.ConnPool {
	t.Helper()
	if _, err := getDSN(EnvDSN); errors.NotFound.Match(err) {
		t.Skipf("%s", err)
	}
	cfg := []dml.ConnPoolOption{dml.WithDSN(MustGetDSN(t))}
	return dml.MustConnectAndVerify(append(cfg, opts...)...)
}

// Close for usage in conjunction with defer.
// 		defer dmltest.Close(t, db)
func Close(t testing.TB, c io.Closer) {
	t.Helper()
	if err := c.Close(); err != nil {
		t.Errorf("%+v", err)
	}
}

// MockDB creates a mocked database connection. Fatals on error.
func MockDB(t testing.TB, opts ...dml.ConnPoolOption) (*dml.ConnPool, sqlmock.Sqlmock) {
	if t != nil { // t can be nil in Example functions
		t.Helper()
	}
	db, sm, err := sqlmock.New()
	FatalIfError(t, err)
	cfg := []dml.ConnPoolOption{dml.WithDB(db)}
	dbc, err := dml.NewConnPool(append(cfg, opts...)...)
	FatalIfError(t, err)
	return dbc, sm
}

// MockClose for usage in conjunction with defer.
// 		defer dmltest.MockClose(t, db, dbMock)
func MockClose(t testing.TB, c io.Closer, m sqlmock.Sqlmock) {
	if t != nil { // t can be nil in Example functions
		t.Helper()
	}
	m.ExpectClose()
	FatalIfError(t, c.Close())
	FatalIfError(t, m.ExpectationsWereMet())
}

// FatalIfError fails the tests if an unexpected error occurred. If the error is
// gift wrapped, it prints the location. If `t` is nil, this function panics.
func FatalIfError(t testing.TB, err error) {
	if err != nil {
		if t != nil {
			t.Fatalf("%+v", err)
		} else {
			panic(err)
		}
	}
}

// CheckLastInsertID returns a function which accepts the return result from
// Exec*() and returns itself the last_insert_id or emits an error.
func CheckLastInsertID(t interface {
	Errorf(format string, args ...interface{})
}, msg ...string) func(sql.Result, error) int64 {
	return func(res sql.Result, err error) int64 {
		if err != nil {
			if len(msg) == 1 {
				t.Errorf("%q: %+v", msg[0], err)
			} else {
				t.Errorf("%+v", err)
			}
			return 0
		}
		lid, err := res.LastInsertId()
		if err != nil {
			if len(msg) == 1 {
				t.Errorf("%q: %+v", msg[0], err)
			} else {
				t.Errorf("%+v", err)
			}
			return 0
		}
		return lid
	}
}
<commit_msg>sql/dmltest: check CheckLastInsertID that ID is greater than zero<commit_after>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dmltest

import (
	"database/sql"
	"io"
	"os"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/corestoreio/errors"
	"github.com/corestoreio/pkg/sql/dml"
)

// EnvDSN is the name of the environment variable
const EnvDSN = "CS_DSN"

func getDSN(env string) (string, error) {
	dsn := os.Getenv(env)
	if dsn == "" {
		return "", errors.NotFound.Newf("DSN in environment variable %q not found.", EnvDSN)
	}
	return dsn, nil
}

// MustGetDSN returns the data source name from an environment variable or
// panics on error.
func MustGetDSN(t testing.TB) string {
	d, err := getDSN(EnvDSN)
	if err != nil {
		t.Skip(err)
	}
	return d
}

// MustConnectDB is a helper function that creates a new database connection
// using a DSN from an environment variable found in the constant csdb.EnvDSN.
// If the DSN environment variable has not been set it skips the test.
// Argument t specified usually the *testing.T/B struct.
func MustConnectDB(t testing.TB, opts ...dml.ConnPoolOption) *dml.ConnPool {
	t.Helper()
	if _, err := getDSN(EnvDSN); errors.NotFound.Match(err) {
		t.Skipf("%s", err)
	}
	cfg := []dml.ConnPoolOption{dml.WithDSN(MustGetDSN(t))}
	return dml.MustConnectAndVerify(append(cfg, opts...)...)
}

// Close for usage in conjunction with defer.
// 		defer dmltest.Close(t, db)
func Close(t testing.TB, c io.Closer) {
	t.Helper()
	if err := c.Close(); err != nil {
		t.Errorf("%+v", err)
	}
}

// MockDB creates a mocked database connection. Fatals on error.
func MockDB(t testing.TB, opts ...dml.ConnPoolOption) (*dml.ConnPool, sqlmock.Sqlmock) {
	if t != nil { // t can be nil in Example functions
		t.Helper()
	}
	db, sm, err := sqlmock.New()
	FatalIfError(t, err)
	cfg := []dml.ConnPoolOption{dml.WithDB(db)}
	dbc, err := dml.NewConnPool(append(cfg, opts...)...)
	FatalIfError(t, err)
	return dbc, sm
}

// MockClose for usage in conjunction with defer.
// 		defer dmltest.MockClose(t, db, dbMock)
func MockClose(t testing.TB, c io.Closer, m sqlmock.Sqlmock) {
	if t != nil { // t can be nil in Example functions
		t.Helper()
	}
	m.ExpectClose()
	FatalIfError(t, c.Close())
	FatalIfError(t, m.ExpectationsWereMet())
}

// FatalIfError fails the tests if an unexpected error occurred. If the error is
// gift wrapped, it prints the location. If `t` is nil, this function panics.
func FatalIfError(t testing.TB, err error) {
	if err != nil {
		if t != nil {
			t.Fatalf("%+v", err)
		} else {
			panic(err)
		}
	}
}

// CheckLastInsertID returns a function which accepts the return result from
// Exec*() and returns itself the last_insert_id or emits an error.
func CheckLastInsertID(t interface {
	Errorf(format string, args ...interface{})
}, msg ...string) func(sql.Result, error) int64 {
	return func(res sql.Result, err error) int64 {
		if err != nil {
			if len(msg) == 1 {
				t.Errorf("%q: %+v", msg[0], err)
			} else {
				t.Errorf("%+v", err)
			}
			return 0
		}
		lid, err := res.LastInsertId()
		if err != nil {
			if len(msg) == 1 {
				t.Errorf("%q: %+v", msg[0], err)
			} else {
				t.Errorf("%+v", err)
			}
			return 0
		}
		if lid < 0 {
			t.Errorf("Expecting Last Insert ID to be greater than zero, got %d", lid)
		}
		return lid
	}
}
<|endoftext|>


<commit_before>package gosql

import "testing"

func TestSQLXOrder_ExampleJSON1(t *testing.T) {
	example := `{"%o":["+key1","-key2"]}`      // 定义Example
	order := NewSQLXOrder()                    // 初始化
	order.Allow("key1", "key2")                // 设置关键字过滤规则
	sql, err := order.JSONtoSQLString(example) // 生成SQL语句
	if err != nil {
		t.Fatal(err)
	}
	t.Log(sql)
}

func TestSQLXOrder_ExampleJSON2(t *testing.T) {
	example := `{"%o":["+key1","-key2","key5"]}` // 定义Example，注意前缀设定
	order := NewSQLXOrder()                      // 初始化
	order.Allow("key1", "key2", "key5")          // 设置关键字过滤规则
	sql, err := order.JSONtoSQLString(example)   // 生成SQL语句
	if err != nil {
		t.Fatal(err)
	}
	t.Log(sql)
}
<commit_msg>test: sqlx order alias<commit_after>package gosql

import "testing"

func TestSQLXOrder_ExampleJSON1(t *testing.T) {
	example := `{"%o":["+key1","-key2"]}`      // 定义Example
	order := NewSQLXOrder()                    // 初始化
	order.Allow("key1", "key2")                // 设置关键字过滤规则
	sql, err := order.JSONtoSQLString(example) // 生成SQL语句
	if err != nil {
		t.Fatal(err)
	}
	t.Log(sql)
}

func TestSQLXOrder_ExampleJSON2(t *testing.T) {
	example := `{"%o":["+key1","-key2","key5"]}`    // 定义Example，注意前缀设定
	order := NewSQLXOrder()                         // 初始化
	order.Allow("key1", "key2", "key5")             // 设置关键字过滤规则
	sql, err := order.JSONtoSQLString(example, "a") // 生成SQL语句
	if err != nil {
		t.Fatal(err)
	}
	t.Log(sql)
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"

	"github.com/ikeikeikeike/charneoapo"
	"github.com/ikeikeikeike/gopkg/convert"
	"github.com/ikeikeikeike/gopkg/extract/image"
)

type Character struct {
	Id int64 `orm:"auto"`

	Name string `orm:"size(128);index"`

	Kana   string `orm:"size(128);null"`
	Romaji string `orm:"size(128);null"`
	Gyou   string `orm:"size(6);index;null"`

	Birthday time.Time `orm:"type(date);index;null"`

	Blood string `orm:"size(6);index;null"`

	Height int `orm:"default(0);index"`
	Weight int `orm:"default(0);index"`

	Bust   int    `orm:"default(0);index"`
	Waste  int    `orm:"default(0);index"`
	Hip    int    `orm:"default(0);index"`
	Bracup string `orm:"size(8);index;null"`

	Outline string `orm:"type(text);null"`

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Icon *Image `orm:"rel(one);on_delete(set_null);index;null"`

	Product string `orm:"size(128);index"` // XXX: Anime modelsになった
	Anime   *Anime `orm:"rel(fk);null"`    // TODO: Productをなくす

	PicturesCount int        `orm:"default(0);index"`
	Pictures      []*Picture `orm:"reverse(many)"`
}

// multiple fields unique key
func (u *Character) TableUnique() [][]string {
	return [][]string{
		[]string{"Product", "Name"},
	}
}

func (m *Character) LoadRelated() *Character {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Icon")
	_, _ = o.LoadRelated(m, "Anime")
	_, _ = o.LoadRelated(m, "Pictures", 2, DefaultPerEntities)
	return m
}

func (m *Character) RelLoader() {
	m.LoadRelated()
}

func (m *Character) IdStr() string {
	return convert.ToStr(m.Id)
}

func (m *Character) Insert() error {
	if _, err := orm.NewOrm().Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *Character) Read(fields ...string) error {
	if err := orm.NewOrm().Read(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Character) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

func (m *Character) Update(fields ...string) error {
	if _, err := orm.NewOrm().Update(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Character) Delete() error {
	if _, err := orm.NewOrm().Delete(m); err != nil {
		return err
	}
	return nil
}

func (m *Character) UpdateIconByFileInfo(info *image.FileInfo, name string) error {
	m.Icon.Name = name
	m.Icon.Src = info.Url
	m.Icon.Width = info.Width
	m.Icon.Height = info.Height
	m.Icon.Ext = info.Ext
	m.Icon.Mime = info.Mime

	err := m.Icon.Update()
	if err == nil {
		return m.Update()
	}
	return err
}

func (m *Character) UpdateByNeoapo(n *charneoapo.Neoapo) error {
	// m.Product = n.Product()
	// m.Name = n.Name()
	m.Kana = n.Kana()
	m.Birthday = n.Birthday()
	m.Blood = n.Blood()
	m.Height = n.Height()
	m.Weight = n.Weight()
	m.Bust = n.Bust()
	m.Waste = n.Waist()
	m.Hip = n.Hip()
	m.Bracup = n.Bracup()
	m.Outline = n.Comment()

	return m.Update()
}

func Characters() orm.QuerySeter {
	return orm.NewOrm().QueryTable("character").OrderBy("-Id")
}

func init() {
	orm.RegisterModelWithPrefix(
		beego.AppConfig.String("dbprefix"),
		new(Character))
}
<commit_msg>refactoring<commit_after>package models

import (
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"

	"github.com/ikeikeikeike/charneoapo"
	"github.com/ikeikeikeike/gopkg/convert"
	"github.com/ikeikeikeike/gopkg/extract/image"
)

type Character struct {
	Id int64 `orm:"auto"`

	Name string `orm:"size(128);index"`

	Kana   string `orm:"size(128);null"`
	Romaji string `orm:"size(128);null"`
	Gyou   string `orm:"size(6);index;null"`

	Birthday time.Time `orm:"type(date);index;null"`

	Blood string `orm:"size(6);index;null"`

	Height int `orm:"default(0);index"`
	Weight int `orm:"default(0);index"`

	Bust   int    `orm:"default(0);index"`
	Waste  int    `orm:"default(0);index"`
	Hip    int    `orm:"default(0);index"`
	Bracup string `orm:"size(8);index;null"`

	Outline string `orm:"type(text);null"`
	Html    string `orm:"type(text);null"`

	Created time.Time `orm:"auto_now_add;type(datetime)"`
	Updated time.Time `orm:"auto_now;type(datetime)"`

	Icon *Image `orm:"rel(one);on_delete(set_null);index;null"`

	Product string `orm:"size(128);index"` // XXX: Anime modelsになった
	Anime   *Anime `orm:"rel(fk);null"`    // TODO: Productをなくす

	PicturesCount int        `orm:"default(0);index"`
	Pictures      []*Picture `orm:"reverse(many)"`
}

// multiple fields unique key
func (u *Character) TableUnique() [][]string {
	return [][]string{
		[]string{"Product", "Name"},
	}
}

func (m *Character) LoadRelated() *Character {
	o := orm.NewOrm()
	_, _ = o.LoadRelated(m, "Icon")
	_, _ = o.LoadRelated(m, "Anime")
	_, _ = o.LoadRelated(m, "Pictures", 2, DefaultPerEntities)
	return m
}

func (m *Character) RelLoader() {
	m.LoadRelated()
}

func (m *Character) IdStr() string {
	return convert.ToStr(m.Id)
}

func (m *Character) Insert() error {
	if _, err := orm.NewOrm().Insert(m); err != nil {
		return err
	}
	return nil
}

func (m *Character) Read(fields ...string) error {
	if err := orm.NewOrm().Read(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Character) ReadOrCreate(field string, fields ...string) (bool, int64, error) {
	return orm.NewOrm().ReadOrCreate(m, field, fields...)
}

func (m *Character) Update(fields ...string) error {
	if _, err := orm.NewOrm().Update(m, fields...); err != nil {
		return err
	}
	return nil
}

func (m *Character) Delete() error {
	if _, err := orm.NewOrm().Delete(m); err != nil {
		return err
	}
	return nil
}

func (m *Character) UpdateIconByFileInfo(info *image.FileInfo, name string) error {
	m.Icon.Name = name
	m.Icon.Src = info.Url
	m.Icon.Width = info.Width
	m.Icon.Height = info.Height
	m.Icon.Ext = info.Ext
	m.Icon.Mime = info.Mime

	err := m.Icon.Update()
	if err == nil {
		return m.Update()
	}
	return err
}

func (m *Character) UpdateByNeoapo(n *charneoapo.Neoapo) error {
	// m.Product = n.Product()
	// m.Name = n.Name()
	m.Kana = n.Kana()
	m.Birthday = n.Birthday()
	m.Blood = n.Blood()
	m.Height = n.Height()
	m.Weight = n.Weight()
	m.Bust = n.Bust()
	m.Waste = n.Waist()
	m.Hip = n.Hip()
	m.Bracup = n.Bracup()
	m.Outline = n.Comment()

	return m.Update()
}

func Characters() orm.QuerySeter {
	return orm.NewOrm().QueryTable("character").OrderBy("-Id")
}

func init() {
	orm.RegisterModelWithPrefix(
		beego.AppConfig.String("dbprefix"),
		new(Character))
}
<|endoftext|>


<commit_before>package models

import (
	"testing"
	"reflect"
	"os"
	"io"
	"io/ioutil"
	"path/filepath"
	"sync"
	"crypto/rand"
	"log"
	mrand "math/rand"
	"github.com/bradfitz/iter"
)

const MaxRandomFileSize = 2<<19

func NewRandomFile(path string) string {
	filename := NewRandomName()
	buf := make([]byte, mrand.Intn(MaxRandomFileSize))
	rand.Read(buf)
	f := filepath.Join(path, filename)
	ioutil.WriteFile(f, buf, 0700)
	return f
}

func NewRandomDir(path string) string {
	dirname := NewRandomName()
	p := filepath.Join(path, dirname)
	os.Mkdir(p, 0700)
	return p
}

func NewRandomName() string {
	c := 12
	b := make([]byte, c)
	n, err := io.ReadFull(rand.Reader, b)
	if n != len(b) || err != nil {
		panic(err)
	}
	return SHA1(b)
}

func NewRandomFileWg(path string, wg *sync.WaitGroup) string {
	defer wg.Done()
	return NewRandomFile(path)
}

func CreateRandomTree(t *testing.T, path string, rec, maxrec int) (string, int) {
	p := NewRandomDir(path)
	if rec == 0 {
		log.Printf("Creating a new random tree at %v", p)
	}
	nfiles := 0
	for {
		nfiles = mrand.Intn(10)
		if nfiles >= 3 {
			break
		}
	}
	cnt := 0
	var wg sync.WaitGroup
	for _ = range iter.N(nfiles) {
		wg.Add(1)
    	go NewRandomFileWg(p, &wg)
    	cnt++
    	if rec < maxrec && mrand.Intn(10) < 5 {
    		_, ncnt := CreateRandomTree(t, p, rec+1, maxrec)
    		cnt += ncnt
    	}
    	// Break at 50 to spend less time
    	if cnt > 30 {
    		return p, cnt
    	}
	}
	wg.Wait()
	if rec == 0 {
		log.Printf("Done")
	}
	return p, cnt
}

func NewRandomTree(t *testing.T, path string, maxrec int) (string) {
	tpath, _ := CreateRandomTree(t, path, 0, maxrec)
	return tpath
}

func TestModelsMeta(t *testing.T) {
	pool, err := GetDbPool()
	check(err)
	f := NewMeta()
	f.Hash = "foo_meta"
	err = f.Save(pool)
	check(err)

	fe, err := NewMetaFromDB(pool, "foo_meta")
	check(err)
	if !reflect.DeepEqual(f, fe) {
		t.Errorf("Error retrieving Meta from DB, expected %+v, get %+v", f, fe)
	}
	//NewRandomTree(t, ".", 3)
}
<commit_msg>Bugfix test, now all tests are passing again with kv<commit_after>package models

import (
	"testing"
	"os"
	"io"
	"io/ioutil"
	"path/filepath"
	"sync"
	"crypto/rand"
	"log"
	mrand "math/rand"
	"github.com/bradfitz/iter"
)

const MaxRandomFileSize = 2<<19

func NewRandomFile(path string) string {
	filename := NewRandomName()
	buf := make([]byte, mrand.Intn(MaxRandomFileSize))
	rand.Read(buf)
	f := filepath.Join(path, filename)
	ioutil.WriteFile(f, buf, 0700)
	return f
}

func NewRandomDir(path string) string {
	dirname := NewRandomName()
	p := filepath.Join(path, dirname)
	os.Mkdir(p, 0700)
	return p
}

func NewRandomName() string {
	c := 12
	b := make([]byte, c)
	n, err := io.ReadFull(rand.Reader, b)
	if n != len(b) || err != nil {
		panic(err)
	}
	return SHA1(b)
}

func NewRandomFileWg(path string, wg *sync.WaitGroup) string {
	defer wg.Done()
	return NewRandomFile(path)
}

func CreateRandomTree(t *testing.T, path string, rec, maxrec int) (string, int) {
	p := NewRandomDir(path)
	if rec == 0 {
		log.Printf("Creating a new random tree at %v", p)
	}
	nfiles := 0
	for {
		nfiles = mrand.Intn(10)
		if nfiles >= 3 {
			break
		}
	}
	cnt := 0
	var wg sync.WaitGroup
	for _ = range iter.N(nfiles) {
		wg.Add(1)
    	go NewRandomFileWg(p, &wg)
    	cnt++
    	if rec < maxrec && mrand.Intn(10) < 5 {
    		_, ncnt := CreateRandomTree(t, p, rec+1, maxrec)
    		cnt += ncnt
    	}
    	// Break at 50 to spend less time
    	if cnt > 30 {
    		return p, cnt
    	}
	}
	wg.Wait()
	if rec == 0 {
		log.Printf("Done")
	}
	return p, cnt
}

func NewRandomTree(t *testing.T, path string, maxrec int) (string) {
	tpath, _ := CreateRandomTree(t, path, 0, maxrec)
	return tpath
}

func TestModelsMeta(t *testing.T) {
	pool, err := GetDbPool()
	check(err)
	f := NewMeta()
	f.Hash = "foo_meta"
	err = f.Save(pool)
	check(err)

	fe, err := NewMetaFromDB(pool, "foo_meta")
	check(err)
	if f.Hash != fe.Hash {
		t.Errorf("Error retrieving Meta from DB, expected %+v, get %+v", f, fe)
	}
	//NewRandomTree(t, ".", 3)
}
<|endoftext|>


<commit_before>package annotation

import (
	"bufio"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/biogo/hts/bam"
	"github.com/dhconnelly/rtreego"
	"github.com/guigolab/bamstats/utils"
)

const (
	debugElementsFile = "bamstats-coverage.elements.bed"
)

// RtreeMap is a map of pointers to Rtree with string keys.
type RtreeMap map[string]*rtreego.Rtree

type chunk struct {
	chr   string
	feats chan rtreego.Spatial
}

// Get returns the pointer to an Rtree for the specified chromosome and create a new Rtree if not present.
func (t RtreeMap) Get(chr string) *rtreego.Rtree {
	if _, ok := t[chr]; !ok {
		t[chr] = rtreego.NewTree(1, 25, 50)
	}
	return t[chr]
}

func createTrees(trees RtreeMap, regions chan chunk) {
	chan2slice := func(c chan rtreego.Spatial) []rtreego.Spatial {
		var s []rtreego.Spatial
		for item := range c {
			s = append(s, item)
		}
		return s
	}

	for chunk := range regions {
		trees[chunk.chr] = rtreego.NewTree(1, 25, 50, chan2slice(chunk.feats)...)
		logrus.Debugf("Done %s", chunk.chr)
	}
}

func insertInTree(sem chan bool, rt *rtreego.Rtree, feats []rtreego.Spatial) {
	logrus.Info("Running ", feats[0].(*Feature).Chr())
	defer func() { <-sem }()
	for _, feat := range feats {
		rt.Insert(feat)
	}
}

func getChrLens(bamFile string, cpu int) (chrs map[string]int) {
	bf, err := os.Open(bamFile)
	utils.Check(err)
	br, err := bam.NewReader(bf, cpu)
	utils.Check(err)
	refs := br.Header().Refs()
	chrs = make(map[string]int, len(refs))
	for _, r := range refs {
		chrs[r.Name()] = r.Len()
	}
	return
}

// CreateIndex creates the Rtree indices for the specified annotation file. It builds a Rtree
// for each chromosome and returns a RtreeMap having the chromosome names as keys.
func CreateIndex(annoFile, bamFile string, cpu int) *RtreeMap {
	f, err := os.Open(annoFile)
	utils.Check(err)
	chrLens := getChrLens(bamFile, cpu)
	scanner := NewScanner(f, chrLens)

	return createIndex(scanner, cpu)
}

func writeElements(items <-chan string) {
	var w *bufio.Writer
	out, _ := os.Create(debugElementsFile)
	w = bufio.NewWriter(out)
	logrus.Debugf("Writing index elements to %s", out.Name())
	for item := range items {
		w.WriteString(item)
		w.WriteRune('\n')
	}
	w.Flush()
}

func scan(scanner *Scanner, regions chan chunk, elems chan string) {
	regMap := make(map[string]chan rtreego.Spatial)
	var chr, lastChr string
	for scanner.Next() {
		feature := scanner.Feat()
		if feature == nil {
			continue
		}
		if logrus.GetLevel() == logrus.DebugLevel {
			elems <- feature.Out()
		}
		if len(chr) == 0 {
			lastChr = feature.Chr()
		}
		chr = feature.Chr()
		if lastChr != chr {
			close(regMap[lastChr])
			lastChr = chr
		}
		_, ok := regMap[chr]
		if !ok {
			regMap[chr] = make(chan rtreego.Spatial)
			regions <- chunk{chr, regMap[chr]}
		}
		regMap[chr] <- feature
	}
	close(regMap[lastChr])
	close(regions)
	close(elems)
	if scanner.Error() != nil {
		logrus.Panic(scanner.Error())
	}
}

func createIndex(scanner *Scanner, cpu int) *RtreeMap {
	trees := make(RtreeMap)
	regions := make(chan chunk)
	debugElements := make(chan string)

	if logrus.GetLevel() == logrus.DebugLevel {
		go writeElements(debugElements)
	}

	go scan(scanner, regions, debugElements)

	createTrees(trees, regions)

	return &trees
}

// QueryIndex perform a SearchIntersect on the specified index given a start and end position.
func QueryIndex(index *rtreego.Rtree, begin, end float64) []rtreego.Spatial {
	size := end - begin
	// Create the bounding box for the query:
	bb, _ := rtreego.NewRect(rtreego.Point{begin}, []float64{size})

	// Get a slice of the objects in rt that intersect bb:
	return index.SearchIntersect(bb)
}
<commit_msg>Removed unused function insertInTree<commit_after>package annotation

import (
	"bufio"
	"os"

	"github.com/Sirupsen/logrus"
	"github.com/biogo/hts/bam"
	"github.com/dhconnelly/rtreego"
	"github.com/guigolab/bamstats/utils"
)

const (
	debugElementsFile = "bamstats-coverage.elements.bed"
)

// RtreeMap is a map of pointers to Rtree with string keys.
type RtreeMap map[string]*rtreego.Rtree

type chunk struct {
	chr   string
	feats chan rtreego.Spatial
}

// Get returns the pointer to an Rtree for the specified chromosome and create a new Rtree if not present.
func (t RtreeMap) Get(chr string) *rtreego.Rtree {
	if _, ok := t[chr]; !ok {
		t[chr] = rtreego.NewTree(1, 25, 50)
	}
	return t[chr]
}

func createTrees(trees RtreeMap, regions chan chunk) {
	chan2slice := func(c chan rtreego.Spatial) []rtreego.Spatial {
		var s []rtreego.Spatial
		for item := range c {
			s = append(s, item)
		}
		return s
	}

	for chunk := range regions {
		trees[chunk.chr] = rtreego.NewTree(1, 25, 50, chan2slice(chunk.feats)...)
		logrus.Debugf("Done %s", chunk.chr)
	}
}

func getChrLens(bamFile string, cpu int) (chrs map[string]int) {
	bf, err := os.Open(bamFile)
	utils.Check(err)
	br, err := bam.NewReader(bf, cpu)
	utils.Check(err)
	refs := br.Header().Refs()
	chrs = make(map[string]int, len(refs))
	for _, r := range refs {
		chrs[r.Name()] = r.Len()
	}
	return
}

// CreateIndex creates the Rtree indices for the specified annotation file. It builds a Rtree
// for each chromosome and returns a RtreeMap having the chromosome names as keys.
func CreateIndex(annoFile, bamFile string, cpu int) *RtreeMap {
	f, err := os.Open(annoFile)
	utils.Check(err)
	chrLens := getChrLens(bamFile, cpu)
	scanner := NewScanner(f, chrLens)

	return createIndex(scanner, cpu)
}

func writeElements(items <-chan string) {
	var w *bufio.Writer
	out, _ := os.Create(debugElementsFile)
	w = bufio.NewWriter(out)
	logrus.Debugf("Writing index elements to %s", out.Name())
	for item := range items {
		w.WriteString(item)
		w.WriteRune('\n')
	}
	w.Flush()
}

func scan(scanner *Scanner, regions chan chunk, elems chan string) {
	regMap := make(map[string]chan rtreego.Spatial)
	var chr, lastChr string
	for scanner.Next() {
		feature := scanner.Feat()
		if feature == nil {
			continue
		}
		if logrus.GetLevel() == logrus.DebugLevel {
			elems <- feature.Out()
		}
		if len(chr) == 0 {
			lastChr = feature.Chr()
		}
		chr = feature.Chr()
		if lastChr != chr {
			close(regMap[lastChr])
			lastChr = chr
		}
		_, ok := regMap[chr]
		if !ok {
			regMap[chr] = make(chan rtreego.Spatial)
			regions <- chunk{chr, regMap[chr]}
		}
		regMap[chr] <- feature
	}
	close(regMap[lastChr])
	close(regions)
	close(elems)
	if scanner.Error() != nil {
		logrus.Panic(scanner.Error())
	}
}

func createIndex(scanner *Scanner, cpu int) *RtreeMap {
	trees := make(RtreeMap)
	regions := make(chan chunk)
	debugElements := make(chan string)

	if logrus.GetLevel() == logrus.DebugLevel {
		go writeElements(debugElements)
	}

	go scan(scanner, regions, debugElements)

	createTrees(trees, regions)

	return &trees
}

// QueryIndex perform a SearchIntersect on the specified index given a start and end position.
func QueryIndex(index *rtreego.Rtree, begin, end float64) []rtreego.Spatial {
	size := end - begin
	// Create the bounding box for the query:
	bb, _ := rtreego.NewRect(rtreego.Point{begin}, []float64{size})

	// Get a slice of the objects in rt that intersect bb:
	return index.SearchIntersect(bb)
}
<|endoftext|>


<commit_before>// Copyright 2017 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"fmt"
	"model"
	"time"
	"util"

	. "db"

	"github.com/garyburd/redigo/redis"
	"github.com/go-xorm/xorm"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/nosql"
	"github.com/polaris1119/times"
	"golang.org/x/net/context"
)

var (
	beginAwardWeight = 50
)

type UserRichLogic struct{}

var DefaultUserRich = UserRichLogic{}

func (self UserRichLogic) AwardCooper() {
	redisClient := nosql.NewRedisClient()
	defer redisClient.Close()
	ymd := times.Format("ymd", time.Now().Add(-86400*time.Second))
	key := DefaultRank.getDAURankKey(ymd)

	var (
		cursor      uint64
		err         error
		resultSlice []interface{}
		count       = 20
	)

	for {
		cursor, resultSlice, err = redisClient.ZSCAN(key, cursor, "COUNT", count)
		if err != nil {
			logger.Errorln("AwardCooper ZSCAN error:", err)
			break
		}

		for len(resultSlice) > 0 {
			var (
				uid, weight int
				err         error
			)
			resultSlice, err = redis.Scan(resultSlice, &uid, &weight)
			if err != nil {
				logger.Errorln("AwardCooper redis Scan error:", err)
				continue
			}

			if weight < beginAwardWeight {
				continue
			}

			award := util.Max((weight-500)*5, 0) +
				util.UMin((weight-400), 100)*4 +
				util.UMin((weight-300), 100)*3 +
				util.UMin((weight-200), 100)*2 +
				util.UMin((weight-100), 100) +
				int(float64(util.UMin((weight-beginAwardWeight), beginAwardWeight))*0.5)

			userRank := redisClient.ZREVRANK(key, uid)
			desc := fmt.Sprintf("%s 的活跃度为 %d，排名第 %d，奖励 %d 铜币", ymd, weight, userRank, award)
			fmt.Println(uid, desc)
			user := DefaultUser.FindOne(nil, "uid", uid)
			self.IncrUserRich(user, model.MissionTypeActive, award, desc)
		}

		if cursor == 0 {
			break
		}
	}
}

// IncrUserRich 增加或减少用户财富
func (self UserRichLogic) IncrUserRich(user *model.User, typ, award int, desc string) {
	if award == 0 {
		logger.Errorln("IncrUserRich, but award is empty!")
		return
	}

	var (
		total int64 = -1
		err   error
	)

	if award > 0 && (typ == model.MissionTypeReplied || typ == model.MissionTypeActive) {
		// 老用户，因为之前的主题被人回复而增加财富，自动帮其领取初始资本
		// 因为活跃奖励铜币，自动帮其领取初始资本
		total, err = MasterDB.Where("uid=?", user.Uid).Count(new(model.UserBalanceDetail))
		if err != nil {
			logger.Errorln("IncrUserRich count error:", err)
			return
		}
	}

	session := MasterDB.NewSession()
	defer session.Close()
	session.Begin()

	var initialAward int
	if total == 0 {
		initialAward, err = self.autoCompleteInitial(session, user)
		if err != nil {
			logger.Errorln("IncrUserRich autoCompleteInitial error:", err)
			session.Rollback()
			return
		}
	}

	user.Balance += initialAward + award
	_, err = session.Where("uid=?", user.Uid).Cols("balance").Update(user)
	if err != nil {
		logger.Errorln("IncrUserRich update error:", err)
		session.Rollback()
		return
	}

	balanceDetail := &model.UserBalanceDetail{
		Uid:     user.Uid,
		Type:    typ,
		Num:     award,
		Balance: user.Balance,
		Desc:    desc,
	}
	_, err = session.Insert(balanceDetail)
	if err != nil {
		logger.Errorln("IncrUserRich insert error:", err)
		session.Rollback()
		return
	}

	session.Commit()
}

func (UserRichLogic) FindBalanceDetail(ctx context.Context, me *model.Me) []*model.UserBalanceDetail {
	objLog := GetLogger(ctx)

	balanceDetails := make([]*model.UserBalanceDetail, 0)
	err := MasterDB.Where("uid=?", me.Uid).Desc("id").Find(&balanceDetails)
	if err != nil {
		objLog.Errorln("UserRichLogic FindBalanceDetail error:", err)
		return nil
	}

	return balanceDetails
}

func (UserRichLogic) Total(ctx context.Context, uid int) int64 {
	total, err := MasterDB.Where("uid=?", uid).Count(new(model.UserBalanceDetail))
	if err != nil {
		logger.Errorln("UserRichLogic Total error:", err)
	}
	return total
}

func (UserRichLogic) add(session *xorm.Session, balanceDetail *model.UserBalanceDetail) error {
	_, err := session.Insert(balanceDetail)
	return err
}

func (UserRichLogic) autoCompleteInitial(session *xorm.Session, user *model.User) (int, error) {
	mission := &model.Mission{}
	_, err := session.Where("id=?", model.InitialMissionId).Get(mission)
	if err != nil {
		return 0, err
	}
	if mission.Id == 0 {
		return 0, errors.New("初始资本任务不存在！")
	}

	balanceDetail := &model.UserBalanceDetail{
		Uid:     user.Uid,
		Type:    model.MissionTypeInitial,
		Num:     mission.Fixed,
		Balance: mission.Fixed,
		Desc:    fmt.Sprintf("获得%s %d 铜币", model.BalanceTypeMap[mission.Type], mission.Fixed),
	}
	_, err = session.Insert(balanceDetail)

	return mission.Fixed, err
}
<commit_msg>bugfix<commit_after>// Copyright 2017 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"errors"
	"fmt"
	"model"
	"time"
	"util"

	. "db"

	"github.com/garyburd/redigo/redis"
	"github.com/go-xorm/xorm"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/nosql"
	"github.com/polaris1119/times"
	"golang.org/x/net/context"
)

var (
	beginAwardWeight = 50
)

type UserRichLogic struct{}

var DefaultUserRich = UserRichLogic{}

func (self UserRichLogic) AwardCooper() {
	redisClient := nosql.NewRedisClient()
	defer redisClient.Close()
	ymd := times.Format("ymd", time.Now().Add(-86400*time.Second))
	key := DefaultRank.getDAURankKey(ymd)

	var (
		cursor      uint64
		err         error
		resultSlice []interface{}
		count       = 20
	)

	for {
		cursor, resultSlice, err = redisClient.ZSCAN(key, cursor, "COUNT", count)
		if err != nil {
			logger.Errorln("AwardCooper ZSCAN error:", err)
			break
		}

		for len(resultSlice) > 0 {
			var (
				uid, weight int
				err         error
			)
			resultSlice, err = redis.Scan(resultSlice, &uid, &weight)
			if err != nil {
				logger.Errorln("AwardCooper redis Scan error:", err)
				continue
			}

			if weight < beginAwardWeight {
				continue
			}

			award := util.Max((weight-500)*5, 0) +
				util.UMin((weight-400), 100)*4 +
				util.UMin((weight-300), 100)*3 +
				util.UMin((weight-200), 100)*2 +
				util.UMin((weight-100), 100) +
				int(float64(util.UMin((weight-beginAwardWeight), beginAwardWeight))*0.5)

			userRank := redisClient.ZREVRANK(key, uid)
			desc := fmt.Sprintf("%s 的活跃度为 %d，排名第 %d，奖励 %d 铜币", ymd, weight, userRank, award)

			user := DefaultUser.FindOne(nil, "uid", uid)
			self.IncrUserRich(user, model.MissionTypeActive, award, desc)
		}

		if cursor == 0 {
			break
		}
	}
}

// IncrUserRich 增加或减少用户财富
func (self UserRichLogic) IncrUserRich(user *model.User, typ, award int, desc string) {
	if award == 0 {
		logger.Errorln("IncrUserRich, but award is empty!")
		return
	}

	var (
		total int64 = -1
		err   error
	)

	if award > 0 && (typ == model.MissionTypeReplied || typ == model.MissionTypeActive) {
		// 老用户，因为之前的主题被人回复而增加财富，自动帮其领取初始资本
		// 因为活跃奖励铜币，自动帮其领取初始资本
		total, err = MasterDB.Where("uid=?", user.Uid).Count(new(model.UserBalanceDetail))
		if err != nil {
			logger.Errorln("IncrUserRich count error:", err)
			return
		}
	}

	session := MasterDB.NewSession()
	defer session.Close()
	session.Begin()

	var initialAward int
	if total == 0 {
		initialAward, err = self.autoCompleteInitial(session, user)
		if err != nil {
			logger.Errorln("IncrUserRich autoCompleteInitial error:", err)
			session.Rollback()
			return
		}
	}

	user.Balance += initialAward + award
	_, err = session.Where("uid=?", user.Uid).Cols("balance").Update(user)
	if err != nil {
		logger.Errorln("IncrUserRich update error:", err)
		session.Rollback()
		return
	}

	balanceDetail := &model.UserBalanceDetail{
		Uid:     user.Uid,
		Type:    typ,
		Num:     award,
		Balance: user.Balance,
		Desc:    desc,
	}
	_, err = session.Insert(balanceDetail)
	if err != nil {
		logger.Errorln("IncrUserRich insert error:", err)
		session.Rollback()
		return
	}

	session.Commit()
}

func (UserRichLogic) FindBalanceDetail(ctx context.Context, me *model.Me) []*model.UserBalanceDetail {
	objLog := GetLogger(ctx)

	balanceDetails := make([]*model.UserBalanceDetail, 0)
	err := MasterDB.Where("uid=?", me.Uid).Desc("id").Find(&balanceDetails)
	if err != nil {
		objLog.Errorln("UserRichLogic FindBalanceDetail error:", err)
		return nil
	}

	return balanceDetails
}

func (UserRichLogic) Total(ctx context.Context, uid int) int64 {
	total, err := MasterDB.Where("uid=?", uid).Count(new(model.UserBalanceDetail))
	if err != nil {
		logger.Errorln("UserRichLogic Total error:", err)
	}
	return total
}

func (UserRichLogic) add(session *xorm.Session, balanceDetail *model.UserBalanceDetail) error {
	_, err := session.Insert(balanceDetail)
	return err
}

func (UserRichLogic) autoCompleteInitial(session *xorm.Session, user *model.User) (int, error) {
	mission := &model.Mission{}
	_, err := session.Where("id=?", model.InitialMissionId).Get(mission)
	if err != nil {
		return 0, err
	}
	if mission.Id == 0 {
		return 0, errors.New("初始资本任务不存在！")
	}

	balanceDetail := &model.UserBalanceDetail{
		Uid:     user.Uid,
		Type:    model.MissionTypeInitial,
		Num:     mission.Fixed,
		Balance: mission.Fixed,
		Desc:    fmt.Sprintf("获得%s %d 铜币", model.BalanceTypeMap[mission.Type], mission.Fixed),
	}
	_, err = session.Insert(balanceDetail)

	return mission.Fixed, err
}
<|endoftext|>


<commit_before>package regression

// SimpleLinear is a simple linear regression model.
//
// https://en.wikipedia.org/wiki/Simple_linear_regression
type SimpleLinear struct {
	α float64
	β float64
}

// NewSimpleLinear fits a simple linear regression model.
func NewSimpleLinear(x, y []float64) *SimpleLinear {
	n := uint(len(x))

	μx, μy, μxy, μx2 := 0.0, 0.0, 0.0, 0.0
	for i := uint(0); i < n; i++ {
		μx += x[i]
		μy += y[i]
		μxy += x[i] * y[i]
		μx2 += x[i] * x[i]
	}
	μx, μy, μxy, μx2 = μx/float64(n), μy/float64(n), μxy/float64(n), μx2/float64(n)

	β := (μxy - μx*μy) / (μx2 - μx*μx)
	α := μy - β*μx

	return &SimpleLinear{α, β}
}

// Compute evaluate the model at a point.
func (self *SimpleLinear) Compute(x float64) float64 {
	return self.α*x + self.β
}
<commit_msg>regression: fix a typo<commit_after>package regression

// SimpleLinear is a simple linear regression model.
//
// https://en.wikipedia.org/wiki/Simple_linear_regression
type SimpleLinear struct {
	α float64
	β float64
}

// NewSimpleLinear fits a simple linear regression model.
func NewSimpleLinear(x, y []float64) *SimpleLinear {
	n := uint(len(x))

	μx, μy, μxy, μx2 := 0.0, 0.0, 0.0, 0.0
	for i := uint(0); i < n; i++ {
		μx += x[i]
		μy += y[i]
		μxy += x[i] * y[i]
		μx2 += x[i] * x[i]
	}
	μx, μy, μxy, μx2 = μx/float64(n), μy/float64(n), μxy/float64(n), μx2/float64(n)

	β := (μxy - μx*μy) / (μx2 - μx*μx)
	α := μy - β*μx

	return &SimpleLinear{α, β}
}

// Compute evaluate the model at a point.
func (self *SimpleLinear) Compute(x float64) float64 {
	return self.α + self.β*x
}
<|endoftext|>


<commit_before>package stow_test

import (
	"errors"
	"net/url"
	"testing"

	"github.com/cheekybits/is"
	"github.com/graymeta/stow"
)

func TestKindByURL(t *testing.T) {
	is := is.New(t)
	u, err := url.Parse("test://container/item")
	is.NoErr(err)
	kind, err := stow.KindByURL(u)
	is.NoErr(err)
	is.Equal(kind, testKind)
}

func TestKinds(t *testing.T) {
	is := is.New(t)
	stow.Register("example", nil, nil)
	is.Equal(stow.Kinds(), []string{"test", "example"})
}

func TestIsCursorEnd(t *testing.T) {
	is := is.New(t)
	is.True(stow.IsCursorEnd(""))
	is.False(stow.IsCursorEnd("anything"))
}

func TestErrNotSupported(t *testing.T) {
	is := is.New(t)
	err := errors.New("something")
	is.False(stow.IsNotSupported(err))
	err = stow.NotSupported("feature")
	is.True(stow.IsNotSupported(err))
}
<commit_msg>Write test for duplicate kinds<commit_after>package stow_test

import (
	"errors"
	"net/url"
	"testing"

	"github.com/cheekybits/is"
	"github.com/graymeta/stow"
)

func TestKindByURL(t *testing.T) {
	is := is.New(t)
	u, err := url.Parse("test://container/item")
	is.NoErr(err)
	kind, err := stow.KindByURL(u)
	is.NoErr(err)
	is.Equal(kind, testKind)
}

func TestKinds(t *testing.T) {
	is := is.New(t)
	stow.Register("example", nil, nil)
	is.Equal(stow.Kinds(), []string{"test", "example"})
}

func TestIsCursorEnd(t *testing.T) {
	is := is.New(t)
	is.True(stow.IsCursorEnd(""))
	is.False(stow.IsCursorEnd("anything"))
}

func TestErrNotSupported(t *testing.T) {
	is := is.New(t)
	err := errors.New("something")
	is.False(stow.IsNotSupported(err))
	err = stow.NotSupported("feature")
	is.True(stow.IsNotSupported(err))
}

func TestDuplicateKinds(t *testing.T) {
	is := is.New(t)
	stow.Register("example", nil, nil)
	is.Equal(stow.Kinds(), []string{"test", "example"})
	stow.Register("example", nil, nil)
	is.Equal(stow.Kinds(), []string{"test", "example"})
}
<|endoftext|>


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

package stompngo

import (
	"fmt"
	"log"
	"strconv"
)

var _ = fmt.Println

/*
	Subscribe to a STOMP subscription.

	Headers MUST contain a "destination" header key.

	All clients are recommended to supply a unique HK_ID header on Subscribe.

	For STOMP 1.0 clients:  if an "id" header is supplied, attempt to use it.
	If the "id" header is not unique in the session, return an error.  If no
	"id" header is supplied, attempt to generate a unique subscription id based
	on the destination name. If a unique subscription id cannot be generated,
	return an error.

	For STOMP 1.1+ clients: If any client does not supply an HK_ID header,
	attempt to generate a unique "id".  In all cases, do not allow duplicate
	subscription "id"s in this session.

	In summary, multiple subscriptions to the same destination are not allowed
	unless a unique "id" is supplied.

	For details about the returned MessageData channel, see: https://github.com/gmallard/stompngo/wiki/subscribe-and-messagedata

	Example:
		// Possible additional Header keys: "ack", "id".
		h := stompngo.Headers{stompngo.HK_DESTINATION, "/queue/myqueue"}
		s, e := c.Subscribe(h)
		if e != nil {
			// Do something sane ...
		}

*/
func (c *Connection) Subscribe(h Headers) (<-chan MessageData, error) {
	c.log(SUBSCRIBE, "start", h, c.Protocol())
	if !c.connected {
		return nil, ECONBAD
	}
	e := checkHeaders(h, c.Protocol())
	if e != nil {
		return nil, e
	}
	e = c.checkSubscribeHeaders(h)
	if e != nil {
		return nil, e
	}
	ch := h.Clone()
	if _, ok := ch.Contains(HK_ACK); !ok {
		ch = append(ch, HK_ACK, AckModeAuto)
	}
	sub, e, ch := c.establishSubscription(ch)
	if e != nil {
		return nil, e
	}
	//
	f := Frame{SUBSCRIBE, ch, NULLBUFF}
	//
	r := make(chan error)
	c.output <- wiredata{f, r}
	e = <-r
	c.log(SUBSCRIBE, "end", ch, c.Protocol())
	return sub.md, e
}

/*
	Check SUBSCRIBE specific requirements.
*/
func (c *Connection) checkSubscribeHeaders(h Headers) error {
	if _, ok := h.Contains(HK_DESTINATION); !ok {
		return EREQDSTSUB
	}
	//
	am, ok := h.Contains(HK_ACK)
	//
	switch c.Protocol() {
	case SPL_10:
		if ok { // Client supplied ack header
			if !validAckModes10[am] {
				return ESBADAM
			}
		}
	case SPL_11:
		fallthrough
	case SPL_12:
		if ok { // Client supplied ack header
			if !(validAckModes10[am] || validAckModes1x[am]) {
				return ESBADAM
			}
		}
	default:
		log.Fatalf("Internal protocol level error:<%s>\n", c.Protocol())
	}
	return nil
}

/*
	Handle subscribe id.
*/
func (c *Connection) establishSubscription(h Headers) (*subscription, error, Headers) {
	// c.log(SUBSCRIBE, "start establishSubscription")
	//
	id, hid := h.Contains(HK_ID)
	uuid1 := Uuid()
	sha11 := Sha1(h.Value(HK_DESTINATION))
	//
	c.subsLock.RLock() // Acquire Read lock
	// No duplicates
	if hid {
		if _, q := c.subs[id]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
		if _, q := c.subs[sha11]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
	} else {
		if _, q := c.subs[uuid1]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
	}
	c.subsLock.RUnlock() // Release Read lock
	//
	sd := new(subscription) // New subscription data
	if hid {
		sd.id = id // Note user supplied id
	}
	sd.cs = false                         // No shutdown yet
	sd.drav = false                       // Drain after value validity
	sd.dra = 0                            // Never drain MESSAGE frames
	sd.drmc = 0                           // Current drain count
	sd.md = make(chan MessageData, c.scc) // Make subscription MD channel
	sd.am = h.Value(HK_ACK)               // Set subscription ack mode
	//
	if !hid {
		// No caller supplied ID.  This STOMP client package supplies one.  It is the
		// caller's responsibility for discover the value from subsequent message
		// traffic.
		switch c.Protocol() {
		case SPL_10:
			nsid := sha11 // This will be unique for a given destination
			sd.id = nsid
			h = h.Add(HK_ID, nsid)
		case SPL_11:
			fallthrough
		case SPL_12:
			sd.id = uuid1
			h = h.Add(HK_ID, uuid1)
		default:
			log.Fatalf("Internal protocol level error:<%s>\n", c.Protocol())
		}
	}

	// STOMP Protocol Enhancement
	if dc, okda := h.Contains(StompPlusDrainAfter); okda {
		n, e := strconv.ParseInt(dc, 10, 0)
		if e != nil {
			log.Printf("sng_drafter conversion error: %v\n", e)
		} else {
			sd.drav = true   // Drain after value is OK
			sd.dra = uint(n) // Drain after count
		}
	}

	// This is a write lock
	c.subsLock.Lock()
	c.subs[sd.id] = sd // Add subscription to the connection subscription map
	c.subsLock.Unlock()
	//c.log(SUBSCRIBE, "end establishSubscription")
	return sd, nil, h // Return the subscription pointer
}
<commit_msg>Add logging to establishSubscription function.<commit_after>//
// Copyright © 2011-2017 Guy M. Allard
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package stompngo

import (
	"fmt"
	"log"
	"strconv"
)

var _ = fmt.Println

/*
	Subscribe to a STOMP subscription.

	Headers MUST contain a "destination" header key.

	All clients are recommended to supply a unique HK_ID header on Subscribe.

	For STOMP 1.0 clients:  if an "id" header is supplied, attempt to use it.
	If the "id" header is not unique in the session, return an error.  If no
	"id" header is supplied, attempt to generate a unique subscription id based
	on the destination name. If a unique subscription id cannot be generated,
	return an error.

	For STOMP 1.1+ clients: If any client does not supply an HK_ID header,
	attempt to generate a unique "id".  In all cases, do not allow duplicate
	subscription "id"s in this session.

	In summary, multiple subscriptions to the same destination are not allowed
	unless a unique "id" is supplied.

	For details about the returned MessageData channel, see: https://github.com/gmallard/stompngo/wiki/subscribe-and-messagedata

	Example:
		// Possible additional Header keys: "ack", "id".
		h := stompngo.Headers{stompngo.HK_DESTINATION, "/queue/myqueue"}
		s, e := c.Subscribe(h)
		if e != nil {
			// Do something sane ...
		}

*/
func (c *Connection) Subscribe(h Headers) (<-chan MessageData, error) {
	c.log(SUBSCRIBE, "start", h, c.Protocol())
	if !c.connected {
		return nil, ECONBAD
	}
	e := checkHeaders(h, c.Protocol())
	if e != nil {
		return nil, e
	}
	e = c.checkSubscribeHeaders(h)
	if e != nil {
		return nil, e
	}
	ch := h.Clone()
	if _, ok := ch.Contains(HK_ACK); !ok {
		ch = append(ch, HK_ACK, AckModeAuto)
	}
	sub, e, ch := c.establishSubscription(ch)
	if e != nil {
		return nil, e
	}
	//
	f := Frame{SUBSCRIBE, ch, NULLBUFF}
	//
	r := make(chan error)
	c.output <- wiredata{f, r}
	e = <-r
	c.log(SUBSCRIBE, "end", ch, c.Protocol())
	return sub.md, e
}

/*
	Check SUBSCRIBE specific requirements.
*/
func (c *Connection) checkSubscribeHeaders(h Headers) error {
	if _, ok := h.Contains(HK_DESTINATION); !ok {
		return EREQDSTSUB
	}
	//
	am, ok := h.Contains(HK_ACK)
	//
	switch c.Protocol() {
	case SPL_10:
		if ok { // Client supplied ack header
			if !validAckModes10[am] {
				return ESBADAM
			}
		}
	case SPL_11:
		fallthrough
	case SPL_12:
		if ok { // Client supplied ack header
			if !(validAckModes10[am] || validAckModes1x[am]) {
				return ESBADAM
			}
		}
	default:
		log.Fatalf("Internal protocol level error:<%s>\n", c.Protocol())
	}
	return nil
}

/*
	Handle subscribe id.
*/
func (c *Connection) establishSubscription(h Headers) (*subscription, error, Headers) {
	c.log(SUBSCRIBE, "start establishSubscription")
	defer c.log(SUBSCRIBE, "end establishSubscription")
	//
	id, hid := h.Contains(HK_ID)
	uuid1 := Uuid()
	sha11 := Sha1(h.Value(HK_DESTINATION))
	//
	c.subsLock.RLock() // Acquire Read lock
	// No duplicates
	if hid {
		if _, q := c.subs[id]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
		if _, q := c.subs[sha11]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
	} else {
		if _, q := c.subs[uuid1]; q {
			c.subsLock.RUnlock()   // Release Read lock
			return nil, EDUPSID, h // Duplicate subscriptions not allowed
		}
	}
	c.subsLock.RUnlock() // Release Read lock
	//
	sd := new(subscription) // New subscription data
	if hid {
		sd.id = id // Note user supplied id
	}
	sd.cs = false                         // No shutdown yet
	sd.drav = false                       // Drain after value validity
	sd.dra = 0                            // Never drain MESSAGE frames
	sd.drmc = 0                           // Current drain count
	sd.md = make(chan MessageData, c.scc) // Make subscription MD channel
	sd.am = h.Value(HK_ACK)               // Set subscription ack mode
	//
	if !hid {
		// No caller supplied ID.  This STOMP client package supplies one.  It is the
		// caller's responsibility for discover the value from subsequent message
		// traffic.
		switch c.Protocol() {
		case SPL_10:
			nsid := sha11 // This will be unique for a given destination
			sd.id = nsid
			h = h.Add(HK_ID, nsid)
		case SPL_11:
			fallthrough
		case SPL_12:
			sd.id = uuid1
			h = h.Add(HK_ID, uuid1)
		default:
			log.Fatalf("Internal protocol level error:<%s>\n", c.Protocol())
		}
	}

	// STOMP Protocol Enhancement
	if dc, okda := h.Contains(StompPlusDrainAfter); okda {
		n, e := strconv.ParseInt(dc, 10, 0)
		if e != nil {
			log.Printf("sng_drafter conversion error: %v\n", e)
		} else {
			sd.drav = true   // Drain after value is OK
			sd.dra = uint(n) // Drain after count
		}
	}

	// This is a write lock
	c.subsLock.Lock()
	c.subs[sd.id] = sd // Add subscription to the connection subscription map
	c.subsLock.Unlock()
	//
	return sd, nil, h // Return the subscription pointer
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"net/http"

	debug_handler "github.com/bborbe/http_handler/debug"

	"runtime"

	"fmt"

	"github.com/bborbe/portfolio/handler"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/golang/glog"
	"github.com/bborbe/portfolio/model"
)

const (
	DEFAULT_PORT int = 8080
	PARAMETER_PORT = "port"
)

var (
	portPtr = flag.Int(PARAMETER_PORT, DEFAULT_PORT, "port")
	documentRootPtr = flag.String("root", "", "Document root directory")
)

func main() {
	defer glog.Flush()
	glog.CopyStandardLogTo("info")
	flag.Parse()
	runtime.GOMAXPROCS(runtime.NumCPU())

	if err := do(); err != nil {
		glog.Exit(err)
	}

}

func do() error {
	server, err := createServer()
	if err != nil {
		return err
	}
	glog.V(2).Infof("start server")
	return gracehttp.Serve(server)
}

func createServer() (*http.Server, error) {
	port := model.Port(*portPtr)
	documentRoot := *documentRootPtr

	handler := handler.NewHandler(documentRoot)

	if glog.V(4) {
		handler = debug_handler.New(handler)
	}

	glog.V(2).Infof("create http server on %s", port.Address())
	return &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: handler}, nil
}
<commit_msg>use port address<commit_after>package main

import (
	"flag"
	"net/http"

	debug_handler "github.com/bborbe/http_handler/debug"

	"runtime"

	"fmt"

	"github.com/bborbe/portfolio/handler"
	"github.com/bborbe/portfolio/model"
	"github.com/facebookgo/grace/gracehttp"
	"github.com/golang/glog"
)

const (
	DEFAULT_PORT   int = 8080
	PARAMETER_PORT     = "port"
)

var (
	portPtr         = flag.Int(PARAMETER_PORT, DEFAULT_PORT, "port")
	documentRootPtr = flag.String("root", "", "Document root directory")
)

func main() {
	defer glog.Flush()
	glog.CopyStandardLogTo("info")
	flag.Parse()
	runtime.GOMAXPROCS(runtime.NumCPU())

	if err := do(); err != nil {
		glog.Exit(err)
	}

}

func do() error {
	server, err := createServer()
	if err != nil {
		return err
	}
	glog.V(2).Infof("start server")
	return gracehttp.Serve(server)
}

func createServer() (*http.Server, error) {
	port := model.Port(*portPtr)
	documentRoot := *documentRootPtr

	handler := handler.NewHandler(documentRoot)

	if glog.V(4) {
		handler = debug_handler.New(handler)
	}

	glog.V(2).Infof("create http server on %s", port.Address())
	return &http.Server{Addr: port.Address(), Handler: handler}, nil
}
<|endoftext|>


<commit_before>package jiacrontabd

import (
	"jiacrontab/pkg/file"
	"jiacrontab/pkg/util"
	"net"
	"reflect"

	"github.com/iwannay/log"

	ini "gopkg.in/ini.v1"
)

const (
	appname = "jiacrontabd"
)

type Config struct {
	LogLevel            string `opt:"log_level"`
	VerboseJobLog       bool   `opt:"verbose_job_log"`
	ListenAddr          string `opt:"listen_addr"`
	AdminAddr           string `opt:"admin_addr"`
	LogPath             string `opt:"log_path"`
	AutoCleanTaskLog    bool   `opt:"auto_clean_task_log"`
	NodeName            string `opt:"node_name"`
	BoardcastAddr       string `opt:"boardcast_addr"`
	ClientAliveInterval int    `opt:"client_alive_interval"`
	CfgPath             string
	Debug               bool `opt:"debug"`
	iniFile             *ini.File
	DriverName          string `opt:"driver_name"`
	DSN                 string `opt:"dsn"`
}

func (c *Config) Resolve() error {
	c.iniFile = c.loadConfig(c.CfgPath)

	val := reflect.ValueOf(c).Elem()
	typ := val.Type()

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		opt := field.Tag.Get("opt")
		if opt == "" {
			continue
		}
		sec := c.iniFile.Section("jiacrontabd")

		if !sec.HasKey(opt) {
			continue
		}

		key := sec.Key(opt)
		switch field.Type.Kind() {
		case reflect.Bool:
			v, err := key.Bool()
			if err != nil {
				log.Errorf("cannot resolve ini field %s err(%v)", opt, err)
			}
			val.Field(i).SetBool(v)
		case reflect.String:
			val.Field(i).SetString(key.String())
		case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
			if v, err := key.Int64(); err != nil {
				log.Errorf("cannot convert to int64 type (%s)", err)
			} else {
				val.Field(i).SetInt(v)
			}
		}
	}

	_, port, _ := net.SplitHostPort(c.ListenAddr)
	c.BoardcastAddr = util.InternalIP() + ":" + port

	return nil
}

func NewConfig() *Config {
	return &Config{
		LogLevel:            "warn",
		VerboseJobLog:       true,
		ListenAddr:          "127.0.0.1:20001",
		AdminAddr:           "127.0.0.1:20003",
		LogPath:             "./logs",
		AutoCleanTaskLog:    true,
		NodeName:            util.GetHostname(),
		CfgPath:             "./jiacrontabd.ini",
		DriverName:          "sqlite3",
		DSN:                 "data/jiacrontabd.db",
		ClientAliveInterval: 30,
	}
}

func (c *Config) loadConfig(path string) *ini.File {
	if !file.Exist(path) {
		f, err := file.CreateFile(path)
		if err != nil {
			panic(err)
		}
		f.Close()
	}

	iniFile, err := ini.Load(path)
	if err != nil {
		panic(err)
	}
	return iniFile
}
<commit_msg>修复jiacrontabd广播地址无效<commit_after>package jiacrontabd

import (
	"jiacrontab/pkg/file"
	"jiacrontab/pkg/util"
	"net"
	"reflect"

	"github.com/iwannay/log"

	ini "gopkg.in/ini.v1"
)

const (
	appname = "jiacrontabd"
)

type Config struct {
	LogLevel            string `opt:"log_level"`
	VerboseJobLog       bool   `opt:"verbose_job_log"`
	ListenAddr          string `opt:"listen_addr"`
	AdminAddr           string `opt:"admin_addr"`
	LogPath             string `opt:"log_path"`
	AutoCleanTaskLog    bool   `opt:"auto_clean_task_log"`
	NodeName            string `opt:"node_name"`
	BoardcastAddr       string `opt:"boardcast_addr"`
	ClientAliveInterval int    `opt:"client_alive_interval"`
	CfgPath             string
	Debug               bool `opt:"debug"`
	iniFile             *ini.File
	DriverName          string `opt:"driver_name"`
	DSN                 string `opt:"dsn"`
}

func (c *Config) Resolve() error {
	c.iniFile = c.loadConfig(c.CfgPath)

	val := reflect.ValueOf(c).Elem()
	typ := val.Type()

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		opt := field.Tag.Get("opt")
		if opt == "" {
			continue
		}
		sec := c.iniFile.Section("jiacrontabd")

		if !sec.HasKey(opt) {
			continue
		}

		key := sec.Key(opt)
		switch field.Type.Kind() {
		case reflect.Bool:
			v, err := key.Bool()
			if err != nil {
				log.Errorf("cannot resolve ini field %s err(%v)", opt, err)
			}
			val.Field(i).SetBool(v)
		case reflect.String:
			val.Field(i).SetString(key.String())
		case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8:
			if v, err := key.Int64(); err != nil {
				log.Errorf("cannot convert to int64 type (%s)", err)
			} else {
				val.Field(i).SetInt(v)
			}
		}
	}

	if c.BoardcastAddr == "" {
		_, port, _ := net.SplitHostPort(c.ListenAddr)
		c.BoardcastAddr = util.InternalIP() + ":" + port
	}

	return nil
}

func NewConfig() *Config {
	return &Config{
		LogLevel:            "warn",
		VerboseJobLog:       true,
		ListenAddr:          "127.0.0.1:20001",
		AdminAddr:           "127.0.0.1:20003",
		LogPath:             "./logs",
		AutoCleanTaskLog:    true,
		NodeName:            util.GetHostname(),
		CfgPath:             "./jiacrontabd.ini",
		DriverName:          "sqlite3",
		DSN:                 "data/jiacrontabd.db",
		ClientAliveInterval: 30,
	}
}

func (c *Config) loadConfig(path string) *ini.File {
	if !file.Exist(path) {
		f, err := file.CreateFile(path)
		if err != nil {
			panic(err)
		}
		f.Close()
	}

	iniFile, err := ini.Load(path)
	if err != nil {
		panic(err)
	}
	return iniFile
}
<|endoftext|>