<commit_before>// Copyright 2010 The Walk 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 walk

import (
	"os"
	"sort"
)

type Orientation byte

const (
	Horizontal Orientation = iota
	Vertical
)

type BoxLayout struct {
	container            Container
	margins              Margins
	spacing              int
	orientation          Orientation
	widget2StretchFactor map[*WidgetBase]int
	resetNeeded          bool
}

func newBoxLayout(orientation Orientation) *BoxLayout {
	return &BoxLayout{
		orientation:          orientation,
		widget2StretchFactor: make(map[*WidgetBase]int),
	}
}

func NewHBoxLayout() *BoxLayout {
	return newBoxLayout(Horizontal)
}

func NewVBoxLayout() *BoxLayout {
	return newBoxLayout(Vertical)
}

func (l *BoxLayout) Container() Container {
	return l.container
}

func (l *BoxLayout) SetContainer(value Container) {
	if value != l.container {
		if l.container != nil {
			l.container.SetLayout(nil)
		}

		l.container = value

		if value != nil && value.Layout() != Layout(l) {
			value.SetLayout(l)

			l.Update(true)
		}
	}
}

func (l *BoxLayout) Margins() Margins {
	return l.margins
}

func (l *BoxLayout) SetMargins(value Margins) os.Error {
	if value.HNear < 0 || value.VNear < 0 || value.HFar < 0 || value.VFar < 0 {
		return newError("margins must be positive")
	}

	l.margins = value

	return nil
}

func (l *BoxLayout) Orientation() Orientation {
	return l.orientation
}

func (l *BoxLayout) SetOrientation(value Orientation) os.Error {
	if value != l.orientation {
		switch value {
		case Horizontal, Vertical:

		default:
			return newError("invalid Orientation value")
		}

		l.orientation = value

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) Spacing() int {
	return l.spacing
}

func (l *BoxLayout) SetSpacing(value int) os.Error {
	if value != l.spacing {
		if value < 0 {
			return newError("spacing cannot be negative")
		}

		l.spacing = value

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) StretchFactor(widget Widget) int {
	if factor, ok := l.widget2StretchFactor[widget.BaseWidget()]; ok {
		return factor
	}

	return 1
}

func (l *BoxLayout) SetStretchFactor(widget Widget, factor int) os.Error {
	if factor != l.StretchFactor(widget) {
		if l.container == nil {
			return newError("container required")
		}
		if !l.container.Children().containsHandle(widget.BaseWidget().hWnd) {
			return newError("unknown widget")
		}
		if factor < 1 {
			return newError("factor must be >= 1")
		}

		l.widget2StretchFactor[widget.BaseWidget()] = factor

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) cleanupStretchFactors() {
	widgets := l.container.Children()

	for widget, _ := range l.widget2StretchFactor {
		if !widgets.containsHandle(widget.BaseWidget().hWnd) {
			l.widget2StretchFactor[widget.BaseWidget()] = 0, false
		}
	}
}

type widgetInfo struct {
	index   int
	minSize int
	maxSize int
	stretch int
	widget  Widget
}

type widgetInfoList []widgetInfo

func (l widgetInfoList) Len() int {
	return len(l)
}

func (l widgetInfoList) Less(i, j int) bool {
	_, iIsSpacer := l[i].widget.(*Spacer)
	_, jIsSpacer := l[j].widget.(*Spacer)

	if iIsSpacer == jIsSpacer {
		minDiff := l[i].minSize - l[j].minSize

		if minDiff == 0 {
			return l[i].maxSize/l[i].stretch < l[j].maxSize/l[j].stretch
		}

		return minDiff > 0
	}

	return jIsSpacer
}

func (l widgetInfoList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l *BoxLayout) MinSize() Size {
	if l.container == nil {
		return Size{}
	}

	// Begin by finding out which widgets we care about.
	children := l.container.Children()
	widgets := make([]Widget, 0, children.Len())

	for i := 0; i < cap(widgets); i++ {
		widget := children.At(i)

		ps := widget.PreferredSize()
		if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
			continue
		}

		widgets = append(widgets, widget)
	}

	// Prepare some useful data.
	sizes := make([]int, len(widgets))
	var s2 int

	for i, widget := range widgets {
		max := widget.MaxSize()
		pref := widget.PreferredSize()

		if l.orientation == Horizontal {
			if max.Width > 0 {
				sizes[i] = mini(pref.Width, max.Width)
			} else {
				sizes[i] = pref.Width
			}

			if pref.Height > s2 {
				s2 = pref.Height
			}
		} else {
			if max.Height > 0 {
				sizes[i] = mini(pref.Height, max.Height)
			} else {
				sizes[i] = pref.Height
			}

			if pref.Width > s2 {
				s2 = pref.Width
			}
		}
	}

	s1 := l.spacing * (len(widgets) - 1)

	if l.orientation == Horizontal {
		s1 += l.margins.HNear + l.margins.HFar
		s2 += l.margins.VNear + l.margins.VFar
	} else {
		s1 += l.margins.VNear + l.margins.VFar
		s2 += l.margins.HNear + l.margins.HFar
	}

	for _, s := range sizes {
		s1 += s
	}

	if l.orientation == Horizontal {
		return Size{s1, s2}
	}

	return Size{s2, s1}
}

func (l *BoxLayout) Update(reset bool) os.Error {
	if l.container == nil {
		return newError("container required")
	}

	if reset {
		l.resetNeeded = true
	}

	if l.container.Suspended() {
		return nil
	}

	if l.resetNeeded {
		l.resetNeeded = false

		// Make GC happy.
		l.cleanupStretchFactors()
	}

	// Begin by finding out which widgets we care about.
	children := l.container.Children()
	widgets := make([]Widget, 0, children.Len())

	for i := 0; i < cap(widgets); i++ {
		widget := children.At(i)

		ps := widget.PreferredSize()
		if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
			continue
		}

		widgets = append(widgets, widget)
	}

	// Prepare some useful data.
	var nonSpacerCount int
	var stretchFactorsTotal [2]int
	stretchFactors := make([]int, len(widgets))
	var minSizesRemaining int
	minSizes := make([]int, len(widgets))
	maxSizes := make([]int, len(widgets))
	sizes := make([]int, len(widgets))
	prefSizes2 := make([]int, len(widgets))
	canGrow2 := make([]bool, len(widgets))
	sortedWidgetInfo := widgetInfoList(make([]widgetInfo, len(widgets)))

	for i, widget := range widgets {
		sf := l.widget2StretchFactor[widget.BaseWidget()]
		if sf == 0 {
			sf = 1
		}
		stretchFactors[i] = sf

		flags := widget.LayoutFlags()

		min := widget.MinSize()
		max := widget.MaxSize()
		pref := widget.PreferredSize()

		if l.orientation == Horizontal {
			canGrow2[i] = flags&VGrow > 0

			if min.Width > 0 {
				minSizes[i] = min.Width
			} else if pref.Width > 0 && flags&HShrink == 0 {
				minSizes[i] = pref.Width
			}

			if max.Width > 0 {
				maxSizes[i] = max.Width
			} else if pref.Width > 0 && flags&HGrow == 0 {
				maxSizes[i] = pref.Width
			} else {
				maxSizes[i] = 32768
			}

			prefSizes2[i] = pref.Height
		} else {
			canGrow2[i] = flags&HGrow > 0

			if min.Height > 0 {
				minSizes[i] = min.Height
			} else if pref.Height > 0 && flags&VShrink == 0 {
				minSizes[i] = pref.Height
			}

			if max.Height > 0 {
				maxSizes[i] = max.Height
			} else if pref.Height > 0 && flags&VGrow == 0 {
				maxSizes[i] = pref.Height
			} else {
				maxSizes[i] = 32768
			}

			prefSizes2[i] = pref.Width
		}

		sortedWidgetInfo[i].index = i
		sortedWidgetInfo[i].minSize = minSizes[i]
		sortedWidgetInfo[i].maxSize = maxSizes[i]
		sortedWidgetInfo[i].stretch = sf
		sortedWidgetInfo[i].widget = widget

		minSizesRemaining += minSizes[i]

		if _, isSpacer := widget.(*Spacer); !isSpacer {
			nonSpacerCount++
			stretchFactorsTotal[0] += sf
		} else {
			stretchFactorsTotal[1] += sf
		}
	}

	sort.Sort(sortedWidgetInfo)

	cb := l.container.ClientBounds()
	var start1, start2, space1, space2 int
	if l.orientation == Horizontal {
		start1 = cb.X + l.margins.HNear
		start2 = cb.Y + l.margins.VNear
		space1 = cb.Width - l.margins.HNear - l.margins.HFar
		space2 = cb.Height - l.margins.VNear - l.margins.VFar
	} else {
		start1 = cb.Y + l.margins.VNear
		start2 = cb.X + l.margins.HNear
		space1 = cb.Height - l.margins.VNear - l.margins.VFar
		space2 = cb.Width - l.margins.HNear - l.margins.HFar
	}

	// Now calculate widget primary axis sizes.
	spacingRemaining := l.spacing * (len(widgets) - 1)

	offsets := [2]int{0, nonSpacerCount}
	counts := [2]int{nonSpacerCount, len(widgets) - nonSpacerCount}

	for i := 0; i < 2; i++ {
		stretchFactorsRemaining := stretchFactorsTotal[i]

		for j := 0; j < counts[i]; j++ {
			info := sortedWidgetInfo[offsets[i]+j]
			k := info.index

			stretch := stretchFactors[k]
			min := info.minSize
			max := info.maxSize
			size := min

			if min < max {
				excessSpace := float64(space1 - minSizesRemaining - spacingRemaining)
				size += int(excessSpace * float64(stretch) / float64(stretchFactorsRemaining))
				if size < min {
					size = min
				} else if size > max {
					size = max
				}
			}

			sizes[k] = size

			minSizesRemaining -= min
			stretchFactorsRemaining -= stretch
			space1 -= (size + l.spacing)
			spacingRemaining -= l.spacing
		}
	}

	// Finally position widgets.
	p1 := start1
	for i, widget := range widgets {
		s1 := sizes[i]

		var s2 int
		if canGrow2[i] {
			s2 = space2
		} else {
			s2 = prefSizes2[i]
		}

		p2 := start2 + (space2-s2)/2

		if l.orientation == Horizontal {
			widget.SetBounds(Rectangle{p1, p2, s1, s2})
		} else {
			widget.SetBounds(Rectangle{p2, p1, s2, s1})
		}

		p1 += s1 + l.spacing
	}

	return nil
}
<commit_msg>BoxLayout: Account for excess space when positioning widgets<commit_after>// Copyright 2010 The Walk 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 walk

import (
	"os"
	"sort"
)

type Orientation byte

const (
	Horizontal Orientation = iota
	Vertical
)

type BoxLayout struct {
	container            Container
	margins              Margins
	spacing              int
	orientation          Orientation
	widget2StretchFactor map[*WidgetBase]int
	resetNeeded          bool
}

func newBoxLayout(orientation Orientation) *BoxLayout {
	return &BoxLayout{
		orientation:          orientation,
		widget2StretchFactor: make(map[*WidgetBase]int),
	}
}

func NewHBoxLayout() *BoxLayout {
	return newBoxLayout(Horizontal)
}

func NewVBoxLayout() *BoxLayout {
	return newBoxLayout(Vertical)
}

func (l *BoxLayout) Container() Container {
	return l.container
}

func (l *BoxLayout) SetContainer(value Container) {
	if value != l.container {
		if l.container != nil {
			l.container.SetLayout(nil)
		}

		l.container = value

		if value != nil && value.Layout() != Layout(l) {
			value.SetLayout(l)

			l.Update(true)
		}
	}
}

func (l *BoxLayout) Margins() Margins {
	return l.margins
}

func (l *BoxLayout) SetMargins(value Margins) os.Error {
	if value.HNear < 0 || value.VNear < 0 || value.HFar < 0 || value.VFar < 0 {
		return newError("margins must be positive")
	}

	l.margins = value

	return nil
}

func (l *BoxLayout) Orientation() Orientation {
	return l.orientation
}

func (l *BoxLayout) SetOrientation(value Orientation) os.Error {
	if value != l.orientation {
		switch value {
		case Horizontal, Vertical:

		default:
			return newError("invalid Orientation value")
		}

		l.orientation = value

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) Spacing() int {
	return l.spacing
}

func (l *BoxLayout) SetSpacing(value int) os.Error {
	if value != l.spacing {
		if value < 0 {
			return newError("spacing cannot be negative")
		}

		l.spacing = value

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) StretchFactor(widget Widget) int {
	if factor, ok := l.widget2StretchFactor[widget.BaseWidget()]; ok {
		return factor
	}

	return 1
}

func (l *BoxLayout) SetStretchFactor(widget Widget, factor int) os.Error {
	if factor != l.StretchFactor(widget) {
		if l.container == nil {
			return newError("container required")
		}
		if !l.container.Children().containsHandle(widget.BaseWidget().hWnd) {
			return newError("unknown widget")
		}
		if factor < 1 {
			return newError("factor must be >= 1")
		}

		l.widget2StretchFactor[widget.BaseWidget()] = factor

		l.Update(false)
	}

	return nil
}

func (l *BoxLayout) cleanupStretchFactors() {
	widgets := l.container.Children()

	for widget, _ := range l.widget2StretchFactor {
		if !widgets.containsHandle(widget.BaseWidget().hWnd) {
			l.widget2StretchFactor[widget.BaseWidget()] = 0, false
		}
	}
}

type widgetInfo struct {
	index   int
	minSize int
	maxSize int
	stretch int
	widget  Widget
}

type widgetInfoList []widgetInfo

func (l widgetInfoList) Len() int {
	return len(l)
}

func (l widgetInfoList) Less(i, j int) bool {
	_, iIsSpacer := l[i].widget.(*Spacer)
	_, jIsSpacer := l[j].widget.(*Spacer)

	if iIsSpacer == jIsSpacer {
		minDiff := l[i].minSize - l[j].minSize

		if minDiff == 0 {
			return l[i].maxSize/l[i].stretch < l[j].maxSize/l[j].stretch
		}

		return minDiff > 0
	}

	return jIsSpacer
}

func (l widgetInfoList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

func (l *BoxLayout) MinSize() Size {
	if l.container == nil {
		return Size{}
	}

	// Begin by finding out which widgets we care about.
	children := l.container.Children()
	widgets := make([]Widget, 0, children.Len())

	for i := 0; i < cap(widgets); i++ {
		widget := children.At(i)

		ps := widget.PreferredSize()
		if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
			continue
		}

		widgets = append(widgets, widget)
	}

	// Prepare some useful data.
	sizes := make([]int, len(widgets))
	var s2 int

	for i, widget := range widgets {
		max := widget.MaxSize()
		pref := widget.PreferredSize()

		if l.orientation == Horizontal {
			if max.Width > 0 {
				sizes[i] = mini(pref.Width, max.Width)
			} else {
				sizes[i] = pref.Width
			}

			if pref.Height > s2 {
				s2 = pref.Height
			}
		} else {
			if max.Height > 0 {
				sizes[i] = mini(pref.Height, max.Height)
			} else {
				sizes[i] = pref.Height
			}

			if pref.Width > s2 {
				s2 = pref.Width
			}
		}
	}

	s1 := l.spacing * (len(widgets) - 1)

	if l.orientation == Horizontal {
		s1 += l.margins.HNear + l.margins.HFar
		s2 += l.margins.VNear + l.margins.VFar
	} else {
		s1 += l.margins.VNear + l.margins.VFar
		s2 += l.margins.HNear + l.margins.HFar
	}

	for _, s := range sizes {
		s1 += s
	}

	if l.orientation == Horizontal {
		return Size{s1, s2}
	}

	return Size{s2, s1}
}

func (l *BoxLayout) Update(reset bool) os.Error {
	if l.container == nil {
		return newError("container required")
	}

	if reset {
		l.resetNeeded = true
	}

	if l.container.Suspended() {
		return nil
	}

	if l.resetNeeded {
		l.resetNeeded = false

		// Make GC happy.
		l.cleanupStretchFactors()
	}

	// Begin by finding out which widgets we care about.
	children := l.container.Children()
	widgets := make([]Widget, 0, children.Len())

	for i := 0; i < cap(widgets); i++ {
		widget := children.At(i)

		ps := widget.PreferredSize()
		if ps.Width == 0 && ps.Height == 0 && widget.LayoutFlags() == 0 {
			continue
		}

		widgets = append(widgets, widget)
	}

	// Prepare some useful data.
	var nonSpacerCount int
	var stretchFactorsTotal [2]int
	stretchFactors := make([]int, len(widgets))
	var minSizesRemaining int
	minSizes := make([]int, len(widgets))
	maxSizes := make([]int, len(widgets))
	sizes := make([]int, len(widgets))
	prefSizes2 := make([]int, len(widgets))
	canGrow2 := make([]bool, len(widgets))
	sortedWidgetInfo := widgetInfoList(make([]widgetInfo, len(widgets)))

	for i, widget := range widgets {
		sf := l.widget2StretchFactor[widget.BaseWidget()]
		if sf == 0 {
			sf = 1
		}
		stretchFactors[i] = sf

		flags := widget.LayoutFlags()

		min := widget.MinSize()
		max := widget.MaxSize()
		pref := widget.PreferredSize()

		if l.orientation == Horizontal {
			canGrow2[i] = flags&VGrow > 0

			if min.Width > 0 {
				minSizes[i] = min.Width
			} else if pref.Width > 0 && flags&HShrink == 0 {
				minSizes[i] = pref.Width
			}

			if max.Width > 0 {
				maxSizes[i] = max.Width
			} else if pref.Width > 0 && flags&HGrow == 0 {
				maxSizes[i] = pref.Width
			} else {
				maxSizes[i] = 32768
			}

			prefSizes2[i] = pref.Height
		} else {
			canGrow2[i] = flags&HGrow > 0

			if min.Height > 0 {
				minSizes[i] = min.Height
			} else if pref.Height > 0 && flags&VShrink == 0 {
				minSizes[i] = pref.Height
			}

			if max.Height > 0 {
				maxSizes[i] = max.Height
			} else if pref.Height > 0 && flags&VGrow == 0 {
				maxSizes[i] = pref.Height
			} else {
				maxSizes[i] = 32768
			}

			prefSizes2[i] = pref.Width
		}

		sortedWidgetInfo[i].index = i
		sortedWidgetInfo[i].minSize = minSizes[i]
		sortedWidgetInfo[i].maxSize = maxSizes[i]
		sortedWidgetInfo[i].stretch = sf
		sortedWidgetInfo[i].widget = widget

		minSizesRemaining += minSizes[i]

		if _, isSpacer := widget.(*Spacer); !isSpacer {
			nonSpacerCount++
			stretchFactorsTotal[0] += sf
		} else {
			stretchFactorsTotal[1] += sf
		}
	}

	sort.Sort(sortedWidgetInfo)

	cb := l.container.ClientBounds()
	var start1, start2, space1, space2 int
	if l.orientation == Horizontal {
		start1 = cb.X + l.margins.HNear
		start2 = cb.Y + l.margins.VNear
		space1 = cb.Width - l.margins.HNear - l.margins.HFar
		space2 = cb.Height - l.margins.VNear - l.margins.VFar
	} else {
		start1 = cb.Y + l.margins.VNear
		start2 = cb.X + l.margins.HNear
		space1 = cb.Height - l.margins.VNear - l.margins.VFar
		space2 = cb.Width - l.margins.HNear - l.margins.HFar
	}

	// Now calculate widget primary axis sizes.
	spacingRemaining := l.spacing * (len(widgets) - 1)

	offsets := [2]int{0, nonSpacerCount}
	counts := [2]int{nonSpacerCount, len(widgets) - nonSpacerCount}

	for i := 0; i < 2; i++ {
		stretchFactorsRemaining := stretchFactorsTotal[i]

		for j := 0; j < counts[i]; j++ {
			info := sortedWidgetInfo[offsets[i]+j]
			k := info.index

			stretch := stretchFactors[k]
			min := info.minSize
			max := info.maxSize
			size := min

			if min < max {
				excessSpace := float64(space1 - minSizesRemaining - spacingRemaining)
				size += int(excessSpace * float64(stretch) / float64(stretchFactorsRemaining))
				if size < min {
					size = min
				} else if size > max {
					size = max
				}
			}

			sizes[k] = size

			minSizesRemaining -= min
			stretchFactorsRemaining -= stretch
			space1 -= (size + l.spacing)
			spacingRemaining -= l.spacing
		}
	}

	// Finally position widgets.
	excessTotal := space1 - minSizesRemaining - spacingRemaining
	excessShare := excessTotal / (len(widgets) + 1)
	p1 := start1
	for i, widget := range widgets {
		p1 += excessShare
		s1 := sizes[i]

		var s2 int
		if canGrow2[i] {
			s2 = space2
		} else {
			s2 = prefSizes2[i]
		}

		p2 := start2 + (space2-s2)/2

		if l.orientation == Horizontal {
			widget.SetBounds(Rectangle{p1, p2, s1, s2})
		} else {
			widget.SetBounds(Rectangle{p2, p1, s2, s1})
		}

		p1 += s1 + l.spacing
	}

	return nil
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	rtl "github.com/jpoirier/gortlsdr"
	"log"
)

func main() {
	indx := -1
	c := rtl.GetDeviceCount()
	if c == 0 {
		log.Fatal("No devices found.\n")
	}

	for i := 0; i < c; i++ {
		m, p, s, err := rtl.GetDeviceUsbStrings(i)
		if err != 0 {
			indx++
			log.Printf("err: %d, m: %s, p: %s, s: %s\n", err, m, p, s)
		}
	}

	if indx != -1 {
		log.Fatal("No devices found.\n")
	}

	log.Printf("Using device indx %d\n", 0)
	dev, err := rtl.Open(0)
	if err != 0 {
		log.Fatal("Failed to open the device\n")
	}
	defer dev.Close()

	g := dev.GetTunerGains()
	for i, j := range g {
		log.Printf("Gain %d: %d\n", i, j)
	}

	log.Printf("Setting sample rate to %d\n", rtl.DEFAULT_SAMPLE_RATE)
	err = dev.SetSampleRate(rtl.DEFAULT_SAMPLE_RATE)
	if err != 0 {
		log.Fatal("SetSampleRate failed, exiting\n")
	}

	err = dev.SetTestMode(1)
	if err == -1 {
		log.Fatal("Setting test mode failed, exiting\n")
	}

	err = dev.ResetBuffer()
	if err == -1 {
		log.Fatal("Buffer reset failed, exiting\n")
	}

	var buffer []byte = make([]uint8, rtl.DEFAULT_BUF_LENGTH)
	n_read, err := dev.ReadSync(buffer, rtl.DEFAULT_BUF_LENGTH)
	if err == -1 {
		log.Fatal("ReadSync failed, exiting\n")
	}
	if n_read < rtl.DEFAULT_BUF_LENGTH {
		log.Fatal("ReadSync short read, samples lost, exiting\n")
	}
	log.Println("ReadSync successful")
	// log.Println(buffer)

	log.Printf("Closing...\n")
}
<commit_msg>added more function calls to the example code<commit_after>// +build ignore

package main

import (
	rtl "github.com/jpoirier/gortlsdr"
	"log"
)

func main() {
	c := rtl.GetDeviceCount()
	if c == 0 {
		log.Fatal("No devices found.\n")
	}

	for i := 0; i < c; i++ {
		m, p, s, err := rtl.GetDeviceUsbStrings(i)
		log.Printf("Device USB Striing - err: %d, m: %s, p: %s, s: %s\n", err, m, p, s)
	}

	log.Printf("Device name: %s\n", rtl.GetDeviceName(0))

	log.Printf("Using device indx %d\n", 0)
	dev, err := rtl.Open(0)
	if err != 0 {
		log.Fatal("Failed to open the device\n")
	}
	defer dev.Close()

	m, p, s, err := dev.GetUsbStrings()
	if err == -1 {
		log.Fatal("GetUsbStrings failed, exiting\n")
	}
	log.Printf("USB strings - m: %s, p: %s, s: %s\n", m, p, s)

	g := dev.GetTunerGains()
	for i, j := range g {
		log.Printf("Gain %d: %d\n", i, j)
	}

	log.Printf("Setting sample rate to %d\n", rtl.DEFAULT_SAMPLE_RATE)
	err = dev.SetSampleRate(rtl.DEFAULT_SAMPLE_RATE)
	if err != 0 {
		log.Fatal("SetSampleRate failed, exiting\n")
	}

	err = dev.SetTestMode(1)
	if err == -1 {
		log.Fatal("Setting test mode failed, exiting\n")
	}

	err = dev.ResetBuffer()
	if err == -1 {
		log.Fatal("Buffer reset failed, exiting\n")
	}

	var buffer []byte = make([]uint8, rtl.DEFAULT_BUF_LENGTH)
	n_read, err := dev.ReadSync(buffer, rtl.DEFAULT_BUF_LENGTH)
	if err == -1 {
		log.Fatal("ReadSync failed, exiting\n")
	}
	if n_read < rtl.DEFAULT_BUF_LENGTH {
		log.Fatal("ReadSync short read, samples lost, exiting\n")
	}
	log.Println("ReadSync successful")
	// log.Println(buffer)

	log.Printf("Closing...\n")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	c "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	"github.com/flynn/flynn/appliance/postgresql/state"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/postgres"
)

type PostgresSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&PostgresSuite{})

// Check postgres config to avoid regressing on https://github.com/flynn/flynn/issues/101
func (s *PostgresSuite) TestSSLRenegotiationLimit(t *c.C) {
	query := flynn(t, "/", "-a", "controller", "pg", "psql", "--", "-c", "SHOW ssl_renegotiation_limit")
	t.Assert(query, SuccessfulOutputContains, "ssl_renegotiation_limit \n-------------------------\n 0\n(1 row)")
}

func (s *PostgresSuite) TestDumpRestore(t *c.C) {
	r := s.newGitRepo(t, "empty")
	t.Assert(r.flynn("create"), Succeeds)

	t.Assert(r.flynn("resource", "add", "postgres"), Succeeds)

	t.Assert(r.flynn("pg", "psql", "--", "-c",
		"CREATE table foos (data text); INSERT INTO foos (data) VALUES ('foobar')"), Succeeds)

	file := filepath.Join(t.MkDir(), "db.dump")
	t.Assert(r.flynn("pg", "dump", "-f", file), Succeeds)
	t.Assert(r.flynn("pg", "psql", "--", "-c", "DROP TABLE foos"), Succeeds)

	r.flynn("pg", "restore", "-f", file)

	query := r.flynn("pg", "psql", "--", "-c", "SELECT * FROM foos")
	t.Assert(query, SuccessfulOutputContains, "foobar")
}

type pgDeploy struct {
	name     string
	pgJobs   int
	webJobs  int
	expected func(string, string) []expectedPgState
}

type expectedPgState struct {
	Primary, Sync string
	Async         []string
}

func (s *PostgresSuite) TestDeployMultipleAsync(t *c.C) {
	s.testDeploy(t, &pgDeploy{
		name:    "postgres-multiple-async",
		pgJobs:  5,
		webJobs: 2,
		expected: func(oldRelease, newRelease string) []expectedPgState {
			return []expectedPgState{
				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, oldRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, newRelease}},

				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease, newRelease}},

				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease, newRelease}},

				// new Async[3], kill Sync
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease}},

				// new Async[3], kill Primary
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease, newRelease}},
				{Primary: newRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease}},
			}
		},
	})
}

func (s *PostgresSuite) TestDeploySingleAsync(t *c.C) {
	s.testDeploy(t, &pgDeploy{
		name:    "postgres-single-async",
		pgJobs:  3,
		webJobs: 2,
		expected: func(oldRelease, newRelease string) []expectedPgState {
			return []expectedPgState{
				// new Async[1], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease}},

				// new Async[1], kill Sync
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease}},
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease}},

				// new Async[1], kill Primary
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease}},
				{Primary: newRelease, Sync: newRelease, Async: []string{newRelease}},
			}
		},
	})
}

func (s *PostgresSuite) testDeploy(t *c.C, d *pgDeploy) {
	// create postgres app
	client := s.controllerClient(t)
	app := &ct.App{Name: d.name, Strategy: "postgres"}
	t.Assert(client.CreateApp(app), c.IsNil)

	// copy release from default postgres app
	release, err := client.GetAppRelease("postgres")
	t.Assert(err, c.IsNil)
	release.ID = ""
	proc := release.Processes["postgres"]
	delete(proc.Env, "SINGLETON")
	proc.Env["FLYNN_POSTGRES"] = d.name
	proc.Service = d.name
	release.Processes["postgres"] = proc
	t.Assert(client.CreateRelease(release), c.IsNil)
	t.Assert(client.SetAppRelease(app.ID, release.ID), c.IsNil)
	oldRelease := release.ID

	// create formation
	discEvents := make(chan *discoverd.Event)
	discStream, err := s.discoverdClient(t).Service(d.name).Watch(discEvents)
	t.Assert(err, c.IsNil)
	defer discStream.Close()
	jobEvents := make(chan *ct.JobEvent)
	jobStream, err := client.StreamJobEvents(d.name, jobEvents)
	t.Assert(err, c.IsNil)
	defer jobStream.Close()
	t.Assert(client.PutFormation(&ct.Formation{
		AppID:     app.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"postgres": d.pgJobs, "web": d.webJobs},
	}), c.IsNil)

	// watch cluster state changes
	type stateChange struct {
		state *state.State
		err   error
	}
	stateCh := make(chan stateChange)
	go func() {
		for event := range discEvents {
			if event.Kind != discoverd.EventKindServiceMeta {
				continue
			}
			var state state.State
			if err := json.Unmarshal(event.ServiceMeta.Data, &state); err != nil {
				stateCh <- stateChange{err: err}
				return
			}
			primary := ""
			if state.Primary != nil {
				primary = state.Primary.Addr
			}
			sync := ""
			if state.Sync != nil {
				sync = state.Sync.Addr
			}
			var async []string
			for _, a := range state.Async {
				async = append(async, a.Addr)
			}
			debugf(t, "got pg cluster state: index=%d primary=%s sync=%s async=%s",
				event.ServiceMeta.Index, primary, sync, strings.Join(async, ","))
			stateCh <- stateChange{state: &state}
		}
	}()

	// wait for correct cluster state and number of web processes
	var pgState state.State
	var webJobs int
	ready := func() bool {
		if webJobs != d.webJobs {
			return false
		}
		if pgState.Primary == nil {
			return false
		}
		if d.pgJobs > 1 && pgState.Sync == nil {
			return false
		}
		if d.pgJobs > 2 && len(pgState.Async) != d.pgJobs-2 {
			return false
		}
		return true
	}
	for {
		if ready() {
			break
		}
		select {
		case s := <-stateCh:
			t.Assert(s.err, c.IsNil)
			pgState = *s.state
		case e, ok := <-jobEvents:
			if !ok {
				t.Fatalf("job event stream closed: %s", jobStream.Err())
			}
			debugf(t, "got job event: %s %s %s", e.Type, e.JobID, e.State)
			if e.Type == "web" && e.State == "up" {
				webJobs++
			}
		case <-time.After(30 * time.Second):
			t.Fatal("timed out waiting for postgres formation")
		}
	}

	// connect to the db so we can test writes
	db := postgres.Wait(d.name, fmt.Sprintf("dbname=postgres user=flynn password=%s", release.Env["PGPASSWORD"]))
	dbname := "deploy-test"
	t.Assert(db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH OWNER = "flynn"`, dbname)), c.IsNil)
	db.Close()
	db, err = postgres.Open(d.name, fmt.Sprintf("dbname=%s user=flynn password=%s", dbname, release.Env["PGPASSWORD"]))
	t.Assert(err, c.IsNil)
	defer db.Close()
	t.Assert(db.Exec(`CREATE TABLE deploy_test ( data text)`), c.IsNil)
	assertWriteable := func() {
		debug(t, "writing to postgres database")
		t.Assert(db.Exec(`INSERT INTO deploy_test (data) VALUES ('data')`), c.IsNil)
	}

	// check currently writeable
	assertWriteable()

	// check a deploy completes with expected cluster state changes
	release.ID = ""
	t.Assert(client.CreateRelease(release), c.IsNil)
	newRelease := release.ID
	deployment, err := client.CreateDeployment(app.ID, newRelease)
	t.Assert(err, c.IsNil)
	deployEvents := make(chan *ct.DeploymentEvent)
	deployStream, err := client.StreamDeployment(deployment, deployEvents)
	t.Assert(err, c.IsNil)
	defer deployStream.Close()

	assertNextState := func(expected expectedPgState) {
		var state state.State
		select {
		case s := <-stateCh:
			t.Assert(s.err, c.IsNil)
			state = *s.state
		case <-time.After(60 * time.Second):
			t.Fatal("timed out waiting for postgres cluster state")
		}
		if state.Primary == nil {
			t.Fatal("no primary configured")
		}
		if state.Primary.Meta["FLYNN_RELEASE_ID"] != expected.Primary {
			t.Fatal("primary has incorrect release")
		}
		if expected.Sync == "" {
			return
		}
		if state.Sync == nil {
			t.Fatal("no sync configured")
		}
		if state.Sync.Meta["FLYNN_RELEASE_ID"] != expected.Sync {
			t.Fatal("sync has incorrect release")
		}
		if expected.Async == nil {
			return
		}
		if len(state.Async) != len(expected.Async) {
			t.Fatalf("expected %d asyncs, got %d", len(expected.Async), len(state.Async))
		}
		for i, release := range expected.Async {
			if state.Async[i].Meta["FLYNN_RELEASE_ID"] != release {
				t.Fatalf("async[%d] has incorrect release", i)
			}
		}
	}
	expected := d.expected(oldRelease, newRelease)
	var expectedIndex, newWebJobs int
loop:
	for {
		select {
		case e, ok := <-deployEvents:
			if !ok {
				t.Fatal("unexpected close of deployment event stream")
			}
			switch e.Status {
			case "complete":
				break loop
			case "failed":
				t.Fatalf("deployment failed: %s", e.Error)
			}
			debugf(t, "got deployment event: %s %s", e.JobType, e.JobState)
			if e.JobState != "up" && e.JobState != "down" {
				continue
			}
			switch e.JobType {
			case "postgres":
				assertNextState(expected[expectedIndex])
				expectedIndex++
			case "web":
				if e.JobState == "up" && e.ReleaseID == newRelease {
					newWebJobs++
				}
			}
		case <-time.After(2 * time.Minute):
			t.Fatal("timed out waiting for deployment")
		}
	}

	// check we have the correct number of new web jobs
	t.Assert(newWebJobs, c.Equals, d.webJobs)

	// check writeable now deploy is complete
	assertWriteable()
}
<commit_msg>test: Handle postgres states being skipped<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	c "github.com/flynn/flynn/Godeps/_workspace/src/github.com/flynn/go-check"
	"github.com/flynn/flynn/appliance/postgresql/state"
	ct "github.com/flynn/flynn/controller/types"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/pkg/postgres"
)

type PostgresSuite struct {
	Helper
}

var _ = c.ConcurrentSuite(&PostgresSuite{})

// Check postgres config to avoid regressing on https://github.com/flynn/flynn/issues/101
func (s *PostgresSuite) TestSSLRenegotiationLimit(t *c.C) {
	query := flynn(t, "/", "-a", "controller", "pg", "psql", "--", "-c", "SHOW ssl_renegotiation_limit")
	t.Assert(query, SuccessfulOutputContains, "ssl_renegotiation_limit \n-------------------------\n 0\n(1 row)")
}

func (s *PostgresSuite) TestDumpRestore(t *c.C) {
	r := s.newGitRepo(t, "empty")
	t.Assert(r.flynn("create"), Succeeds)

	t.Assert(r.flynn("resource", "add", "postgres"), Succeeds)

	t.Assert(r.flynn("pg", "psql", "--", "-c",
		"CREATE table foos (data text); INSERT INTO foos (data) VALUES ('foobar')"), Succeeds)

	file := filepath.Join(t.MkDir(), "db.dump")
	t.Assert(r.flynn("pg", "dump", "-f", file), Succeeds)
	t.Assert(r.flynn("pg", "psql", "--", "-c", "DROP TABLE foos"), Succeeds)

	r.flynn("pg", "restore", "-f", file)

	query := r.flynn("pg", "psql", "--", "-c", "SELECT * FROM foos")
	t.Assert(query, SuccessfulOutputContains, "foobar")
}

type pgDeploy struct {
	name     string
	pgJobs   int
	webJobs  int
	expected func(string, string) []expectedPgState
}

type expectedPgState struct {
	Primary, Sync string
	Async         []string
}

func (s *PostgresSuite) TestDeployMultipleAsync(t *c.C) {
	s.testDeploy(t, &pgDeploy{
		name:    "postgres-multiple-async",
		pgJobs:  5,
		webJobs: 2,
		expected: func(oldRelease, newRelease string) []expectedPgState {
			return []expectedPgState{
				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, oldRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, newRelease}},

				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, oldRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease, newRelease}},

				// new Async[3], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease, newRelease}},

				// new Async[3], kill Sync
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease, newRelease, newRelease}},
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease}},

				// new Async[3], kill Primary
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease, newRelease}},
				{Primary: newRelease, Sync: newRelease, Async: []string{newRelease, newRelease, newRelease}},
			}
		},
	})
}

func (s *PostgresSuite) TestDeploySingleAsync(t *c.C) {
	s.testDeploy(t, &pgDeploy{
		name:    "postgres-single-async",
		pgJobs:  3,
		webJobs: 2,
		expected: func(oldRelease, newRelease string) []expectedPgState {
			return []expectedPgState{
				// new Async[1], kill Async[0]
				{Primary: oldRelease, Sync: oldRelease, Async: []string{oldRelease, newRelease}},
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease}},

				// new Async[1], kill Sync
				{Primary: oldRelease, Sync: oldRelease, Async: []string{newRelease, newRelease}},
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease}},

				// new Async[1], kill Primary
				{Primary: oldRelease, Sync: newRelease, Async: []string{newRelease, newRelease}},
				{Primary: newRelease, Sync: newRelease, Async: []string{newRelease}},
			}
		},
	})
}

func (s *PostgresSuite) testDeploy(t *c.C, d *pgDeploy) {
	// create postgres app
	client := s.controllerClient(t)
	app := &ct.App{Name: d.name, Strategy: "postgres"}
	t.Assert(client.CreateApp(app), c.IsNil)

	// copy release from default postgres app
	release, err := client.GetAppRelease("postgres")
	t.Assert(err, c.IsNil)
	release.ID = ""
	proc := release.Processes["postgres"]
	delete(proc.Env, "SINGLETON")
	proc.Env["FLYNN_POSTGRES"] = d.name
	proc.Service = d.name
	release.Processes["postgres"] = proc
	t.Assert(client.CreateRelease(release), c.IsNil)
	t.Assert(client.SetAppRelease(app.ID, release.ID), c.IsNil)
	oldRelease := release.ID

	// create formation
	discEvents := make(chan *discoverd.Event)
	discStream, err := s.discoverdClient(t).Service(d.name).Watch(discEvents)
	t.Assert(err, c.IsNil)
	defer discStream.Close()
	jobEvents := make(chan *ct.JobEvent)
	jobStream, err := client.StreamJobEvents(d.name, jobEvents)
	t.Assert(err, c.IsNil)
	defer jobStream.Close()
	t.Assert(client.PutFormation(&ct.Formation{
		AppID:     app.ID,
		ReleaseID: release.ID,
		Processes: map[string]int{"postgres": d.pgJobs, "web": d.webJobs},
	}), c.IsNil)

	// watch cluster state changes
	type stateChange struct {
		state *state.State
		err   error
	}
	stateCh := make(chan stateChange)
	go func() {
		for event := range discEvents {
			if event.Kind != discoverd.EventKindServiceMeta {
				continue
			}
			var state state.State
			if err := json.Unmarshal(event.ServiceMeta.Data, &state); err != nil {
				stateCh <- stateChange{err: err}
				return
			}
			primary := ""
			if state.Primary != nil {
				primary = state.Primary.Addr
			}
			sync := ""
			if state.Sync != nil {
				sync = state.Sync.Addr
			}
			var async []string
			for _, a := range state.Async {
				async = append(async, a.Addr)
			}
			debugf(t, "got pg cluster state: index=%d primary=%s sync=%s async=%s",
				event.ServiceMeta.Index, primary, sync, strings.Join(async, ","))
			stateCh <- stateChange{state: &state}
		}
	}()

	// wait for correct cluster state and number of web processes
	var pgState state.State
	var webJobs int
	ready := func() bool {
		if webJobs != d.webJobs {
			return false
		}
		if pgState.Primary == nil {
			return false
		}
		if d.pgJobs > 1 && pgState.Sync == nil {
			return false
		}
		if d.pgJobs > 2 && len(pgState.Async) != d.pgJobs-2 {
			return false
		}
		return true
	}
	for {
		if ready() {
			break
		}
		select {
		case s := <-stateCh:
			t.Assert(s.err, c.IsNil)
			pgState = *s.state
		case e, ok := <-jobEvents:
			if !ok {
				t.Fatalf("job event stream closed: %s", jobStream.Err())
			}
			debugf(t, "got job event: %s %s %s", e.Type, e.JobID, e.State)
			if e.Type == "web" && e.State == "up" {
				webJobs++
			}
		case <-time.After(30 * time.Second):
			t.Fatal("timed out waiting for postgres formation")
		}
	}

	// connect to the db so we can test writes
	db := postgres.Wait(d.name, fmt.Sprintf("dbname=postgres user=flynn password=%s", release.Env["PGPASSWORD"]))
	dbname := "deploy-test"
	t.Assert(db.Exec(fmt.Sprintf(`CREATE DATABASE "%s" WITH OWNER = "flynn"`, dbname)), c.IsNil)
	db.Close()
	db, err = postgres.Open(d.name, fmt.Sprintf("dbname=%s user=flynn password=%s", dbname, release.Env["PGPASSWORD"]))
	t.Assert(err, c.IsNil)
	defer db.Close()
	t.Assert(db.Exec(`CREATE TABLE deploy_test ( data text)`), c.IsNil)
	assertWriteable := func() {
		debug(t, "writing to postgres database")
		t.Assert(db.Exec(`INSERT INTO deploy_test (data) VALUES ('data')`), c.IsNil)
	}

	// check currently writeable
	assertWriteable()

	// check a deploy completes with expected cluster state changes
	release.ID = ""
	t.Assert(client.CreateRelease(release), c.IsNil)
	newRelease := release.ID
	deployment, err := client.CreateDeployment(app.ID, newRelease)
	t.Assert(err, c.IsNil)
	deployEvents := make(chan *ct.DeploymentEvent)
	deployStream, err := client.StreamDeployment(deployment, deployEvents)
	t.Assert(err, c.IsNil)
	defer deployStream.Close()

	// assertNextState checks that the next state received is in the remaining states
	// that were expected, so handles the fact that some states don't happen, but the
	// states that do happen are expected and in-order.
	assertNextState := func(remaining []expectedPgState) int {
		var state state.State
		select {
		case s := <-stateCh:
			t.Assert(s.err, c.IsNil)
			state = *s.state
		case <-time.After(60 * time.Second):
			t.Fatal("timed out waiting for postgres cluster state")
		}
		if state.Primary == nil {
			t.Fatal("no primary configured")
		}
		log := func(format string, v ...interface{}) {
			debugf(t, "skipping expected state: %s", fmt.Sprintf(format, v...))
		}
	outer:
		for i, expected := range remaining {
			if state.Primary.Meta["FLYNN_RELEASE_ID"] != expected.Primary {
				log("primary has incorrect release")
				continue
			}
			if state.Sync == nil {
				if expected.Sync == "" {
					return i
				}
				log("state has no sync node")
				continue
			}
			if state.Sync.Meta["FLYNN_RELEASE_ID"] != expected.Sync {
				log("sync has incorrect release")
				continue
			}
			if state.Async == nil {
				if expected.Async == nil {
					return i
				}
				log("state has no async nodes")
				continue
			}
			if len(state.Async) != len(expected.Async) {
				log("expected %d asyncs, got %d", len(expected.Async), len(state.Async))
				continue
			}
			for i, release := range expected.Async {
				if state.Async[i].Meta["FLYNN_RELEASE_ID"] != release {
					log("async[%d] has incorrect release", i)
					continue outer
				}
			}
			return i
		}
		t.Fatal("unexpected pg state")
		return -1
	}
	expected := d.expected(oldRelease, newRelease)
	var expectedIndex, newWebJobs int
loop:
	for {
		select {
		case e, ok := <-deployEvents:
			if !ok {
				t.Fatal("unexpected close of deployment event stream")
			}
			switch e.Status {
			case "complete":
				break loop
			case "failed":
				t.Fatalf("deployment failed: %s", e.Error)
			}
			debugf(t, "got deployment event: %s %s", e.JobType, e.JobState)
			if e.JobState != "up" && e.JobState != "down" {
				continue
			}
			switch e.JobType {
			case "postgres":
				skipped := assertNextState(expected[expectedIndex:])
				expectedIndex += 1 + skipped
			case "web":
				if e.JobState == "up" && e.ReleaseID == newRelease {
					newWebJobs++
				}
			}
		case <-time.After(2 * time.Minute):
			t.Fatal("timed out waiting for deployment")
		}
	}

	// check we have the correct number of new web jobs
	t.Assert(newWebJobs, c.Equals, d.webJobs)

	// check writeable now deploy is complete
	assertWriteable()
}
<|endoftext|>


<commit_before>package presilo

import (
  "fmt"
  "bytes"
  "strings"
)

/*
  Generates valid Go code for a given schema.
*/
func GenerateGo(schema *ObjectSchema, module string) string {

  var ret bytes.Buffer

  ret.WriteString("package " + module)
  ret.WriteString("\n")
  ret.WriteString(generateGoImports(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoTypeDeclaration(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoConstructor(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoFunctions(schema))
  ret.WriteString("\n")

  return ret.String()
}

func generateGoImports(schema *ObjectSchema) string {

    var ret bytes.Buffer
    return ret.String()
}

func generateGoTypeDeclaration(schema *ObjectSchema) string {

    var ret bytes.Buffer

    ret.WriteString("type ")
    ret.WriteString(schema.GetTitle())
    ret.WriteString(" struct {\n")

    for propertyName, subschema := range schema.Properties {

      ret.WriteString("\tvar " + ToCamelCase(propertyName) + " ")
      ret.WriteString(generateGoTypeForSchema(subschema))
      ret.WriteString("\n")
    }

    ret.WriteString("}\n")
    return ret.String()
}

func generateGoFunctions(schema *ObjectSchema) string {

    var ret bytes.Buffer
    return ret.String()
}

func generateGoConstructor(schema *ObjectSchema) string {

  var ret bytes.Buffer
  var parameters, parameterNames []string
  var title, signature, parameterDefinition string

  for propertyName, subschema := range schema.Properties {

    propertyName = ToCamelCase(propertyName)

    ret.WriteString(propertyName)
    ret.WriteString(" ")
    ret.WriteString(generateGoTypeForSchema(subschema))

    parameterNames = append(parameterNames, propertyName)
    parameters = append(parameters, ret.String())
    ret.Reset()

  }

  // signature
  title = ToCamelCase(schema.Title)
  signature = fmt.Sprintf("func New%s(%s)(*%s) {\n", title, strings.Join(parameters, ","), title)
  ret.WriteString(signature)

  // body
  parameterDefinition = fmt.Sprintf("\tret := new(%s)\n", title)
  ret.WriteString(parameterDefinition)

  for _, propertyName := range parameterNames {

    parameterDefinition = fmt.Sprintf("\tret.%s = %s\n", propertyName, propertyName)
    ret.WriteString(parameterDefinition)
  }

  ret.WriteString("\treturn ret\n}\n\n")
  return ret.String()
}

func generateGoIntegerFunctions(schema *IntegerSchema) string {

  var ret bytes.Buffer

  if(!schema.HasConstraints()) {
    return ""
  }

  if(schema.Minimum != nil) {

  }

  return ret.String()
}

func generateGoStringFunctions(schema *StringSchema) string {

  var ret bytes.Buffer

  return ret.String()
}

func generateGoTypeForSchema(schema TypeSchema) string {

  switch schema.GetSchemaType() {
  case SCHEMATYPE_STRING:
    return "string"
  case SCHEMATYPE_INTEGER:
    return "int"
  case SCHEMATYPE_OBJECT:
    return "*" + ToCamelCase(schema.GetTitle())
  }

  return "interface{}"
}
<commit_msg>Ensured that only required properties are parameters of go ctor<commit_after>package presilo

import (
  "fmt"
  "bytes"
  "strings"
)

/*
  Generates valid Go code for a given schema.
*/
func GenerateGo(schema *ObjectSchema, module string) string {

  var ret bytes.Buffer

  ret.WriteString("package " + module)
  ret.WriteString("\n")
  ret.WriteString(generateGoImports(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoTypeDeclaration(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoConstructor(schema))
  ret.WriteString("\n")
  ret.WriteString(generateGoFunctions(schema))
  ret.WriteString("\n")

  return ret.String()
}

func generateGoImports(schema *ObjectSchema) string {

    var ret bytes.Buffer
    return ret.String()
}

func generateGoTypeDeclaration(schema *ObjectSchema) string {

    var ret bytes.Buffer

    ret.WriteString("type ")
    ret.WriteString(schema.GetTitle())
    ret.WriteString(" struct {\n")

    for propertyName, subschema := range schema.Properties {

      ret.WriteString("\tvar " + ToCamelCase(propertyName) + " ")
      ret.WriteString(generateGoTypeForSchema(subschema))
      ret.WriteString("\n")
    }

    ret.WriteString("}\n")
    return ret.String()
}

func generateGoFunctions(schema *ObjectSchema) string {

    var ret bytes.Buffer
    return ret.String()
}

func generateGoConstructor(schema *ObjectSchema) string {

  var subschema TypeSchema
  var ret bytes.Buffer
  var parameters, parameterNames []string
  var title, signature, parameterDefinition string

  for _, propertyName := range schema.RequiredProperties {

    subschema = schema.Properties[propertyName]
    propertyName = ToCamelCase(propertyName)

    ret.WriteString(propertyName)
    ret.WriteString(" ")
    ret.WriteString(generateGoTypeForSchema(subschema))

    parameterNames = append(parameterNames, propertyName)
    parameters = append(parameters, ret.String())
    ret.Reset()

  }

  // signature
  title = ToCamelCase(schema.Title)
  signature = fmt.Sprintf("func New%s(%s)(*%s) {\n", title, strings.Join(parameters, ","), title)
  ret.WriteString(signature)

  // body
  parameterDefinition = fmt.Sprintf("\tret := new(%s)\n", title)
  ret.WriteString(parameterDefinition)

  for _, propertyName := range parameterNames {

    parameterDefinition = fmt.Sprintf("\tret.%s = %s\n", propertyName, propertyName)
    ret.WriteString(parameterDefinition)
  }

  ret.WriteString("\treturn ret\n}\n\n")
  return ret.String()
}

func generateGoIntegerFunctions(schema *IntegerSchema) string {

  var ret bytes.Buffer

  if(!schema.HasConstraints()) {
    return ""
  }

  if(schema.Minimum != nil) {

  }

  return ret.String()
}

func generateGoStringFunctions(schema *StringSchema) string {

  var ret bytes.Buffer

  return ret.String()
}

func generateGoTypeForSchema(schema TypeSchema) string {

  switch schema.GetSchemaType() {
  case SCHEMATYPE_STRING:
    return "string"
  case SCHEMATYPE_INTEGER:
    return "int"
  case SCHEMATYPE_OBJECT:
    return "*" + ToCamelCase(schema.GetTitle())
  }

  return "interface{}"
}
<|endoftext|>


<commit_before>package notifier

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/emailer"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/stopwatch"
	"github.com/oinume/lekcije/server/util"
	"go.uber.org/zap"
)

type Notifier struct {
	db              *gorm.DB
	fetcher         *fetcher.TeacherLessonFetcher
	dryRun          bool
	lessonService   *model.LessonService
	teachers        map[uint32]*model.Teacher
	fetchedLessons  map[uint32][]*model.Lesson
	sender          emailer.Sender
	senderWaitGroup *sync.WaitGroup
	stopwatch       stopwatch.Stopwatch
	sync.Mutex
}

func NewNotifier(db *gorm.DB, fetcher *fetcher.TeacherLessonFetcher, dryRun bool, sender emailer.Sender) *Notifier {
	return &Notifier{
		db:              db,
		fetcher:         fetcher,
		dryRun:          dryRun,
		teachers:        make(map[uint32]*model.Teacher, 1000),
		fetchedLessons:  make(map[uint32][]*model.Lesson, 1000),
		sender:          sender,
		senderWaitGroup: &sync.WaitGroup{},
		stopwatch:       stopwatch.NewSync().Start(),
	}
}

func (n *Notifier) SendNotification(user *model.User) error {
	followingTeacherService := model.NewFollowingTeacherService(n.db)
	n.lessonService = model.NewLessonService(n.db)
	const maxFetchErrorCount = 5
	teacherIDs, err := followingTeacherService.FindTeacherIDsByUserID(user.ID, maxFetchErrorCount)
	if err != nil {
		return errors.Wrapperf(err, "Failed to FindTeacherIDsByUserID(): userID=%v", user.ID)
	}
	n.stopwatch.Mark(fmt.Sprintf("FindTeacherIDsByUserID:%d", user.ID))

	if len(teacherIDs) == 0 {
		return nil
	}

	logger.App.Info(
		"Target teachers",
		zap.Uint("userID", uint(user.ID)),
		zap.Int("teachers", len(teacherIDs)),
	)

	availableLessonsPerTeacher := make(map[uint32][]*model.Lesson, 1000)
	wg := &sync.WaitGroup{}
	for _, teacherID := range teacherIDs {
		wg.Add(1)
		go func(teacherID uint32) {
			defer n.stopwatch.Mark(fmt.Sprintf("fetchAndExtractNewAvailableLessons:%d", teacherID))
			defer wg.Done()
			teacher, fetchedLessons, newAvailableLessons, err := n.fetchAndExtractNewAvailableLessons(teacherID)
			if err != nil {
				switch err.(type) {
				case *errors.NotFound:
					if err := model.NewTeacherService(n.db).IncrementFetchErrorCount(teacherID, 1); err != nil {
						logger.App.Error(
							"IncrementFetchErrorCount failed",
							zap.Uint("teacherID", uint(teacherID)), zap.Error(err),
						)
					}
					logger.App.Warn("Cannot find teacher", zap.Uint("teacherID", uint(teacherID)))
				// TODO: Handle a case eikaiwa.dmm.com is down
				default:
					logger.App.Error("Cannot fetch teacher", zap.Uint("teacherID", uint(teacherID)), zap.Error(err))
				}
				return
			}

			n.Lock()
			defer n.Unlock()
			n.teachers[teacherID] = teacher
			if _, ok := n.fetchedLessons[teacherID]; !ok {
				n.fetchedLessons[teacherID] = make([]*model.Lesson, 0, 5000)
			}
			n.fetchedLessons[teacherID] = append(n.fetchedLessons[teacherID], fetchedLessons...)
			if len(newAvailableLessons) > 0 {
				availableLessonsPerTeacher[teacherID] = newAvailableLessons
			}
		}(teacherID)

		if err != nil {
			return err
		}
	}
	wg.Wait()

	if err := n.sendNotificationToUser(user, availableLessonsPerTeacher); err != nil {
		return err
	}

	time.Sleep(150 * time.Millisecond)
	n.stopwatch.Mark("sleep")

	return nil
}

// Returns teacher, fetchedLessons, newAvailableLessons, error
func (n *Notifier) fetchAndExtractNewAvailableLessons(teacherID uint32) (
	*model.Teacher, []*model.Lesson, []*model.Lesson, error,
) {
	teacher, fetchedLessons, err := n.fetcher.Fetch(teacherID)
	if err != nil {
		return nil, nil, nil, err
	}
	logger.App.Debug(
		"fetcher.Fetch",
		zap.Uint("teacherID", uint(teacher.ID)),
		zap.Int("lessons", len(fetchedLessons)),
	)

	//fmt.Printf("fetchedLessons ---\n")
	//for _, l := range fetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	now := time.Now().In(config.LocalTimezone())
	fromDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, config.LocalTimezone())
	toDate := fromDate.Add(24 * 6 * time.Hour)
	lastFetchedLessons, err := n.lessonService.FindLessons(teacher.ID, fromDate, toDate)
	if err != nil {
		return nil, nil, nil, err
	}
	//fmt.Printf("lastFetchedLessons ---\n")
	//for _, l := range lastFetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	newAvailableLessons := n.lessonService.GetNewAvailableLessons(lastFetchedLessons, fetchedLessons)
	//fmt.Printf("newAvailableLessons ---\n")
	//for _, l := range newAvailableLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}
	return teacher, fetchedLessons, newAvailableLessons, nil
}

func (n *Notifier) sendNotificationToUser(
	user *model.User,
	lessonsPerTeacher map[uint32][]*model.Lesson,
) error {
	lessonsCount := 0
	var teacherIDs []int
	for teacherID, lessons := range lessonsPerTeacher {
		teacherIDs = append(teacherIDs, int(teacherID))
		lessonsCount += len(lessons)
	}
	if lessonsCount == 0 {
		// Don't send notification
		return nil
	}

	sort.Ints(teacherIDs)
	var teacherIDs2 []uint32
	var teacherNames []string
	for _, id := range teacherIDs {
		teacherIDs2 = append(teacherIDs2, uint32(id))
		teacherNames = append(teacherNames, n.teachers[uint32(id)].Name)
	}

	// TODO: getEmailTemplate as a static file
	t := emailer.NewTemplate("notifier", getEmailTemplateJP())
	data := struct {
		To                string
		TeacherNames      string
		TeacherIDs        []uint32
		Teachers          map[uint32]*model.Teacher
		LessonsPerTeacher map[uint32][]*model.Lesson
		WebURL            string
	}{
		To:                user.Email,
		TeacherNames:      strings.Join(teacherNames, ", "),
		TeacherIDs:        teacherIDs2,
		Teachers:          n.teachers,
		LessonsPerTeacher: lessonsPerTeacher,
		WebURL:            config.WebURL(),
	}
	email, err := emailer.NewEmailFromTemplate(t, data)
	if err != nil {
		return errors.InternalWrapf(err, "Failed to create emailer.Email from template: to=%v", user.Email)
	}
	email.SetCustomArg("email_type", model.EmailTypeNewLessonNotifier)
	email.SetCustomArg("user_id", fmt.Sprint(user.ID))
	email.SetCustomArg("teacher_ids", strings.Join(util.Uint32ToStringSlice(teacherIDs2...), ","))
	//fmt.Printf("--- mail ---\n%s", email.BodyString())
	n.stopwatch.Mark("emailer.NewEmailFromTemplate")

	logger.App.Info("sendNotificationToUser", zap.String("email", user.Email))

	n.senderWaitGroup.Add(1)
	go func(email *emailer.Email) {
		defer n.stopwatch.Mark(fmt.Sprintf("sender.Send:%d", user.ID))
		defer n.senderWaitGroup.Done()
		if err := n.sender.Send(email); err != nil {
			logger.App.Error(
				"Failed to sendNotificationToUser",
				zap.String("email", user.Email), zap.Error(err),
			)
		}
	}(email)

	return nil
	//	return n.sender.Send(email)
}

func getEmailTemplateJP() string {
	return strings.TrimSpace(`
From: lekcije <lekcije@lekcije.com>
To: {{ .To }}
Subject: {{ .TeacherNames }}の空きレッスンがあります
Body: text/html
{{ range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

レッスンの予約はこちらから:
PC
Mobile

{{ end }}
空きレッスンの通知の解除はこちら

お問い合わせ
	`)
}

//func getEmailTemplateEN() string {
//	return strings.TrimSpace(`
//{{- range $teacherID := .TeacherIDs }}
//{{- $teacher := index $.Teachers $teacherID -}}
//--- {{ $teacher.Name }} ---
//  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
//  {{- range $lesson := $lessons }}
//{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
//  {{- end }}
//
//Reserve here:
//PC
//Mobile
//{{ end }}
//Click here if you want to stop notification of the teacher.
//	`)
//}

func (n *Notifier) Close() {
	n.senderWaitGroup.Wait()
	defer n.fetcher.Close()
	defer func() {
		if n.dryRun {
			return
		}
		for teacherID, lessons := range n.fetchedLessons {
			if _, err := n.lessonService.UpdateLessons(lessons); err != nil {
				logger.App.Error(
					"An error ocurred in Notifier.Close",
					zap.Error(err), zap.Uint("teacherID", uint(teacherID)),
				)
			}
		}
	}()
	defer func() {
		n.stopwatch.Stop()
		//logger.App.Info("Stopwatch report", zap.String("report", watch.Report()))
		//fmt.Println("--- stopwatch ---")
		//fmt.Println(n.stopwatch.Report())
	}()
}
<commit_msg>Omit log message for papertrail<commit_after>package notifier

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/oinume/lekcije/server/config"
	"github.com/oinume/lekcije/server/emailer"
	"github.com/oinume/lekcije/server/errors"
	"github.com/oinume/lekcije/server/fetcher"
	"github.com/oinume/lekcije/server/logger"
	"github.com/oinume/lekcije/server/model"
	"github.com/oinume/lekcije/server/stopwatch"
	"github.com/oinume/lekcije/server/util"
	"go.uber.org/zap"
)

type Notifier struct {
	db              *gorm.DB
	fetcher         *fetcher.TeacherLessonFetcher
	dryRun          bool
	lessonService   *model.LessonService
	teachers        map[uint32]*model.Teacher
	fetchedLessons  map[uint32][]*model.Lesson
	sender          emailer.Sender
	senderWaitGroup *sync.WaitGroup
	stopwatch       stopwatch.Stopwatch
	sync.Mutex
}

func NewNotifier(db *gorm.DB, fetcher *fetcher.TeacherLessonFetcher, dryRun bool, sender emailer.Sender) *Notifier {
	return &Notifier{
		db:              db,
		fetcher:         fetcher,
		dryRun:          dryRun,
		teachers:        make(map[uint32]*model.Teacher, 1000),
		fetchedLessons:  make(map[uint32][]*model.Lesson, 1000),
		sender:          sender,
		senderWaitGroup: &sync.WaitGroup{},
		stopwatch:       stopwatch.NewSync().Start(),
	}
}

func (n *Notifier) SendNotification(user *model.User) error {
	followingTeacherService := model.NewFollowingTeacherService(n.db)
	n.lessonService = model.NewLessonService(n.db)
	const maxFetchErrorCount = 5
	teacherIDs, err := followingTeacherService.FindTeacherIDsByUserID(user.ID, maxFetchErrorCount)
	if err != nil {
		return errors.Wrapperf(err, "Failed to FindTeacherIDsByUserID(): userID=%v", user.ID)
	}
	n.stopwatch.Mark(fmt.Sprintf("FindTeacherIDsByUserID:%d", user.ID))

	if len(teacherIDs) == 0 {
		return nil
	}

	logger.App.Info("", zap.Uint("userID", uint(user.ID)), zap.Int("teachers", len(teacherIDs)))

	availableLessonsPerTeacher := make(map[uint32][]*model.Lesson, 1000)
	wg := &sync.WaitGroup{}
	for _, teacherID := range teacherIDs {
		wg.Add(1)
		go func(teacherID uint32) {
			defer n.stopwatch.Mark(fmt.Sprintf("fetchAndExtractNewAvailableLessons:%d", teacherID))
			defer wg.Done()
			teacher, fetchedLessons, newAvailableLessons, err := n.fetchAndExtractNewAvailableLessons(teacherID)
			if err != nil {
				switch err.(type) {
				case *errors.NotFound:
					if err := model.NewTeacherService(n.db).IncrementFetchErrorCount(teacherID, 1); err != nil {
						logger.App.Error(
							"IncrementFetchErrorCount failed",
							zap.Uint("teacherID", uint(teacherID)), zap.Error(err),
						)
					}
					logger.App.Warn("Cannot find teacher", zap.Uint("teacherID", uint(teacherID)))
				// TODO: Handle a case eikaiwa.dmm.com is down
				default:
					logger.App.Error("Cannot fetch teacher", zap.Uint("teacherID", uint(teacherID)), zap.Error(err))
				}
				return
			}

			n.Lock()
			defer n.Unlock()
			n.teachers[teacherID] = teacher
			if _, ok := n.fetchedLessons[teacherID]; !ok {
				n.fetchedLessons[teacherID] = make([]*model.Lesson, 0, 5000)
			}
			n.fetchedLessons[teacherID] = append(n.fetchedLessons[teacherID], fetchedLessons...)
			if len(newAvailableLessons) > 0 {
				availableLessonsPerTeacher[teacherID] = newAvailableLessons
			}
		}(teacherID)

		if err != nil {
			return err
		}
	}
	wg.Wait()

	if err := n.sendNotificationToUser(user, availableLessonsPerTeacher); err != nil {
		return err
	}

	time.Sleep(150 * time.Millisecond)
	n.stopwatch.Mark("sleep")

	return nil
}

// Returns teacher, fetchedLessons, newAvailableLessons, error
func (n *Notifier) fetchAndExtractNewAvailableLessons(teacherID uint32) (
	*model.Teacher, []*model.Lesson, []*model.Lesson, error,
) {
	teacher, fetchedLessons, err := n.fetcher.Fetch(teacherID)
	if err != nil {
		return nil, nil, nil, err
	}
	logger.App.Debug(
		"fetcher.Fetch",
		zap.Uint("teacherID", uint(teacher.ID)),
		zap.Int("lessons", len(fetchedLessons)),
	)

	//fmt.Printf("fetchedLessons ---\n")
	//for _, l := range fetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	now := time.Now().In(config.LocalTimezone())
	fromDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, config.LocalTimezone())
	toDate := fromDate.Add(24 * 6 * time.Hour)
	lastFetchedLessons, err := n.lessonService.FindLessons(teacher.ID, fromDate, toDate)
	if err != nil {
		return nil, nil, nil, err
	}
	//fmt.Printf("lastFetchedLessons ---\n")
	//for _, l := range lastFetchedLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}

	newAvailableLessons := n.lessonService.GetNewAvailableLessons(lastFetchedLessons, fetchedLessons)
	//fmt.Printf("newAvailableLessons ---\n")
	//for _, l := range newAvailableLessons {
	//	fmt.Printf("teacherID=%v, datetime=%v, status=%v\n", l.TeacherId, l.Datetime, l.Status)
	//}
	return teacher, fetchedLessons, newAvailableLessons, nil
}

func (n *Notifier) sendNotificationToUser(
	user *model.User,
	lessonsPerTeacher map[uint32][]*model.Lesson,
) error {
	lessonsCount := 0
	var teacherIDs []int
	for teacherID, lessons := range lessonsPerTeacher {
		teacherIDs = append(teacherIDs, int(teacherID))
		lessonsCount += len(lessons)
	}
	if lessonsCount == 0 {
		// Don't send notification
		return nil
	}

	sort.Ints(teacherIDs)
	var teacherIDs2 []uint32
	var teacherNames []string
	for _, id := range teacherIDs {
		teacherIDs2 = append(teacherIDs2, uint32(id))
		teacherNames = append(teacherNames, n.teachers[uint32(id)].Name)
	}

	// TODO: getEmailTemplate as a static file
	t := emailer.NewTemplate("notifier", getEmailTemplateJP())
	data := struct {
		To                string
		TeacherNames      string
		TeacherIDs        []uint32
		Teachers          map[uint32]*model.Teacher
		LessonsPerTeacher map[uint32][]*model.Lesson
		WebURL            string
	}{
		To:                user.Email,
		TeacherNames:      strings.Join(teacherNames, ", "),
		TeacherIDs:        teacherIDs2,
		Teachers:          n.teachers,
		LessonsPerTeacher: lessonsPerTeacher,
		WebURL:            config.WebURL(),
	}
	email, err := emailer.NewEmailFromTemplate(t, data)
	if err != nil {
		return errors.InternalWrapf(err, "Failed to create emailer.Email from template: to=%v", user.Email)
	}
	email.SetCustomArg("email_type", model.EmailTypeNewLessonNotifier)
	email.SetCustomArg("user_id", fmt.Sprint(user.ID))
	email.SetCustomArg("teacher_ids", strings.Join(util.Uint32ToStringSlice(teacherIDs2...), ","))
	//fmt.Printf("--- mail ---\n%s", email.BodyString())
	n.stopwatch.Mark("emailer.NewEmailFromTemplate")

	logger.App.Info("sendNotificationToUser", zap.String("email", user.Email))

	n.senderWaitGroup.Add(1)
	go func(email *emailer.Email) {
		defer n.stopwatch.Mark(fmt.Sprintf("sender.Send:%d", user.ID))
		defer n.senderWaitGroup.Done()
		if err := n.sender.Send(email); err != nil {
			logger.App.Error(
				"Failed to sendNotificationToUser",
				zap.String("email", user.Email), zap.Error(err),
			)
		}
	}(email)

	return nil
	//	return n.sender.Send(email)
}

func getEmailTemplateJP() string {
	return strings.TrimSpace(`
From: lekcije <lekcije@lekcije.com>
To: {{ .To }}
Subject: {{ .TeacherNames }}の空きレッスンがあります
Body: text/html
{{ range $teacherID := .TeacherIDs }}
{{- $teacher := index $.Teachers $teacherID -}}
--- {{ $teacher.Name }} ---
  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
  {{- range $lesson := $lessons }}
{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
  {{- end }}

レッスンの予約はこちらから:
PC
Mobile

{{ end }}
空きレッスンの通知の解除はこちら

お問い合わせ
	`)
}

//func getEmailTemplateEN() string {
//	return strings.TrimSpace(`
//{{- range $teacherID := .TeacherIDs }}
//{{- $teacher := index $.Teachers $teacherID -}}
//--- {{ $teacher.Name }} ---
//  {{- $lessons := index $.LessonsPerTeacher $teacherID }}
//  {{- range $lesson := $lessons }}
//{{ $lesson.Datetime.Format "2006-01-02 15:04" }}
//  {{- end }}
//
//Reserve here:
//PC
//Mobile
//{{ end }}
//Click here if you want to stop notification of the teacher.
//	`)
//}

func (n *Notifier) Close() {
	n.senderWaitGroup.Wait()
	defer n.fetcher.Close()
	defer func() {
		if n.dryRun {
			return
		}
		for teacherID, lessons := range n.fetchedLessons {
			if _, err := n.lessonService.UpdateLessons(lessons); err != nil {
				logger.App.Error(
					"An error ocurred in Notifier.Close",
					zap.Error(err), zap.Uint("teacherID", uint(teacherID)),
				)
			}
		}
	}()
	defer func() {
		n.stopwatch.Stop()
		//logger.App.Info("Stopwatch report", zap.String("report", watch.Report()))
		//fmt.Println("--- stopwatch ---")
		//fmt.Println(n.stopwatch.Report())
	}()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image"
	"io/ioutil"
	"os"
	"runtime"

	"code.google.com/p/jnj.plan9/draw"
	"code.google.com/p/jnj/die"
	"github.com/jnjackins/graphics/text"
)

var (
	disp   *draw.Display
	screen *draw.Image
	buf    *text.Buffer
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	inputfile := false
	if len(flag.Args()) == 1 {
		inputfile = true
	} else if len(flag.Args()) > 1 {
		fmt.Fprintln(os.Stderr, "Usage: buf [file]")
		os.Exit(1)
	}

	gopath := os.Getenv("GOPATH")
	fontpath := gopath + "/src/github.com/jnjackins/graphics/cmd/buf/proggyfont.ttf"
	var err error
	buf, err = text.NewBuffer(image.Rect(0, 0, 800, 600), fontpath)
	die.On(err)

	var path string
	if inputfile {
		path = flag.Arg(0)
		_, err := os.Stat(path)
		if err != nil {
			// if there's no file, no worries. otherwise, bail.
			if !os.IsNotExist(err) {
				die.On(err)
			}
		} else {
			// no issues, open file for reading
			f, err := os.Open(path)
			die.On(err)
			s, err := ioutil.ReadAll(f)
			buf.LoadString(string(s))
			buf.Select(text.Selection{})
			f.Close()
		}
	}

	disp, err = draw.Init(nil, "buf", "800x622")
	die.On(err)
	defer disp.Close()
	screen = disp.ScreenImage
	mouse := disp.InitMouse()
	kbd := disp.InitKeyboard()
	redraw()

loop:
	for {
		select {
		case <-mouse.Resize:
			resize()
		case me := <-mouse.C:
			buf.SendMouseEvent(me.Point, me.Buttons)
		case ke := <-kbd.C:
			// esc
			if ke == 27 {
				if path != "" {
					ioutil.WriteFile(path, []byte(buf.Contents()), 0666)
				}
				break loop
			}
			buf.SendKey(ke)
		}
		if buf.Dirty() {
			redraw()
		}
	}
}

func redraw() {
	img, clipr := buf.Img()
	_, err := screen.Load(screen.Bounds(), img.SubImage(clipr).(*image.RGBA).Pix)
	die.On(err)
	disp.Flush()
}

func resize() {
	err := disp.Attach(draw.Refmesg)
	die.On(err, "error reattaching display after resize")
	buf.Resize(screen.Bounds())
}
<commit_msg>cmd/buf: add cpu profiling support<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"io/ioutil"
	"os"
	"runtime"
	"runtime/pprof"

	"code.google.com/p/jnj.plan9/draw"
	"code.google.com/p/jnj/die"
	"github.com/jnjackins/graphics/text"
)

var (
	disp   *draw.Display
	screen *draw.Image
	buf    *text.Buffer
)

var cpuprofile = flag.String("cpuprofile", "", "provide a path for cpu profile")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()
	inputfile := false
	if len(flag.Args()) == 1 {
		inputfile = true
	} else if len(flag.Args()) > 1 {
		fmt.Fprintln(os.Stderr, "Usage: buf [file]")
		os.Exit(1)
	}

	if *cpuprofile != "" {
		profileWriter, err := os.Create(*cpuprofile)
		die.On(err, "error creating file for cpu profile")
		defer profileWriter.Close()
		pprof.StartCPUProfile(profileWriter)
		defer pprof.StopCPUProfile()
	}

	gopath := os.Getenv("GOPATH")
	fontpath := gopath + "/src/github.com/jnjackins/graphics/cmd/buf/proggyfont.ttf"
	var err error
	buf, err = text.NewBuffer(image.Rect(0, 0, 800, 600), fontpath)
	die.On(err)

	var path string
	if inputfile {
		path = flag.Arg(0)
		_, err := os.Stat(path)
		if err != nil {
			// if there's no file, no worries. otherwise, bail.
			if !os.IsNotExist(err) {
				die.On(err)
			}
		} else {
			// no issues, open file for reading
			f, err := os.Open(path)
			die.On(err)
			s, err := ioutil.ReadAll(f)
			buf.LoadString(string(s))
			buf.Select(text.Selection{})
			f.Close()
		}
	}

	disp, err = draw.Init(nil, "buf", "800x622")
	die.On(err)
	defer disp.Close()
	screen = disp.ScreenImage
	mouse := disp.InitMouse()
	kbd := disp.InitKeyboard()
	redraw()

loop:
	for {
		select {
		case <-mouse.Resize:
			resize()
		case me := <-mouse.C:
			buf.SendMouseEvent(me.Point, me.Buttons)
		case ke := <-kbd.C:
			// esc
			if ke == 27 {
				if path != "" {
					ioutil.WriteFile(path, []byte(buf.Contents()), 0666)
				}
				break loop
			}
			buf.SendKey(ke)
		}
		if buf.Dirty() {
			redraw()
		}
	}
}

func redraw() {
	img, clipr := buf.Img()
	_, err := screen.Load(screen.Bounds(), img.SubImage(clipr).(*image.RGBA).Pix)
	die.On(err)
	disp.Flush()
}

func resize() {
	err := disp.Attach(draw.Refmesg)
	die.On(err, "error reattaching display after resize")
	buf.Resize(screen.Bounds())
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/nilslice/cms/system/admin"
	"github.com/nilslice/cms/system/api"
	"github.com/nilslice/cms/system/db"
)

var usage = `
$ cms <option> <params>

Options

new <directory>:

	Creates a new 'cms' in the current directory, or one supplied
	as a parameter immediately following the 'new' option. Note: 'new'
	depends on the program 'git' and possibly a network connection. If there is
	no local repository to clone from at the local machine's $GOPATH, 'new' will
	attempt to clone the 'cms' package from over the network.

	Example:
	$ cms new ~/Projects/my-project.dev

generate, gen, g <type>:

    Generate a content type file with boilerplate code to implement
    the editor.Editable interface. Must be given one (1) parameter of
    the name of the type for the new content.

    Example:
	$ cms gen review

serve, s <service:port:tls>:

	Starts the 'cms' HTTP server for the JSON API, Admin System, or both.
	Must be given at least one (1) parameter. The segments describe
	which services to start, in order, either 'admin'
	(Admin System / CMS backend) or 'api' (JSON API), the port to which the
	service is bound, and, optionally, if the server(s) should utilize TLS
	encryption (served over HTTPS), which is automatically managed using
	Let's Encrypt (https://letsencrypt.org)

	Example:
	$ cms serve admin:8080:tls api:8000
	(or)
	$ cms serve admin:8080
	(or)
	$ cms serve api:8000:tls
`

func init() {
	flag.Usage = func() {
		fmt.Println(usage)
	}
}

func main() {
	flag.Parse()

	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(0)
	}

	switch args[0] {
	case "new":
		if len(args) < 2 {
			flag.PrintDefaults()
			os.Exit(0)
		}

		err := newProjectInDir(args[1])
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "generate", "gen", "g":
		if len(args) < 2 {
			flag.PrintDefaults()
			os.Exit(0)
		}

		err := generateContentType(args[1])
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "serve", "s":
		db.Init()
		go admin.Run("8080")
		api.Run("8000")

	case "":
		flag.PrintDefaults()
	default:
		flag.PrintDefaults()
	}
}
<commit_msg>changing api for running admin + api server<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/nilslice/cms/system/admin"
	"github.com/nilslice/cms/system/api"
	"github.com/nilslice/cms/system/db"
)

var usage = `
$ cms <option> <params>

Options

new <directory>:

	Creates a new 'cms' in the current directory, or one supplied
	as a parameter immediately following the 'new' option. Note: 'new'
	depends on the program 'git' and possibly a network connection. If there is
	no local repository to clone from at the local machine's $GOPATH, 'new' will
	attempt to clone the 'cms' package from over the network.

	Example:
	$ cms new ~/Projects/my-project.dev

generate, gen, g <type>:

    Generate a content type file with boilerplate code to implement
    the editor.Editable interface. Must be given one (1) parameter of
    the name of the type for the new content.

    Example:
	$ cms gen review

serve, s <service:port:tls>:

	Starts the 'cms' HTTP server for the JSON API, Admin System, or both.
	Must be given at least one (1) parameter. The segments describe
	which services to start, in order, either 'admin'
	(Admin System / CMS backend) or 'api' (JSON API), the port to which the
	service is bound, and, optionally, if the server(s) should utilize TLS
	encryption (served over HTTPS), which is automatically managed using
	Let's Encrypt (https://letsencrypt.org)

	Example:
	$ cms serve admin:8080:tls api:8000
	(or)
	$ cms serve admin:8080
	(or)
	$ cms serve api:8000:tls
`

func init() {
	flag.Usage = func() {
		fmt.Println(usage)
	}
}

func main() {
	flag.Parse()

	args := flag.Args()

	if len(args) < 1 {
		flag.Usage()
		os.Exit(0)
	}

	switch args[0] {
	case "new":
		if len(args) < 2 {
			flag.PrintDefaults()
			os.Exit(0)
		}

		err := newProjectInDir(args[1])
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

	case "generate", "gen", "g":
		if len(args) < 2 {
			flag.PrintDefaults()
			os.Exit(0)
		}

		err := generateContentType(args[1])
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "serve", "s":
		db.Init()
		admin.Run()
		api.Run()
		log.Fatal(http.ListenAndServe(":8080", nil))
	case "":
		flag.PrintDefaults()
	default:
		flag.PrintDefaults()
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if usrCfg.GetString("token") == "" {
		return fmt.Errorf(msgWelcomePleaseConfigure, config.SettingsURL(usrCfg.GetString("apibaseurl")), BinaryName)
	}
	if usrCfg.GetString("workspace") == "" || usrCfg.GetString("apibaseurl") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid == "" && slug == "" {
		return errors.New("need an --exercise name or a solution --uuid")
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	solution := workspace.Solution{
		AutoApprove: payload.Solution.Exercise.AutoApprove,
		Track:       payload.Solution.Exercise.Track.ID,
		Team:        payload.Solution.Team.Slug,
		Exercise:    payload.Solution.Exercise.ID,
		ID:          payload.Solution.ID,
		URL:         payload.Solution.URL,
		Handle:      payload.Solution.User.Handle,
		IsRequester: payload.Solution.User.IsRequester,
	}

	root := usrCfg.GetString("workspace")
	if solution.Team != "" {
		root = filepath.Join(root, "teams", solution.Team)
	}
	if !solution.IsRequester {
		root = filepath.Join(root, "users", solution.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: solution.Track,
		Slug:  solution.Exercise,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = solution.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.
		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(solution.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(solution.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", solution.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<commit_msg>download: add mutual exclusive flag check for exercise and uuid<commit_after>package cmd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	netURL "net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/exercism/cli/api"
	"github.com/exercism/cli/config"
	"github.com/exercism/cli/workspace"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

// downloadCmd represents the download command
var downloadCmd = &cobra.Command{
	Use:     "download",
	Aliases: []string{"d"},
	Short:   "Download an exercise.",
	Long: `Download an exercise.

You may download an exercise to work on. If you've already
started working on it, the command will also download your
latest solution.

Download other people's solutions by providing the UUID.
`,
	RunE: func(cmd *cobra.Command, args []string) error {
		cfg := config.NewConfig()

		v := viper.New()
		v.AddConfigPath(cfg.Dir)
		v.SetConfigName("user")
		v.SetConfigType("json")
		// Ignore error. If the file doesn't exist, that is fine.
		_ = v.ReadInConfig()
		cfg.UserViperConfig = v

		return runDownload(cfg, cmd.Flags(), args)
	},
}

func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
	usrCfg := cfg.UserViperConfig
	if usrCfg.GetString("token") == "" {
		return fmt.Errorf(msgWelcomePleaseConfigure, config.SettingsURL(usrCfg.GetString("apibaseurl")), BinaryName)
	}
	if usrCfg.GetString("workspace") == "" || usrCfg.GetString("apibaseurl") == "" {
		return fmt.Errorf(msgRerunConfigure, BinaryName)
	}

	uuid, err := flags.GetString("uuid")
	if err != nil {
		return err
	}
	slug, err := flags.GetString("exercise")
	if err != nil {
		return err
	}
	if uuid != "" && slug != "" || uuid == slug {
		return errors.New("need an --exercise name or a solution --uuid")
	}

	track, err := flags.GetString("track")
	if err != nil {
		return err
	}

	team, err := flags.GetString("team")
	if err != nil {
		return err
	}

	param := "latest"
	if uuid != "" {
		param = uuid
	}
	url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), param)

	client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
	if err != nil {
		return err
	}

	req, err := client.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	if uuid == "" {
		q := req.URL.Query()
		q.Add("exercise_id", slug)
		if track != "" {
			q.Add("track_id", track)
		}
		if team != "" {
			q.Add("team_id", team)
		}
		req.URL.RawQuery = q.Encode()
	}

	res, err := client.Do(req)
	if err != nil {
		return err
	}

	var payload downloadPayload
	defer res.Body.Close()
	if err := json.NewDecoder(res.Body).Decode(&payload); err != nil {
		return fmt.Errorf("unable to parse API response - %s", err)
	}

	if res.StatusCode == http.StatusUnauthorized {
		siteURL := config.InferSiteURL(usrCfg.GetString("apibaseurl"))
		return fmt.Errorf("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings", siteURL)
	}

	if res.StatusCode != http.StatusOK {
		switch payload.Error.Type {
		case "track_ambiguous":
			return fmt.Errorf("%s: %s", payload.Error.Message, strings.Join(payload.Error.PossibleTrackIDs, ", "))
		default:
			return errors.New(payload.Error.Message)
		}
	}

	solution := workspace.Solution{
		AutoApprove: payload.Solution.Exercise.AutoApprove,
		Track:       payload.Solution.Exercise.Track.ID,
		Team:        payload.Solution.Team.Slug,
		Exercise:    payload.Solution.Exercise.ID,
		ID:          payload.Solution.ID,
		URL:         payload.Solution.URL,
		Handle:      payload.Solution.User.Handle,
		IsRequester: payload.Solution.User.IsRequester,
	}

	root := usrCfg.GetString("workspace")
	if solution.Team != "" {
		root = filepath.Join(root, "teams", solution.Team)
	}
	if !solution.IsRequester {
		root = filepath.Join(root, "users", solution.Handle)
	}

	exercise := workspace.Exercise{
		Root:  root,
		Track: solution.Track,
		Slug:  solution.Exercise,
	}

	dir := exercise.MetadataDir()

	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
		return err
	}

	err = solution.Write(dir)
	if err != nil {
		return err
	}

	for _, file := range payload.Solution.Files {
		unparsedURL := fmt.Sprintf("%s%s", payload.Solution.FileDownloadBaseURL, file)
		parsedURL, err := netURL.ParseRequestURI(unparsedURL)

		if err != nil {
			return err
		}

		url := parsedURL.String()

		req, err := client.NewRequest("GET", url, nil)
		if err != nil {
			return err
		}

		res, err := client.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()

		if res.StatusCode != http.StatusOK {
			// TODO: deal with it
			continue
		}
		// Don't bother with empty files.
		if res.Header.Get("Content-Length") == "0" {
			continue
		}

		// TODO: if there's a collision, interactively resolve (show diff, ask if overwrite).
		// TODO: handle --force flag to overwrite without asking.
		relativePath := filepath.FromSlash(file)
		dir := filepath.Join(solution.Dir, filepath.Dir(relativePath))
		os.MkdirAll(dir, os.FileMode(0755))

		f, err := os.Create(filepath.Join(solution.Dir, relativePath))
		if err != nil {
			return err
		}
		defer f.Close()
		_, err = io.Copy(f, res.Body)
		if err != nil {
			return err
		}
	}
	fmt.Fprintf(Err, "\nDownloaded to\n")
	fmt.Fprintf(Out, "%s\n", solution.Dir)
	return nil
}

type downloadPayload struct {
	Solution struct {
		ID   string `json:"id"`
		URL  string `json:"url"`
		Team struct {
			Name string `json:"name"`
			Slug string `json:"slug"`
		} `json:"team"`
		User struct {
			Handle      string `json:"handle"`
			IsRequester bool   `json:"is_requester"`
		} `json:"user"`
		Exercise struct {
			ID              string `json:"id"`
			InstructionsURL string `json:"instructions_url"`
			AutoApprove     bool   `json:"auto_approve"`
			Track           struct {
				ID       string `json:"id"`
				Language string `json:"language"`
			} `json:"track"`
		} `json:"exercise"`
		FileDownloadBaseURL string   `json:"file_download_base_url"`
		Files               []string `json:"files"`
		Iteration           struct {
			SubmittedAt *string `json:"submitted_at"`
		}
	} `json:"solution"`
	Error struct {
		Type             string   `json:"type"`
		Message          string   `json:"message"`
		PossibleTrackIDs []string `json:"possible_track_ids"`
	} `json:"error,omitempty"`
}

func setupDownloadFlags(flags *pflag.FlagSet) {
	flags.StringP("uuid", "u", "", "the solution UUID")
	flags.StringP("track", "t", "", "the track ID")
	flags.StringP("exercise", "e", "", "the exercise slug")
	flags.StringP("team", "T", "", "the team slug")
}

func init() {
	RootCmd.AddCommand(downloadCmd)
	setupDownloadFlags(downloadCmd.Flags())
}
<|endoftext|>


<commit_before>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"math/big"
	"os"
	"runtime"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/logger"
)

var (
	code     = flag.String("code", "", "evm code")
	loglevel = flag.Int("log", 4, "log level")
	gas      = flag.String("gas", "1000000000", "gas amount")
	price    = flag.String("price", "0", "gas price")
	value    = flag.String("value", "0", "tx value")
	dump     = flag.Bool("dump", false, "dump state after run")
	data     = flag.String("data", "", "data")
)

func perr(v ...interface{}) {
	fmt.Println(v...)
	//os.Exit(1)
}

func main() {
	flag.Parse()

	logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))

	db, _ := ethdb.NewMemDatabase()
	statedb := state.New(common.Hash{}, db)
	sender := statedb.CreateAccount(common.StringToAddress("sender"))
	receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
	receiver.SetCode(common.Hex2Bytes(*code))

	vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value))

	tstart := time.Now()

	ret, e := vmenv.Call(sender, receiver.Address(), common.Hex2Bytes(*data), common.Big(*gas), common.Big(*price), common.Big(*value))

	logger.Flush()
	if e != nil {
		perr(e)
	}

	if *dump {
		fmt.Println(string(statedb.Dump()))
	}

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	fmt.Printf("vm took %v\n", time.Since(tstart))
	fmt.Printf(`alloc:      %d
tot alloc:  %d
no. malloc: %d
heap alloc: %d
heap objs:  %d
num gc:     %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)

	fmt.Printf("%x\n", ret)
}

type VMEnv struct {
	state *state.StateDB
	block *types.Block

	transactor *common.Address
	value      *big.Int

	depth int
	Gas   *big.Int
	time  int64
	logs  []vm.StructLog
}

func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VMEnv {
	return &VMEnv{
		state:      state,
		transactor: &transactor,
		value:      value,
		time:       time.Now().Unix(),
	}
}

func (self *VMEnv) State() *state.StateDB    { return self.state }
func (self *VMEnv) Origin() common.Address   { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int    { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() int64              { return self.time }
func (self *VMEnv) Difficulty() *big.Int     { return common.Big1 }
func (self *VMEnv) BlockHash() []byte        { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int          { return self.value }
func (self *VMEnv) GasLimit() *big.Int       { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type          { return vm.StdVmTy }
func (self *VMEnv) Depth() int               { return 0 }
func (self *VMEnv) SetDepth(i int)           { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
	if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
		return self.block.Hash()
	}
	return common.Hash{}
}
func (self *VMEnv) AddStructLog(log vm.StructLog) {
	self.logs = append(self.logs, log)
}
func (self *VMEnv) StructLogs() []vm.StructLog {
	return self.logs
}
func (self *VMEnv) AddLog(log *state.Log) {
	self.state.AddLog(log)
}
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
	return vm.Transfer(from, to, amount)
}

func (self *VMEnv) vm(addr *common.Address, data []byte, gas, price, value *big.Int) *core.Execution {
	return core.NewExecution(self, addr, data, gas, price, value)
}

func (self *VMEnv) Call(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
	exe := self.vm(&addr, data, gas, price, value)
	ret, err := exe.Call(addr, caller)
	self.Gas = exe.Gas

	return ret, err
}
func (self *VMEnv) CallCode(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
	a := caller.Address()
	exe := self.vm(&a, data, gas, price, value)
	return exe.Call(addr, caller)
}

func (self *VMEnv) Create(caller vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
	exe := self.vm(nil, data, gas, price, value)
	return exe.Create(caller)
}
<commit_msg>cmd/evm: print trace when running programs<commit_after>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"math/big"
	"os"
	"runtime"
	"time"

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/state"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/ethdb"
	"github.com/ethereum/go-ethereum/logger"
)

var (
	code     = flag.String("code", "", "evm code")
	loglevel = flag.Int("log", 4, "log level")
	gas      = flag.String("gas", "1000000000", "gas amount")
	price    = flag.String("price", "0", "gas price")
	value    = flag.String("value", "0", "tx value")
	dump     = flag.Bool("dump", false, "dump state after run")
	data     = flag.String("data", "", "data")
)

func perr(v ...interface{}) {
	fmt.Println(v...)
	//os.Exit(1)
}

func main() {
	flag.Parse()

	logger.AddLogSystem(logger.NewStdLogSystem(os.Stdout, log.LstdFlags, logger.LogLevel(*loglevel)))

	vm.Debug = true
	db, _ := ethdb.NewMemDatabase()
	statedb := state.New(common.Hash{}, db)
	sender := statedb.CreateAccount(common.StringToAddress("sender"))
	receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
	receiver.SetCode(common.Hex2Bytes(*code))

	vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(*value))

	tstart := time.Now()

	ret, e := vmenv.Call(sender, receiver.Address(), common.Hex2Bytes(*data), common.Big(*gas), common.Big(*price), common.Big(*value))

	logger.Flush()
	if e != nil {
		perr(e)
	}

	if *dump {
		fmt.Println(string(statedb.Dump()))
	}

	vm.StdErrFormat(vmenv.StructLogs())

	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)
	fmt.Printf("vm took %v\n", time.Since(tstart))
	fmt.Printf(`alloc:      %d
tot alloc:  %d
no. malloc: %d
heap alloc: %d
heap objs:  %d
num gc:     %d
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)

	fmt.Printf("%x\n", ret)
}

type VMEnv struct {
	state *state.StateDB
	block *types.Block

	transactor *common.Address
	value      *big.Int

	depth int
	Gas   *big.Int
	time  int64
	logs  []vm.StructLog
}

func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int) *VMEnv {
	return &VMEnv{
		state:      state,
		transactor: &transactor,
		value:      value,
		time:       time.Now().Unix(),
	}
}

func (self *VMEnv) State() *state.StateDB    { return self.state }
func (self *VMEnv) Origin() common.Address   { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int    { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() int64              { return self.time }
func (self *VMEnv) Difficulty() *big.Int     { return common.Big1 }
func (self *VMEnv) BlockHash() []byte        { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int          { return self.value }
func (self *VMEnv) GasLimit() *big.Int       { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type          { return vm.StdVmTy }
func (self *VMEnv) Depth() int               { return 0 }
func (self *VMEnv) SetDepth(i int)           { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
	if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
		return self.block.Hash()
	}
	return common.Hash{}
}
func (self *VMEnv) AddStructLog(log vm.StructLog) {
	self.logs = append(self.logs, log)
}
func (self *VMEnv) StructLogs() []vm.StructLog {
	return self.logs
}
func (self *VMEnv) AddLog(log *state.Log) {
	self.state.AddLog(log)
}
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error {
	return vm.Transfer(from, to, amount)
}

func (self *VMEnv) vm(addr *common.Address, data []byte, gas, price, value *big.Int) *core.Execution {
	return core.NewExecution(self, addr, data, gas, price, value)
}

func (self *VMEnv) Call(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
	exe := self.vm(&addr, data, gas, price, value)
	ret, err := exe.Call(addr, caller)
	self.Gas = exe.Gas

	return ret, err
}
func (self *VMEnv) CallCode(caller vm.ContextRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
	a := caller.Address()
	exe := self.vm(&a, data, gas, price, value)
	return exe.Call(addr, caller)
}

func (self *VMEnv) Create(caller vm.ContextRef, data []byte, gas, price, value *big.Int) ([]byte, error, vm.ContextRef) {
	exe := self.vm(nil, data, gas, price, value)
	return exe.Create(caller)
}
<|endoftext|>