<commit_before>package handler

import (
	"OnlineJudge/base"
	"OnlineJudge/models"
	"OnlineJudge/pbgen/api"

	"github.com/jmoiron/sqlx"

	"sort"
	"strings"
	"time"
)

const (
	WATime = 20
)

func (this *AdminHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	// tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
}

func (this *BasicHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
	access, err := CheckContestAccess(
		tx, true, req.GetContestId(), this.session.GetUserId(), this.debug)
	PanicOnError(err)
	if access.If404 {
		MakeResponseError(response, this.debug, PBContestNotFound, nil)
		return
	}
	if !access.Rank {
		MakeResponseError(response, this.debug, PBUnauthorized, nil)
		return
	}
	// make ranklist
	ContestRanklist_BuildResponse(tx, response, req.GetContestId(), req.GetRankType(), this.debug)
}

func (this *UserHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
	access, err := CheckContestAccess(
		tx, false, req.GetContestId(), this.session.GetUserId(), this.debug)
	PanicOnError(err)
	if access.If404 {
		MakeResponseError(response, this.debug, PBContestNotFound, nil)
		return
	}
	if !access.Rank {
		MakeResponseError(response, this.debug, PBUnauthorized, nil)
		return
	}
	ContestRanklist_BuildResponse(tx, response, req.GetContestId(), req.GetRankType(), this.debug)
}

type ICPCColumn struct {
	Label   string
	IsAC    bool
	IsFB    bool
	Attempt int
	Minutes int64
	Seconds int64
}

type ICPCRank struct {
	Username     string
	ACNum        int32
	TotalMins    int64
	TotalSeconds int64
	Cols         []ICPCColumn
}

type ICPCRanks []*ICPCRank

func (this ICPCRanks) Len() int {
	return len(this)
}
func (this ICPCRanks) Swap(i, j int) {
	this[i], this[j] = this[j], this[i]
}

func (this ICPCRanks) Less(i, j int) bool {
	if this[i].ACNum > this[j].ACNum {
		return true
	} else if this[i].ACNum < this[j].ACNum {
		return false
	}
	if this[i].TotalSeconds < this[j].TotalSeconds {
		return true
	}
	return false
}

func ContestRanklist_BuildResponse(
	tx *sqlx.Tx,
	response *api.ContestRanklistResponse,
	contest_id int64,
	rank_type string,
	debug bool,
) {
	cst, err := models.Query_Contest_By_ContestId(tx, contest_id, nil, nil)
	PanicOnError(err)
	if cst == nil {
		MakeResponseError(response, debug, PBContestNotFound, nil)
		return
	}
	subs, err := models.XQuery_ContestRanklist_Submissions(tx, contest_id, cst.StartTime, cst.EndTime)
	PanicOnError(err)
	labels, err := models.Query_ContestProblemLabels_By_ContestId(tx, contest_id)
	PanicOnError(err)
	usernames, err := models.Query_ContestUsers_By_ContestId(tx, contest_id)
	PanicOnError(err)

	switch strings.ToLower(rank_type) {
	case "icpc":
		MakeICPCRank(response, usernames, labels, subs, cst.StartTime)
		return
	}
}

func MakeICPCRank(
	response *api.ContestRanklistResponse,
	users []string,
	labels []string,
	subs []models.ContestSubmissionExt,
	start_time time.Time,
) {
	response.RankIcpc = &api.RankICPC{}

	response.RankIcpc.Labels = labels
	// init user map
	userMap := make(map[string]*ICPCRank)
	for _, u := range users {
		userMap[u] = &ICPCRank{}
		userMap[u].Username = u
		userMap[u].Cols = make([]ICPCColumn, len(labels))
		for i, l := range labels {
			userMap[u].Cols[i].Label = l
		}
	}
	// init label map
	fbMap := make(map[string]bool)
	for _, l := range labels {
		fbMap[l] = false
	}

	for _, sub := range subs {
		u := sub.Username
		l := sub.Label
		idx := base.LabelToInt(l)
		switch sub.StatusCode {
		case "ac":
			if userMap[u].Cols[idx].IsAC {
				continue
			}
			if fbMap[l] == false {
				fbMap[l] = true
				userMap[u].Cols[idx].IsFB = true
			}
			userMap[u].Cols[idx].IsAC = true
			userMap[u].ACNum += 1
			// caculate time
			dur := sub.SubmitTime.Sub(start_time)
			mins := int64(dur.Minutes())
			secs := int64(dur.Seconds())
			userMap[u].Cols[idx].Minutes += mins
			userMap[u].Cols[idx].Seconds += secs
			userMap[u].TotalMins += userMap[u].Cols[idx].Minutes
			userMap[u].TotalSeconds += userMap[u].Cols[idx].Seconds

		case "pe", "wa", "tle", "re", "mle", "ole", "ce":
			if userMap[u].Cols[idx].IsAC {
				continue
			}
			userMap[u].Cols[idx].Minutes += WATime
			userMap[u].Cols[idx].Seconds += WATime * 20
			userMap[u].Cols[idx].Attempt += 1
		}
	}
	var ranks []*ICPCRank
	for _, v := range userMap {
		ranks = append(ranks, v)
	}
	sort.Sort(ICPCRanks(ranks))
	lines := []*api.RankICPC_PerLine{}
	for k, r := range ranks {
		line := &api.RankICPC_PerLine{
			Rank:      int32(k + 1),
			Username:  r.Username,
			AcNum:     r.ACNum,
			TotalMins: r.TotalMins,
		}
		cols := []*api.RankICPC_PerLine_Column{}
		for _, c := range r.Cols {
			col := &api.RankICPC_PerLine_Column{
				IsAc:  c.IsAC,
				Label: c.Label,
				// IsAc:    true,
				IsFb:    c.IsFB,
				Minutes: c.Minutes,
				Attempt: int32(c.Attempt),
			}
			cols = append(cols, col)
		}
		line.Cols = cols
		lines = append(lines, line)
	}
	response.RankIcpc.Lines = lines
}
<commit_msg>contest ranklist only show minutes when ac<commit_after>package handler

import (
	"OnlineJudge/base"
	"OnlineJudge/models"
	"OnlineJudge/pbgen/api"

	"github.com/jmoiron/sqlx"

	"sort"
	"strings"
	"time"
)

const (
	WATime = 20
)

func (this *AdminHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	// tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
}

func (this *BasicHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
	access, err := CheckContestAccess(
		tx, true, req.GetContestId(), this.session.GetUserId(), this.debug)
	PanicOnError(err)
	if access.If404 {
		MakeResponseError(response, this.debug, PBContestNotFound, nil)
		return
	}
	if !access.Rank {
		MakeResponseError(response, this.debug, PBUnauthorized, nil)
		return
	}
	// make ranklist
	ContestRanklist_BuildResponse(tx, response, req.GetContestId(), req.GetRankType(), this.debug)
}

func (this *UserHandler) ContestRanklist(response *api.ContestRanklistResponse, req *api.ContestRanklistRequest) {
	defer PanicHandler(response, this.debug)
	tx := this.dbu.MustBegin()
	defer this.dbu.Rollback()
	access, err := CheckContestAccess(
		tx, false, req.GetContestId(), this.session.GetUserId(), this.debug)
	PanicOnError(err)
	if access.If404 {
		MakeResponseError(response, this.debug, PBContestNotFound, nil)
		return
	}
	if !access.Rank {
		MakeResponseError(response, this.debug, PBUnauthorized, nil)
		return
	}
	ContestRanklist_BuildResponse(tx, response, req.GetContestId(), req.GetRankType(), this.debug)
}

type ICPCColumn struct {
	Label   string
	IsAC    bool
	IsFB    bool
	Attempt int
	Minutes int64
	Seconds int64
}

type ICPCRank struct {
	Username     string
	ACNum        int32
	TotalMins    int64
	TotalSeconds int64
	Cols         []ICPCColumn
}

type ICPCRanks []*ICPCRank

func (this ICPCRanks) Len() int {
	return len(this)
}
func (this ICPCRanks) Swap(i, j int) {
	this[i], this[j] = this[j], this[i]
}

func (this ICPCRanks) Less(i, j int) bool {
	if this[i].ACNum > this[j].ACNum {
		return true
	} else if this[i].ACNum < this[j].ACNum {
		return false
	}
	if this[i].TotalSeconds < this[j].TotalSeconds {
		return true
	}
	return false
}

func ContestRanklist_BuildResponse(
	tx *sqlx.Tx,
	response *api.ContestRanklistResponse,
	contest_id int64,
	rank_type string,
	debug bool,
) {
	cst, err := models.Query_Contest_By_ContestId(tx, contest_id, nil, nil)
	PanicOnError(err)
	if cst == nil {
		MakeResponseError(response, debug, PBContestNotFound, nil)
		return
	}
	subs, err := models.XQuery_ContestRanklist_Submissions(tx, contest_id, cst.StartTime, cst.EndTime)
	PanicOnError(err)
	labels, err := models.Query_ContestProblemLabels_By_ContestId(tx, contest_id)
	PanicOnError(err)
	usernames, err := models.Query_ContestUsers_By_ContestId(tx, contest_id)
	PanicOnError(err)

	switch strings.ToLower(rank_type) {
	case "icpc":
		MakeICPCRank(response, usernames, labels, subs, cst.StartTime)
		return
	}
}

func MakeICPCRank(
	response *api.ContestRanklistResponse,
	users []string,
	labels []string,
	subs []models.ContestSubmissionExt,
	start_time time.Time,
) {
	response.RankIcpc = &api.RankICPC{}

	response.RankIcpc.Labels = labels
	// init user map
	userMap := make(map[string]*ICPCRank)
	for _, u := range users {
		userMap[u] = &ICPCRank{}
		userMap[u].Username = u
		userMap[u].Cols = make([]ICPCColumn, len(labels))
		for i, l := range labels {
			userMap[u].Cols[i].Label = l
		}
	}
	// init label map
	fbMap := make(map[string]bool)
	for _, l := range labels {
		fbMap[l] = false
	}

	for _, sub := range subs {
		u := sub.Username
		l := sub.Label
		idx := base.LabelToInt(l)
		switch sub.StatusCode {
		case "ac":
			if userMap[u].Cols[idx].IsAC {
				continue
			}
			if fbMap[l] == false {
				fbMap[l] = true
				userMap[u].Cols[idx].IsFB = true
			}
			userMap[u].Cols[idx].IsAC = true
			userMap[u].ACNum += 1
			// caculate time
			dur := sub.SubmitTime.Sub(start_time)
			mins := int64(dur.Minutes())
			secs := int64(dur.Seconds())
			userMap[u].Cols[idx].Minutes += mins
			userMap[u].Cols[idx].Seconds += secs
			userMap[u].TotalMins += userMap[u].Cols[idx].Minutes
			userMap[u].TotalSeconds += userMap[u].Cols[idx].Seconds

		case "pe", "wa", "tle", "re", "mle", "ole", "ce":
			if userMap[u].Cols[idx].IsAC {
				continue
			}
			userMap[u].Cols[idx].Minutes += WATime
			userMap[u].Cols[idx].Seconds += WATime * 20
			userMap[u].Cols[idx].Attempt += 1
		}
	}
	var ranks []*ICPCRank
	for _, v := range userMap {
		ranks = append(ranks, v)
	}
	sort.Sort(ICPCRanks(ranks))
	lines := []*api.RankICPC_PerLine{}
	for k, r := range ranks {
		line := &api.RankICPC_PerLine{
			Rank:      int32(k + 1),
			Username:  r.Username,
			AcNum:     r.ACNum,
			TotalMins: r.TotalMins,
		}
		cols := []*api.RankICPC_PerLine_Column{}
		for _, c := range r.Cols {
			col := &api.RankICPC_PerLine_Column{
				IsAc:  c.IsAC,
				Label: c.Label,
				// IsAc:    true,
				IsFb:    c.IsFB,
				Attempt: int32(c.Attempt),
			}
			if c.IsAC {
				col.Minutes = c.Minutes
			}
			cols = append(cols, col)
		}
		line.Cols = cols
		lines = append(lines, line)
	}
	response.RankIcpc.Lines = lines
}
<|endoftext|>


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

package ctags

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

const debug = false

type ctagsProcess struct {
	cmd     *exec.Cmd
	in      io.WriteCloser
	out     *bufio.Scanner
	outPipe io.ReadCloser

	procErrMu sync.Mutex
	procErr   error
}

func newProcess(bin string) (*ctagsProcess, error) {
	opt := "default"
	if runtime.GOOS == "linux" {
		opt = "sandbox"
	}

	cmd := exec.Command(bin, "--_interactive="+opt, "--fields=*")
	in, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	out, err := cmd.StdoutPipe()
	if err != nil {
		in.Close()
		return nil, err
	}
	cmd.Stderr = os.Stderr
	proc := ctagsProcess{
		cmd:     cmd,
		in:      in,
		out:     bufio.NewScanner(out),
		outPipe: out,
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	var init reply
	if err := proc.read(&init); err != nil {
		return nil, err
	}

	return &proc, nil
}

func (p *ctagsProcess) Close() {
	p.cmd.Process.Kill()
	p.outPipe.Close()
	p.in.Close()
}

func (p *ctagsProcess) read(rep *reply) error {
	if !p.out.Scan() {
		// capture exit error.
		err := p.cmd.Wait()
		p.outPipe.Close()
		p.in.Close()
		return err
	}
	if debug {
		log.Printf("read %s", p.out.Text())
	}

	// See https://github.com/universal-ctags/ctags/issues/1493
	if bytes.Compare([]byte("(null)"), p.out.Bytes()) == 0 {
		return nil
	}

	err := json.Unmarshal(p.out.Bytes(), rep)
	if err != nil {
		return fmt.Errorf("unmarshal(%s): %v", p.out.Text(), err)
	}
	return nil
}

func (p *ctagsProcess) post(req *request, content []byte) error {
	body, err := json.Marshal(req)
	if err != nil {
		return err
	}
	body = append(body, '\n')
	if debug {
		log.Printf("post %q", body)
	}

	if _, err = p.in.Write(body); err != nil {
		return err
	}
	_, err = p.in.Write(content)
	if debug {
		log.Println(string(content))
	}
	return err
}

type request struct {
	Command  string `json:"command"`
	Filename string `json:"filename"`
	Size     int    `json:"size"`
}

type reply struct {
	// Init
	Typ     string `json:"_type"`
	Name    string `json:"name"`
	Version string `json:"version"`

	// completed
	Command string `json:"command"`

	// Ignore pattern: we don't use it and universal-ctags
	// sometimes generates 'false' as value.
	Path      string `json:"path"`
	Language  string `json:"language"`
	Line      int    `json:"line"`
	Kind      string `json:"kind"`
	End       int    `json:"end"`
	Scope     string `json:"scope"`
	ScopeKind string `json:"scopeKind"`
	Access    string `json:"access"`
	Signature string `json:"signature"`
}

func (p *ctagsProcess) Parse(name string, content []byte) ([]*Entry, error) {
	req := request{
		Command:  "generate-tags",
		Size:     len(content),
		Filename: name,
	}

	if err := p.post(&req, content); err != nil {
		return nil, err
	}

	var es []*Entry
	for {
		var rep reply
		if err := p.read(&rep); err != nil {
			return nil, err
		}
		if rep.Typ == "completed" {
			break
		}

		e := Entry{
			Sym:      rep.Name,
			Path:     rep.Path,
			Line:     rep.Line,
			Kind:     rep.Kind,
			Language: rep.Language,
		}

		es = append(es, &e)
	}

	return es, nil
}

type Parser interface {
	Parse(name string, content []byte) ([]*Entry, error)
}

type lockedParser struct {
	p Parser
	l sync.Mutex
}

func (lp *lockedParser) Parse(name string, content []byte) ([]*Entry, error) {
	lp.l.Lock()
	defer lp.l.Unlock()
	return lp.p.Parse(name, content)
}

func NewParser(bin string) (Parser, error) {
	if strings.Contains(bin, "universal-ctags") {
		// todo: restart, locking, parallelizatoin.
		proc, err := newProcess(bin)
		if err != nil {
			return nil, err
		}
		return &lockedParser{p: proc}, nil
	}

	log.Fatal("not implemented")
	return nil, nil
}
<commit_msg>ctags: clean up JSON parser<commit_after>// Copyright 2017 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ctags

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
)

const debug = false

type ctagsProcess struct {
	cmd     *exec.Cmd
	in      io.WriteCloser
	out     *bufio.Scanner
	outPipe io.ReadCloser
}

func newProcess(bin string) (*ctagsProcess, error) {
	opt := "default"
	if runtime.GOOS == "linux" {
		opt = "sandbox"
	}

	cmd := exec.Command(bin, "--_interactive="+opt, "--fields=*")
	in, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	out, err := cmd.StdoutPipe()
	if err != nil {
		in.Close()
		return nil, err
	}
	cmd.Stderr = os.Stderr
	proc := ctagsProcess{
		cmd:     cmd,
		in:      in,
		out:     bufio.NewScanner(out),
		outPipe: out,
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	var init reply
	if err := proc.read(&init); err != nil {
		return nil, err
	}

	return &proc, nil
}

func (p *ctagsProcess) Close() {
	p.cmd.Process.Kill()
	p.outPipe.Close()
	p.in.Close()
}

func (p *ctagsProcess) read(rep *reply) error {
	if !p.out.Scan() {
		// capture exit error.
		err := p.cmd.Wait()
		p.outPipe.Close()
		p.in.Close()
		return err
	}
	if debug {
		log.Printf("read %s", p.out.Text())
	}

	// See https://github.com/universal-ctags/ctags/issues/1493
	if bytes.Compare([]byte("(null)"), p.out.Bytes()) == 0 {
		return nil
	}

	err := json.Unmarshal(p.out.Bytes(), rep)
	if err != nil {
		return fmt.Errorf("unmarshal(%s): %v", p.out.Text(), err)
	}
	return nil
}

func (p *ctagsProcess) post(req *request, content []byte) error {
	body, err := json.Marshal(req)
	if err != nil {
		return err
	}
	body = append(body, '\n')
	if debug {
		log.Printf("post %q", body)
	}

	if _, err = p.in.Write(body); err != nil {
		return err
	}
	_, err = p.in.Write(content)
	if debug {
		log.Println(string(content))
	}
	return err
}

type request struct {
	Command  string `json:"command"`
	Filename string `json:"filename"`
	Size     int    `json:"size"`
}

type reply struct {
	// Init
	Typ     string `json:"_type"`
	Name    string `json:"name"`
	Version string `json:"version"`

	// completed
	Command string `json:"command"`

	// Ignore pattern: we don't use it and universal-ctags
	// sometimes generates 'false' as value.
	Path      string `json:"path"`
	Language  string `json:"language"`
	Line      int    `json:"line"`
	Kind      string `json:"kind"`
	End       int    `json:"end"`
	Scope     string `json:"scope"`
	ScopeKind string `json:"scopeKind"`
	Access    string `json:"access"`
	Signature string `json:"signature"`
}

func (p *ctagsProcess) Parse(name string, content []byte) ([]*Entry, error) {
	req := request{
		Command:  "generate-tags",
		Size:     len(content),
		Filename: name,
	}

	if err := p.post(&req, content); err != nil {
		return nil, err
	}

	var es []*Entry
	for {
		var rep reply
		if err := p.read(&rep); err != nil {
			return nil, err
		}
		if rep.Typ == "completed" {
			break
		}

		e := Entry{
			Sym:      rep.Name,
			Path:     rep.Path,
			Line:     rep.Line,
			Kind:     rep.Kind,
			Language: rep.Language,
		}

		es = append(es, &e)
	}

	return es, nil
}

type Parser interface {
	Parse(name string, content []byte) ([]*Entry, error)
}

type lockedParser struct {
	p Parser
	l sync.Mutex
}

func (lp *lockedParser) Parse(name string, content []byte) ([]*Entry, error) {
	lp.l.Lock()
	defer lp.l.Unlock()
	return lp.p.Parse(name, content)
}

// NewParser creates a parser that is implemented by the given
// universal-ctags binary. The parser is safe for concurrent use.
func NewParser(bin string) (Parser, error) {
	if strings.Contains(bin, "universal-ctags") {
		// todo: restart, parallelization.
		proc, err := newProcess(bin)
		if err != nil {
			return nil, err
		}
		return &lockedParser{p: proc}, nil
	}

	log.Fatal("not implemented")
	return nil, nil
}
<|endoftext|>


<commit_before>package dropbox

import (
	"encoding/json"
	"io"
	"time"
)

// Files client for files and folders.
type Files struct {
	*Client
}

// NewFiles client.
func NewFiles(config *Config) *Files {
	return &Files{
		Client: &Client{
			Config: config,
		},
	}
}

// WriteMode determines what to do if the file already exists.
type WriteMode string

// Supported write modes.
const (
	WriteModeAdd       WriteMode = "add"
	WriteModeOverwrite           = "overwrite"
)

// Metadata for a file or folder.
type Metadata struct {
	Tag            string    `json:".tag"`
	Name           string    `json:"name"`
	PathLower      string    `json:"path_lower"`
	ClientModified time.Time `json:"client_modified"`
	ServerModified time.Time `json:"server_modified"`
	Rev            string    `json:"rev"`
	Size           uint64    `json:"size"`
	ID             string    `json:"id"`
}

// GetMetadataInput request input.
type GetMetadataInput struct {
	Path             string `json:"path"`
	IncludeMediaInfo bool   `json:"include_media_info"`
}

// GetMetadataOutput request output.
type GetMetadataOutput struct {
	Metadata
}

// GetMetadata returns the metadata for a file or folder.
func (c *Files) GetMetadata(in *GetMetadataInput) (out *GetMetadataOutput, err error) {
	body, err := c.call("/files/get_metadata", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// CreateFolderInput request input.
type CreateFolderInput struct {
	Path string `json:"path"`
}

// CreateFolderOutput request output.
type CreateFolderOutput struct {
	Name      string `json:"name"`
	PathLower string `json:"path_lower"`
	ID        string `json:"id"`
}

// CreateFolder creates a folder.
func (c *Files) CreateFolder(in *CreateFolderInput) (out *CreateFolderOutput, err error) {
	body, err := c.call("/files/create_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DeleteInput request input.
type DeleteInput struct {
	Path string `json:"path"`
}

// DeleteOutput request output.
type DeleteOutput struct {
	Metadata
}

// Delete a file or folder and its contents.
func (c *Files) Delete(in *DeleteInput) (out *DeleteOutput, err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// PermanentlyDeleteInput request input.
type PermanentlyDeleteInput struct {
	Path string `json:"path"`
}

// PermanentlyDelete a file or folder and its contents.
func (c *Files) PermanentlyDelete(in *PermanentlyDeleteInput) (err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	return
}

// CopyInput request input.
type CopyInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// CopyOutput request output.
type CopyOutput struct {
	Metadata
}

// Copy a file or folder to a different location.
func (c *Files) Copy(in *CopyInput) (out *CopyOutput, err error) {
	body, err := c.call("/files/copy", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// MoveInput request input.
type MoveInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// MoveOutput request output.
type MoveOutput struct {
	Metadata
}

// Move a file or folder to a different location.
func (c *Files) Move(in *MoveInput) (out *MoveOutput, err error) {
	body, err := c.call("/files/move", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// RestoreInput request input.
type RestoreInput struct {
	Path string `json:"path"`
	Rev  string `json:"rev"`
}

// RestoreOutput request output.
type RestoreOutput struct {
	Metadata
}

// Restore a file to a specific revision.
func (c *Files) Restore(in *RestoreInput) (out *RestoreOutput, err error) {
	body, err := c.call("/files/restore", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderInput request input.
type ListFolderInput struct {
	Path             string `json:"path"`
	Recursive        bool   `json:"recursive"`
	IncludeMediaInfo bool   `json:"include_media_info"`
	IncludeDeleted   bool   `json:"include_deleted"`
}

// ListFolderOutput request output.
type ListFolderOutput struct {
	Cursor  string `json:"cursor"`
	HasMore bool   `json:"has_more"`
	Entries []*Metadata
}

// ListFolder returns the metadata for a file or folder.
func (c *Files) ListFolder(in *ListFolderInput) (out *ListFolderOutput, err error) {
	in.Path = normalizePath(in.Path)

	body, err := c.call("/files/list_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderContinueInput request input.
type ListFolderContinueInput struct {
	Cursor string `json:"cursor"`
}

// ListFolderContinue pagenates using the cursor from ListFolder.
func (c *Files) ListFolderContinue(in *ListFolderContinueInput) (out *ListFolderOutput, err error) {
	body, err := c.call("/files/list_folder/continue", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// SearchMode determines how a search is performed.
type SearchMode string

// Supported search modes.
const (
	SearchModeFilename           SearchMode = "filename"
	SearchModeFilenameAndContent            = "filename_and_content"
	SearchModeDeletedFilename               = "deleted_filename"
)

// SearchMatchType represents the type of match made.
type SearchMatchType string

// Supported search match types.
const (
	SearchMatchFilename SearchMatchType = "filename"
	SearchMatchContent                  = "content"
	SearchMatchBoth                     = "both"
)

// SearchMatch represents a matched file or folder.
type SearchMatch struct {
	MatchType struct {
		Tag SearchMatchType `json:".tag"`
	} `json:"match_type"`
	Metadata *Metadata `json:"metadata"`
}

// SearchInput request input.
type SearchInput struct {
	Path       string     `json:"path"`
	Query      string     `json:"query"`
	Start      uint64     `json:"start,omitempty"`
	MaxResults uint64     `json:"max_results,omitempty"`
	Mode       SearchMode `json:"mode"`
}

// SearchOutput request output.
type SearchOutput struct {
	Matches []*SearchMatch `json:"matches"`
	More    bool           `json:"more"`
	Start   uint64         `json:"start"`
}

// Search for files and folders.
func (c *Files) Search(in *SearchInput) (out *SearchOutput, err error) {
	in.Path = normalizePath(in.Path)

	if in.Mode == "" {
		in.Mode = SearchModeFilename
	}

	body, err := c.call("/files/search", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// UploadInput request input.
type UploadInput struct {
	Path           string    `json:"path"`
	Mode           WriteMode `json:"mode"`
	AutoRename     bool      `json:"autorename"`
	Mute           bool      `json:"mute"`
	ClientModified time.Time `json:"client_modified,omitempty"`
	Reader         io.Reader `json:"-"`
}

// UploadOutput request output.
type UploadOutput struct {
	Metadata
}

// Upload a file smaller than 150MB.
func (c *Files) Upload(in *UploadInput) (out *UploadOutput, err error) {
	body, _, err := c.download("/files/upload", in, in.Reader)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DownloadInput request input.
type DownloadInput struct {
	Path string `json:"path"`
}

// DownloadOutput request output.
type DownloadOutput struct {
	Body   io.ReadCloser
	Length int64
}

// Download a file.
func (c *Files) Download(in *DownloadInput) (out *DownloadOutput, err error) {
	body, l, err := c.download("/files/download", in, nil)
	if err != nil {
		return
	}

	out = &DownloadOutput{body, l}
	return
}

// ThumbnailFormat determines the format of the thumbnail.
type ThumbnailFormat string

const (
	// GetThumbnailFormatJPEG specifies a JPG thumbnail
	GetThumbnailFormatJPEG ThumbnailFormat = "jpeg"
	// GetThumbnailFormatPNG specifies a PNG thumbnail
	GetThumbnailFormatPNG = "png"
)

// ThumbnailFormat determines the size of the thumbnail.
type ThumbnailSize string

const (
	// GetThumbnailSizeW32H32 specifies a size of 32 by 32 px
	GetThumbnailSizeW32H32 ThumbnailSize = "w32h32"
	// GetThumbnailSizeW64H64 specifies a size of 64 by 64 px
	GetThumbnailSizeW64H64 = "w64h64"
	// GetThumbnailSizeW128H128 specifies a size of 128 by 128 px
	GetThumbnailSizeW128H128 = "w128h128"
	// GetThumbnailSizeW640H480 specifies a size of 640 by 480 px
	GetThumbnailSizeW640H480 = "w640h480"
	// GetThumbnailSizeW1024H768 specifies a size of 1024 by 768 px
	GetThumbnailSizeW1024H768 = "w1024h768"
)

// GetThumbnailInput request input.
type GetThumbnailInput struct {
	Path   string          `json:"path"`
	Format ThumbnailFormat `json:"format"`
	Size   ThumbnailSize   `json:"size"`
}

// GetThumbnailOutput request output.
type GetThumbnailOutput struct {
	Body   io.ReadCloser
	Length int64
}

// GetThumbnail a thumbnail for a file. Currently thumbnails are only generated for the
// files with the following extensions: png, jpeg, png, tiff, tif, gif and bmp.
func (c *Files) GetThumbnail(in *GetThumbnailInput) (out *GetThumbnailOutput, err error) {
	body, l, err := c.download("/files/get_thumbnail", in, nil)
	if err != nil {
		return
	}

	out = &GetThumbnailOutput{body, l}
	return
}

// GetPreviewInput request input.
type GetPreviewInput struct {
	Path string `json:"path"`
}

// GetPreviewOutput request output.
type GetPreviewOutput struct {
	Body   io.ReadCloser
	Length int64
}

// GetPreview a preview for a file. Currently previews are only generated for the
// files with the following extensions: .doc, .docx, .docm, .ppt, .pps, .ppsx,
// .ppsm, .pptx, .pptm, .xls, .xlsx, .xlsm, .rtf
func (c *Files) GetPreview(in *GetPreviewInput) (out *GetPreviewOutput, err error) {
	body, l, err := c.download("/files/get_preview", in, nil)
	if err != nil {
		return
	}

	out = &GetPreviewOutput{body, l}
	return
}

// ListRevisionsInput request input.
type ListRevisionsInput struct {
	Path  string `json:"path"`
	Limit uint64 `json:"limit,omitempty"`
}

// ListRevisionsOutput request output.
type ListRevisionsOutput struct {
	IsDeleted bool
	Entries   []*Metadata
}

// ListRevisions gets the revisions of the specified file.
func (c *Files) ListRevisions(in *ListRevisionsInput) (out *ListRevisionsOutput, err error) {
	body, err := c.call("/files/list_revisions", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// Normalize path so people can use "/" as they expect.
func normalizePath(s string) string {
	if s == "/" {
		return ""
	} else {
		return s
	}
}
<commit_msg>add MediaInfo and SharingInfo to Metadata struct<commit_after>package dropbox

import (
	"encoding/json"
	"io"
	"time"
)

// Files client for files and folders.
type Files struct {
	*Client
}

// NewFiles client.
func NewFiles(config *Config) *Files {
	return &Files{
		Client: &Client{
			Config: config,
		},
	}
}

// WriteMode determines what to do if the file already exists.
type WriteMode string

// Supported write modes.
const (
	WriteModeAdd       WriteMode = "add"
	WriteModeOverwrite           = "overwrite"
)

// Dimensions specifies the dimensions of a photo or video.
type Dimensions struct {
	Width  uint64 `json:"width"`
	Height uint64 `json:"height"`
}

// GPSCoordinates specifies the GPS coordinate of a photo or video.
type GPSCoordinates struct {
	Latitude  float64 `json:"latitude"`
	Longitude float64 `json:"longitude"`
}

// PhotoMetadata specifies metadata for a photo.
type PhotoMetadata struct {
	Dimensions *Dimensions     `json:"dimensions,omitempty"`
	Location   *GPSCoordinates `json:"location,omitempty"`
	TimeTaken  time.Time       `json:"time_taken,omitempty"`
}

// VideoMetadata specifies metadata for a video.
type VideoMetadata struct {
	Dimensions *Dimensions     `json:"dimensions,omitempty"`
	Location   *GPSCoordinates `json:"location,omitempty"`
	TimeTaken  time.Time       `json:"time_taken,omitempty"`
	Duration   uint64          `json:"duration,omitempty"`
}

// MediaMetadata provides metadata for a photo or video.
type MediaMetadata struct {
	Photo *PhotoMetadata `json:"photo,omitempty"`
	Video *VideoMetadata `json:"video,omitempty"`
}

// MediaInfo provides additional information for a photo or video file.
type MediaInfo struct {
	Pending  bool           `json:"pending"`
	Metadata *MediaMetadata `json:"metadata,omitempty"`
}

// FileSharingInfo for a file which is contained in a shared folder.
type FileSharingInfo struct {
	ReadOnly             bool   `json:"read_only"`
	ParentSharedFolderID string `json:"parent_shared_folder_id"`
	ModifiedBy           string `json:"modified_by,omitempty"`
}

// Metadata for a file or folder.
type Metadata struct {
	Tag            string           `json:".tag"`
	Name           string           `json:"name"`
	PathLower      string           `json:"path_lower"`
	ClientModified time.Time        `json:"client_modified"`
	ServerModified time.Time        `json:"server_modified"`
	Rev            string           `json:"rev"`
	Size           uint64           `json:"size"`
	ID             string           `json:"id"`
	MediaInfo      *MediaInfo       `json:"media_info,omitempty"`
	SharingInfo    *FileSharingInfo `json:"sharing_info,omitempty"`
}

// GetMetadataInput request input.
type GetMetadataInput struct {
	Path             string `json:"path"`
	IncludeMediaInfo bool   `json:"include_media_info"`
}

// GetMetadataOutput request output.
type GetMetadataOutput struct {
	Metadata
}

// GetMetadata returns the metadata for a file or folder.
func (c *Files) GetMetadata(in *GetMetadataInput) (out *GetMetadataOutput, err error) {
	body, err := c.call("/files/get_metadata", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// CreateFolderInput request input.
type CreateFolderInput struct {
	Path string `json:"path"`
}

// CreateFolderOutput request output.
type CreateFolderOutput struct {
	Name      string `json:"name"`
	PathLower string `json:"path_lower"`
	ID        string `json:"id"`
}

// CreateFolder creates a folder.
func (c *Files) CreateFolder(in *CreateFolderInput) (out *CreateFolderOutput, err error) {
	body, err := c.call("/files/create_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DeleteInput request input.
type DeleteInput struct {
	Path string `json:"path"`
}

// DeleteOutput request output.
type DeleteOutput struct {
	Metadata
}

// Delete a file or folder and its contents.
func (c *Files) Delete(in *DeleteInput) (out *DeleteOutput, err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// PermanentlyDeleteInput request input.
type PermanentlyDeleteInput struct {
	Path string `json:"path"`
}

// PermanentlyDelete a file or folder and its contents.
func (c *Files) PermanentlyDelete(in *PermanentlyDeleteInput) (err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	return
}

// CopyInput request input.
type CopyInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// CopyOutput request output.
type CopyOutput struct {
	Metadata
}

// Copy a file or folder to a different location.
func (c *Files) Copy(in *CopyInput) (out *CopyOutput, err error) {
	body, err := c.call("/files/copy", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// MoveInput request input.
type MoveInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// MoveOutput request output.
type MoveOutput struct {
	Metadata
}

// Move a file or folder to a different location.
func (c *Files) Move(in *MoveInput) (out *MoveOutput, err error) {
	body, err := c.call("/files/move", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// RestoreInput request input.
type RestoreInput struct {
	Path string `json:"path"`
	Rev  string `json:"rev"`
}

// RestoreOutput request output.
type RestoreOutput struct {
	Metadata
}

// Restore a file to a specific revision.
func (c *Files) Restore(in *RestoreInput) (out *RestoreOutput, err error) {
	body, err := c.call("/files/restore", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderInput request input.
type ListFolderInput struct {
	Path             string `json:"path"`
	Recursive        bool   `json:"recursive"`
	IncludeMediaInfo bool   `json:"include_media_info"`
	IncludeDeleted   bool   `json:"include_deleted"`
}

// ListFolderOutput request output.
type ListFolderOutput struct {
	Cursor  string `json:"cursor"`
	HasMore bool   `json:"has_more"`
	Entries []*Metadata
}

// ListFolder returns the metadata for a file or folder.
func (c *Files) ListFolder(in *ListFolderInput) (out *ListFolderOutput, err error) {
	in.Path = normalizePath(in.Path)

	body, err := c.call("/files/list_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderContinueInput request input.
type ListFolderContinueInput struct {
	Cursor string `json:"cursor"`
}

// ListFolderContinue pagenates using the cursor from ListFolder.
func (c *Files) ListFolderContinue(in *ListFolderContinueInput) (out *ListFolderOutput, err error) {
	body, err := c.call("/files/list_folder/continue", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// SearchMode determines how a search is performed.
type SearchMode string

// Supported search modes.
const (
	SearchModeFilename           SearchMode = "filename"
	SearchModeFilenameAndContent            = "filename_and_content"
	SearchModeDeletedFilename               = "deleted_filename"
)

// SearchMatchType represents the type of match made.
type SearchMatchType string

// Supported search match types.
const (
	SearchMatchFilename SearchMatchType = "filename"
	SearchMatchContent                  = "content"
	SearchMatchBoth                     = "both"
)

// SearchMatch represents a matched file or folder.
type SearchMatch struct {
	MatchType struct {
		Tag SearchMatchType `json:".tag"`
	} `json:"match_type"`
	Metadata *Metadata `json:"metadata"`
}

// SearchInput request input.
type SearchInput struct {
	Path       string     `json:"path"`
	Query      string     `json:"query"`
	Start      uint64     `json:"start,omitempty"`
	MaxResults uint64     `json:"max_results,omitempty"`
	Mode       SearchMode `json:"mode"`
}

// SearchOutput request output.
type SearchOutput struct {
	Matches []*SearchMatch `json:"matches"`
	More    bool           `json:"more"`
	Start   uint64         `json:"start"`
}

// Search for files and folders.
func (c *Files) Search(in *SearchInput) (out *SearchOutput, err error) {
	in.Path = normalizePath(in.Path)

	if in.Mode == "" {
		in.Mode = SearchModeFilename
	}

	body, err := c.call("/files/search", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// UploadInput request input.
type UploadInput struct {
	Path           string    `json:"path"`
	Mode           WriteMode `json:"mode"`
	AutoRename     bool      `json:"autorename"`
	Mute           bool      `json:"mute"`
	ClientModified time.Time `json:"client_modified,omitempty"`
	Reader         io.Reader `json:"-"`
}

// UploadOutput request output.
type UploadOutput struct {
	Metadata
}

// Upload a file smaller than 150MB.
func (c *Files) Upload(in *UploadInput) (out *UploadOutput, err error) {
	body, _, err := c.download("/files/upload", in, in.Reader)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DownloadInput request input.
type DownloadInput struct {
	Path string `json:"path"`
}

// DownloadOutput request output.
type DownloadOutput struct {
	Body   io.ReadCloser
	Length int64
}

// Download a file.
func (c *Files) Download(in *DownloadInput) (out *DownloadOutput, err error) {
	body, l, err := c.download("/files/download", in, nil)
	if err != nil {
		return
	}

	out = &DownloadOutput{body, l}
	return
}

// ThumbnailFormat determines the format of the thumbnail.
type ThumbnailFormat string

const (
	// GetThumbnailFormatJPEG specifies a JPG thumbnail
	GetThumbnailFormatJPEG ThumbnailFormat = "jpeg"
	// GetThumbnailFormatPNG specifies a PNG thumbnail
	GetThumbnailFormatPNG = "png"
)

// ThumbnailFormat determines the size of the thumbnail.
type ThumbnailSize string

const (
	// GetThumbnailSizeW32H32 specifies a size of 32 by 32 px
	GetThumbnailSizeW32H32 ThumbnailSize = "w32h32"
	// GetThumbnailSizeW64H64 specifies a size of 64 by 64 px
	GetThumbnailSizeW64H64 = "w64h64"
	// GetThumbnailSizeW128H128 specifies a size of 128 by 128 px
	GetThumbnailSizeW128H128 = "w128h128"
	// GetThumbnailSizeW640H480 specifies a size of 640 by 480 px
	GetThumbnailSizeW640H480 = "w640h480"
	// GetThumbnailSizeW1024H768 specifies a size of 1024 by 768 px
	GetThumbnailSizeW1024H768 = "w1024h768"
)

// GetThumbnailInput request input.
type GetThumbnailInput struct {
	Path   string          `json:"path"`
	Format ThumbnailFormat `json:"format"`
	Size   ThumbnailSize   `json:"size"`
}

// GetThumbnailOutput request output.
type GetThumbnailOutput struct {
	Body   io.ReadCloser
	Length int64
}

// GetThumbnail a thumbnail for a file. Currently thumbnails are only generated for the
// files with the following extensions: png, jpeg, png, tiff, tif, gif and bmp.
func (c *Files) GetThumbnail(in *GetThumbnailInput) (out *GetThumbnailOutput, err error) {
	body, l, err := c.download("/files/get_thumbnail", in, nil)
	if err != nil {
		return
	}

	out = &GetThumbnailOutput{body, l}
	return
}

// GetPreviewInput request input.
type GetPreviewInput struct {
	Path string `json:"path"`
}

// GetPreviewOutput request output.
type GetPreviewOutput struct {
	Body   io.ReadCloser
	Length int64
}

// GetPreview a preview for a file. Currently previews are only generated for the
// files with the following extensions: .doc, .docx, .docm, .ppt, .pps, .ppsx,
// .ppsm, .pptx, .pptm, .xls, .xlsx, .xlsm, .rtf
func (c *Files) GetPreview(in *GetPreviewInput) (out *GetPreviewOutput, err error) {
	body, l, err := c.download("/files/get_preview", in, nil)
	if err != nil {
		return
	}

	out = &GetPreviewOutput{body, l}
	return
}

// ListRevisionsInput request input.
type ListRevisionsInput struct {
	Path  string `json:"path"`
	Limit uint64 `json:"limit,omitempty"`
}

// ListRevisionsOutput request output.
type ListRevisionsOutput struct {
	IsDeleted bool
	Entries   []*Metadata
}

// ListRevisions gets the revisions of the specified file.
func (c *Files) ListRevisions(in *ListRevisionsInput) (out *ListRevisionsOutput, err error) {
	body, err := c.call("/files/list_revisions", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// Normalize path so people can use "/" as they expect.
func normalizePath(s string) string {
	if s == "/" {
		return ""
	} else {
		return s
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/tarm/serial"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

func flashGame(port string) {
	c := &serial.Config{Name: port, Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("fram_claw.bin")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Waiting for response from Dodo...")

	r, err := readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('R') {
		fmt.Println(rune(r))
		panic("Did not read 'R'")
	} else {
		fmt.Println("Preparing to write...")
	}

	fmt.Println("Sent command to flash game")
	n, err := s.Write([]byte{byte('G')})
	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	r, err = readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	for _, b := range dat {
		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err = readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	fmt.Println("Success!")
}

func flashSystem(port string) {
	c := &serial.Config{Name: port, Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("firmware")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Waiting for response from Dodo...")

	r, err := readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('R') {
		fmt.Println(rune(r))
		panic("Did not read 'R'")
	} else {
		fmt.Println("Preparing to write...")
	}

	fmt.Println("Sent command to flash system")
	n, err := s.Write([]byte{byte('S')})
	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	for i, b := range dat {
		if i%64 == 0 {
			r, err = readByte(s)
			if err != nil {
				log.Fatal(err)
				return
			}

			if r != byte('A') {
				fmt.Println("The byte is ", r)
				panic("Did not read 'A'")
			}
		}

		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err = readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	fmt.Println("Success!")
}

func readByte(s *serial.Port) (byte, error) {
	buf := make([]byte, 1)
	n, err := s.Read(buf)
	if err != nil {
		return 0, err
	}

	if n != 1 {
		return 0, errors.New("Did not read single byte")
	}

	return buf[0], nil
}

func Flash() {
	port := "/dev/tty.usbserial-A6040I72"

	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Println("Flash [S]ystem or [G]ame or [Q]uit?")
		a, _ := reader.ReadString('\n')
		a = strings.TrimSuffix(a, "\n")
		switch a {
		case "s", "S":
			fmt.Println("Flashing system...")
			flashSystem(port)
			return
		case "g", "G":
			fmt.Println("Flashing game...")
			flashGame(port)
			return
		case "q", "Q":
			fmt.Println("Quiting...")
			return
		default:
			fmt.Println("Invalid response")
			break
		}
	}

	c := &serial.Config{Name: "/dev/tty.usbserial-A6040I72", Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("fram.bin")
	if err != nil {
		log.Fatal(err)
	}

	buf := make([]byte, 1)
	n, err := s.Read(buf)

	if err != nil {
		log.Fatal(err)
	}

	if n != 1 {
		panic("Did not read a byte")
	}

	if buf[0] != byte('R') {
		fmt.Println(rune(buf[0]))
		panic("Did not read 'R'")
	} else {
		fmt.Println("Writing...")
	}

	fmt.Println("Wrote a G")
	n, err = s.Write([]byte{byte('G')})

	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	for _, b := range dat {
		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	s.Read(buf)

	fmt.Println("Done!")
}
<commit_msg>Flashing now streamlined<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/tarm/serial"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

func checkVersion(port string) {
	c := &serial.Config{Name: port, Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Sent command to read version")
	n, err := s.Write([]byte{byte('V')})
	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	version := make([]byte, 0)
	for {
		r, err := readByte(s)

		fmt.Println("Just Read: ", r, " ", string([]byte{r}))

		if err != nil {
			log.Fatal(err)
			return
		}

		if r == 0 {
			break
		}

		version = append(version, r)
	}

	fmt.Println("Read Version: ", string(version))
}

func flashGame(port string) {
	c := &serial.Config{Name: port, Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("fram.bin")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Sent command to flash game")
	n, err := s.Write([]byte{byte('G')})
	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	r, err := readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	for _, b := range dat {
		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err = readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	fmt.Println("Success!")
}

func flashSystem(port string) {
	c := &serial.Config{Name: port, Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("firmware")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Sent command to flash system")
	n, err := s.Write([]byte{byte('S')})
	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	for i, b := range dat {
		if i%64 == 0 {
			r, err := readByte(s)
			if err != nil {
				log.Fatal(err)
				return
			}

			if r != byte('A') {
				fmt.Println("The byte is ", r)
				panic("Did not read 'A'")
			}
		}

		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	r, err := readByte(s)
	if err != nil {
		log.Fatal(err)
		return
	}

	if r != byte('A') {
		panic("Did not read 'A'")
		return
	}

	fmt.Println("Success!")
}

func readByte(s *serial.Port) (byte, error) {
	buf := make([]byte, 1)
	n, err := s.Read(buf)
	if err != nil {
		return 0, err
	}

	if n != 1 {
		return 0, errors.New("Did not read single byte")
	}

	return buf[0], nil
}

func Flash() {
	port := "/dev/tty.usbserial-A6040I72"

	reader := bufio.NewReader(os.Stdin)

	for {
		fmt.Println("Flash [S]ystem or [G]ame, check [V]ersion or [Q]uit?")
		a, _ := reader.ReadString('\n')
		a = strings.TrimSuffix(a, "\n")
		switch a {
		case "s", "S":
			fmt.Println("Flashing system...")
			flashSystem(port)
			return
		case "g", "G":
			fmt.Println("Flashing game...")
			flashGame(port)
			return
		case "v", "V":
			fmt.Println("Checking version...")
			checkVersion(port)
			return
		case "q", "Q":
			fmt.Println("Quiting...")
			return
		default:
			fmt.Println("Invalid response")
			break
		}
	}

	c := &serial.Config{Name: "/dev/tty.usbserial-A6040I72", Baud: 9600}
	s, err := serial.OpenPort(c)

	defer s.Close()

	if err != nil {
		log.Fatal(err)
	}

	dat, err := ioutil.ReadFile("fram.bin")
	if err != nil {
		log.Fatal(err)
	}

	buf := make([]byte, 1)
	n, err := s.Read(buf)

	if err != nil {
		log.Fatal(err)
	}

	if n != 1 {
		panic("Did not read a byte")
	}

	if buf[0] != byte('R') {
		fmt.Println(rune(buf[0]))
		panic("Did not read 'R'")
	} else {
		fmt.Println("Writing...")
	}

	fmt.Println("Wrote a G")
	n, err = s.Write([]byte{byte('G')})

	if n != 1 {
		panic("Did not write a byte")
	}
	if err != nil {
		log.Fatal(err)
	}

	for _, b := range dat {
		n, err = s.Write([]byte{b})
		if n != 1 {
			panic("Did not write a byte")
		}
		if err != nil {
			log.Fatal(err)
		}
	}

	s.Read(buf)

	fmt.Println("Done!")
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "os"
    "time"
    "labix.org/v2/mgo"
    "labix.org/v2/mgo/bson"
    "strconv"
)

type FindType string
const (
    Recently FindType = "-last_access"
    Frequently FindType = "-count"
)

type Log struct {
    Id    bson.ObjectId `bson:"_id"`
    Path  string        `bson:"path"`
    Last  time.Time     `bson:"last_access"`
    Count float32       `bson:"count"`
}

func LogCurrent(c *mgo.Collection){
    path := os.Getenv("PWD")
    if os.Getenv("HOME") != path {
        c.Upsert(bson.M{"path": path}, bson.M{"$set": bson.M{"path": path, "last_access": time.Now()}, "$inc":bson.M{"count":1}})
    }
}

func RecentlyFrequently(c *mgo.Collection, sort FindType){
    target := getTarget()
    if target < 0 {
        listRecentyFrequently(c,sort)
    } else {
        printTarget(c,sort,target)
    }
}

func printTarget(c *mgo.Collection, sort FindType, target int) {
    item := &Log{}
    c.Find(nil).Sort(string(sort)).Skip(target).One(item)
    fmt.Println(item.Path)
}

func listRecentyFrequently(c *mgo.Collection, sort FindType) {
    iter := c.Find(nil).Sort(string(sort)).Limit(20).Iter()
    item := &Log{}
    i := 0
    fmt.Printf("usage: %s target_index\n",os.Args[0])
    for iter.Next(&item) {
        fmt.Printf("%2d- %s\n",i,item.Path)
        i+=1
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }
}

func getTarget() int {
    if len(os.Args) > 1 {
        target, err := strconv.Atoi(os.Args[1])
        if nil != err {
            fmt.Fprintf(os.Stderr,"Error parsing target: %v\n",err)
            return -1
        }
        return target
    }
    return -1
}

func DampenFrequency(c *mgo.Collection) {
    iter := c.Find(nil).Iter()
    item := &Log{}
    for iter.Next(&item) {
        item.Count /= 2.0
        c.Update(bson.M{"_id": item.Id},item)
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }
}

func RemoveDead(c *mgo.Collection) {
    count, err := c.Count()
    if nil != err {
        fmt.Fprintf(os.Stderr,"Can't get collection count: %v\n",err)
        return
    }
    limit := float32(count) * 0.1
    if limit < 20 {
        return
    }
    iter := c.Find(bson.M{"count": bson.M{"$lte": 0.25}}).Sort("last_access").Limit(int(limit)).Iter()
    item := &Log{}
    for iter.Next(&item) {
        fmt.Printf("Forgetting about %s\n",item.Path)
        if err := c.Remove(bson.M{"_id": item.Id}); nil != err {
            fmt.Fprintf(os.Stderr,"Error removing item (%v) iter: %v\n",item.Id,err)
        }
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }

}

func main () {
    uri := os.Getenv("CWD_LOGGER_URI")
    if "" == uri {
        // mongodb://user:pass@server.mongohq.com/db_name
        uri = "mongodb://localhost/" + os.Getenv("USER")
    }
    sess, err := mgo.DialWithTimeout(uri,500*time.Millisecond)
    if nil != err {
        fmt.Fprintf(os.Stderr,"Can't connect to mongo: %v\n", err)
        os.Exit(1)
    }
    defer sess.Close()

    collection := sess.DB("").C("logged_dirs")

    switch os.Args[0] {
    case "cwd_logger", "log_cwd", "go_cwd_logger":
        LogCurrent(collection)
    case "cwd_recently":
        RecentlyFrequently(collection, Recently)
    case "cwd_frequency":
        RecentlyFrequently(collection, Frequently)
    case "cwd_dampen_frequency":
        DampenFrequency(collection)
        RemoveDead(collection)
    default:
        LogCurrent(collection)
    }
}
<commit_msg>Removes whitespace, pointlessly<commit_after>package main

import (
    "fmt"
    "os"
    "time"
    "labix.org/v2/mgo"
    "labix.org/v2/mgo/bson"
    "strconv"
)

type FindType string
const (
    Recently FindType = "-last_access"
    Frequently FindType = "-count"
)

type Log struct {
    Id    bson.ObjectId `bson:"_id"`
    Path  string        `bson:"path"`
    Last  time.Time     `bson:"last_access"`
    Count float32       `bson:"count"`
}

func LogCurrent(c *mgo.Collection){
    path := os.Getenv("PWD")
    if os.Getenv("HOME") != path {
        c.Upsert(bson.M{"path": path}, bson.M{"$set": bson.M{"path": path, "last_access": time.Now()}, "$inc":bson.M{"count":1}})
    }
}

func RecentlyFrequently(c *mgo.Collection, sort FindType){
    target := getTarget()
    if target < 0 {
        listRecentyFrequently(c,sort)
    } else {
        printTarget(c,sort,target)
    }
}

func printTarget(c *mgo.Collection, sort FindType, target int) {
    item := &Log{}
    c.Find(nil).Sort(string(sort)).Skip(target).One(item)
    fmt.Println(item.Path)
}

func listRecentyFrequently(c *mgo.Collection, sort FindType) {
    iter := c.Find(nil).Sort(string(sort)).Limit(20).Iter()
    item := &Log{}
    i := 0
    fmt.Printf("usage: %s target_index\n",os.Args[0])
    for iter.Next(&item) {
        fmt.Printf("%2d- %s\n",i,item.Path)
        i+=1
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }
}

func getTarget() int {
    if len(os.Args) > 1 {
        target, err := strconv.Atoi(os.Args[1])
        if nil != err {
            fmt.Fprintf(os.Stderr,"Error parsing target: %v\n",err)
            return -1
        }
        return target
    }
    return -1
}

func DampenFrequency(c *mgo.Collection) {
    iter := c.Find(nil).Iter()
    item := &Log{}
    for iter.Next(&item) {
        item.Count /= 2.0
        c.Update(bson.M{"_id": item.Id},item)
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }
}

func RemoveDead(c *mgo.Collection) {
    count, err := c.Count()
    if nil != err {
        fmt.Fprintf(os.Stderr,"Can't get collection count: %v\n",err)
        return
    }
    limit := float32(count) * 0.1
    if limit < 20 {
        return
    }
    iter := c.Find(bson.M{"count": bson.M{"$lte": 0.25}}).Sort("last_access").Limit(int(limit)).Iter()
    item := &Log{}
    for iter.Next(&item) {
        fmt.Printf("Forgetting about %s\n",item.Path)
        if err := c.Remove(bson.M{"_id": item.Id}); nil != err {
            fmt.Fprintf(os.Stderr,"Error removing item (%v) iter: %v\n",item.Id,err)
        }
    }
    if err := iter.Close(); err != nil {
        fmt.Fprintf(os.Stderr,"Error closing iter: %v\n",err)
    }
}

func main () {
    uri := os.Getenv("CWD_LOGGER_URI")
    if "" == uri {
        // mongodb://user:pass@server.mongohq.com/db_name
        uri = "mongodb://localhost/" + os.Getenv("USER")
    }
    sess, err := mgo.DialWithTimeout(uri,500*time.Millisecond)
    if nil != err {
        fmt.Fprintf(os.Stderr,"Can't connect to mongo: %v\n", err)
        os.Exit(1)
    }
    defer sess.Close()

    collection := sess.DB("").C("logged_dirs")

    switch os.Args[0] {
    case "cwd_logger", "log_cwd", "go_cwd_logger":
        LogCurrent(collection)
    case "cwd_recently":
        RecentlyFrequently(collection, Recently)
    case "cwd_frequency":
        RecentlyFrequently(collection, Frequently)
    case "cwd_dampen_frequency":
        DampenFrequency(collection)
        RemoveDead(collection)
    default:
        LogCurrent(collection)
    }
}
<|endoftext|>


<commit_before>package heka_clever_plugins

import (
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/Clever/heka-clever-plugins/postgres"
	_ "github.com/lib/pq"
	"github.com/mozilla-services/heka/message"
	. "github.com/mozilla-services/heka/pipeline"
)

type PostgresOutput struct {
	db *postgres.PostgresDB

	helper           PluginHelper
	runner           OutputRunner
	lastMsgLoopCount uint

	insertSchema              string
	insertTable               string
	insertMessageFields       []string
	insertTableColumns        []string
	flushInterval             uint32
	flushCount                int // Max messages before flush
	allowMissingMessageFields bool
}

type PostgresOutputConfig struct {
	// Table name and colums. Message fields to write.
	InsertSchema        string `toml:"insert_schema"`
	InsertTable         string `toml:"insert_table"`
	InsertTableColumns  string `toml:"insert_table_columns"`
	InsertMessageFields string `toml:"insert_message_fields"`
	// If a field is missing in the Heka message, allow writing NULL
	AllowMissingMessageFields bool `toml:"allow_missing_message_fields"`

	// Database Connection
	DBHost               string `toml:"db_host"`
	DBPort               int    `toml:"db_port"`
	DBName               string `toml:"db_name"`
	DBUser               string `toml:"db_user"`
	DBPassword           string `toml:"db_password"`
	DBConnectionTimeout  int    `toml:"db_connection_timeout"`
	DBMaxOpenConnections int    `toml:"db_max_open_connections"`
	DBSSLMode            string `toml:"db_ssl_mode"`

	// Interval at which accumulated messages should be written to Postgres,
	// in milliseconds (default 1000, i.e. 1 second)
	FlushInterval uint32 `toml:"flush_interval"`
	// Number of messages that triggers a write to Postgres (default 10000)
	FlushCount int `toml:"flush_count"`
}

func (po *PostgresOutput) ConfigStruct() interface{} {
	return &PostgresOutputConfig{
		AllowMissingMessageFields: true,
		DBConnectionTimeout:       5,
		DBMaxOpenConnections:      10,
		DBSSLMode:                 "require",
		FlushInterval:             uint32(1000),
		FlushCount:                10000,
		InsertSchema:              "public",
	}
}

func (po *PostgresOutput) Init(rawConf interface{}) error {
	config := rawConf.(*PostgresOutputConfig)
	po.flushInterval = config.FlushInterval
	po.flushCount = config.FlushCount
	po.insertSchema = config.InsertSchema
	po.insertTable = config.InsertTable
	if config.InsertMessageFields == "" {
		return fmt.Errorf("config item 'insert_message_fields' cannot be empty string")
	}
	po.insertMessageFields = strings.Split(config.InsertMessageFields, " ")
	if config.InsertTableColumns == "" {
		return fmt.Errorf("config item 'insert_table_columns' cannot be empty string")
	}
	po.insertTableColumns = strings.Split(config.InsertTableColumns, " ")
	po.allowMissingMessageFields = config.AllowMissingMessageFields
	p := postgres.DBConnectionParams{
		Host:           config.DBHost,
		Port:           config.DBPort,
		DBName:         config.DBName,
		User:           config.DBUser,
		Password:       config.DBPassword,
		ConnectTimeout: config.DBConnectionTimeout,
		SSLMode:        config.DBSSLMode,
	}
	db, err := postgres.New(&p)
	if err != nil {
		return err
	}
	db.SetMaxOpenConns(config.DBMaxOpenConnections)
	po.db = db
	return nil
}

func (o *PostgresOutput) Run(or OutputRunner, h PluginHelper) (err error) {
	defer o.db.Close()

	o.runner = or
	o.helper = h

	var wg sync.WaitGroup
	wg.Add(1)

	committers := o.makeCommitters(5, &wg)
	go o.receiver(committers, &wg)

	wg.Wait()
	return
}

// Runs in a separate goroutine, accepting incoming messages, buffering output
// data until the ticker triggers the buffered data should be put onto the
// committer channel.
func (o *PostgresOutput) receiver(committers chan<- [][]interface{}, wg *sync.WaitGroup) {
	var pack *PipelinePack

	ticker := time.Tick(time.Duration(o.flushInterval) * time.Millisecond)
	batch := [][]interface{}{}

	for ok := true; ok; {
		select {
		case pack, ok = <-o.runner.InChan():
			if !ok {
				// Closed inChan => we're shutting down, flush data
				committers <- batch
				close(committers)
				break
			}

			// Read values from message fields
			vals, err := o.convertMessageToValues(pack.Message, o.insertMessageFields)

			o.lastMsgLoopCount = pack.MsgLoopCount // here to help prevent infinite error loops
			pack.Recycle()

			if err != nil {
				o.logError(err)
			} else {
				batch = append(batch, vals)
				if len(batch) >= o.flushCount {
					committers <- batch
					batch = [][]interface{}{}
				}
			}
		case <-ticker:
			committers <- batch
			batch = [][]interface{}{}
		}
	}
	wg.Done()
}

// Runs in a separate goroutine, waits for buffered data on the committer
// channel, bulk inserts it into Postgres, and puts the now empty buffer on the
// return channel for reuse.
func (o *PostgresOutput) makeCommitters(count int, wg *sync.WaitGroup) chan<- [][]interface{} {
	batches := make(chan [][]interface{})

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(i int) {
			for batch := range batches {
				if len(batch) <= 0 {
					continue
				}

				done := o.commit(batch)
				timeout := time.NewTimer(time.Minute)

				select {
				case <-done:
					timeout.Stop()
				case <-timeout.C:
					o.logError(errors.New("Postgres insert took more than 60s."))
				}
			}
			wg.Done()
		}(i)
	}

	return batches
}

func (o *PostgresOutput) commit(batch [][]interface{}) <-chan struct{} {
	done := make(chan struct{})

	go func() {
		err := o.db.Insert(o.insertSchema, o.insertTable, o.insertTableColumns, batch)
		if err != nil {
			o.logError(err)
		}
		done <- struct{}{}
	}()

	return done
}

// convertMessageToValue reads a Heka Message and returns a slice of field values
func (po *PostgresOutput) convertMessageToValues(m *message.Message, insertFields []string) (fieldValues []interface{}, err error) {
	fieldValues = []interface{}{}
	missingFields := []string{}
	for _, field := range insertFields {
		// Special case: get "Timestamp" from Heka message
		if field == "Timestamp" {
			// Convert Heka time (Unix timestamp in nanoseconds) to Golang time
			v := time.Unix(0, m.GetTimestamp())
			fieldValues = append(fieldValues, v)
		} else {
			v, ok := m.GetFieldValue(field)
			if !ok {
				// If configured to do so, write NULL when a FieldValue isn't found in the Heka message
				if po.allowMissingMessageFields {
					v = nil
				} else {
					missingFields = append(missingFields, field)
					continue
				}
			}
			fieldValues = append(fieldValues, v)
		}
	}

	if len(missingFields) > 0 {
		return []interface{}{}, fmt.Errorf("message is missing expected fields: %s", strings.Join(missingFields, ", "))
	}

	return fieldValues, nil
}

// Injects a pack with an error message back into the heka pipeline so it can be alerted on
func (po *PostgresOutput) logError(err error) {
	pack := po.helper.PipelinePack(po.lastMsgLoopCount)
	if pack == nil {
		err = fmt.Errorf(
			"system.postgres-output exceeded MaxMsgLoops = %d, err: %s",
			po.lastMsgLoopCount, err.Error(),
		)
		po.runner.LogError(err)
		return
	}
	pack.Message.SetLogger(po.runner.Name())
	pack.Message.SetType("system.postgres-output")
	pack.Message.SetPayload(err.Error())
	go func() { po.helper.PipelineConfig().Router().InChan() <- pack }() // Don't block sending
}

func init() {
	RegisterPlugin("PostgresOutput", func() interface{} {
		return new(PostgresOutput)
	})
}
<commit_msg>Got rid of empty newlines to make formatting consistent.<commit_after>package heka_clever_plugins

import (
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/Clever/heka-clever-plugins/postgres"
	_ "github.com/lib/pq"
	"github.com/mozilla-services/heka/message"
	. "github.com/mozilla-services/heka/pipeline"
)

type PostgresOutput struct {
	db                        *postgres.PostgresDB
	helper                    PluginHelper
	runner                    OutputRunner
	lastMsgLoopCount          uint
	insertSchema              string
	insertTable               string
	insertMessageFields       []string
	insertTableColumns        []string
	flushInterval             uint32
	flushCount                int // Max messages before flush
	allowMissingMessageFields bool
}

type PostgresOutputConfig struct {
	// Table name and colums. Message fields to write.
	InsertSchema        string `toml:"insert_schema"`
	InsertTable         string `toml:"insert_table"`
	InsertTableColumns  string `toml:"insert_table_columns"`
	InsertMessageFields string `toml:"insert_message_fields"`
	// If a field is missing in the Heka message, allow writing NULL
	AllowMissingMessageFields bool `toml:"allow_missing_message_fields"`

	// Database Connection
	DBHost               string `toml:"db_host"`
	DBPort               int    `toml:"db_port"`
	DBName               string `toml:"db_name"`
	DBUser               string `toml:"db_user"`
	DBPassword           string `toml:"db_password"`
	DBConnectionTimeout  int    `toml:"db_connection_timeout"`
	DBMaxOpenConnections int    `toml:"db_max_open_connections"`
	DBSSLMode            string `toml:"db_ssl_mode"`

	// Interval at which accumulated messages should be written to Postgres,
	// in milliseconds (default 1000, i.e. 1 second)
	FlushInterval uint32 `toml:"flush_interval"`
	// Number of messages that triggers a write to Postgres (default 10000)
	FlushCount int `toml:"flush_count"`
}

func (po *PostgresOutput) ConfigStruct() interface{} {
	return &PostgresOutputConfig{
		AllowMissingMessageFields: true,
		DBConnectionTimeout:       5,
		DBMaxOpenConnections:      10,
		DBSSLMode:                 "require",
		FlushInterval:             uint32(1000),
		FlushCount:                10000,
		InsertSchema:              "public",
	}
}

func (po *PostgresOutput) Init(rawConf interface{}) error {
	config := rawConf.(*PostgresOutputConfig)
	po.flushInterval = config.FlushInterval
	po.flushCount = config.FlushCount
	po.insertSchema = config.InsertSchema
	po.insertTable = config.InsertTable
	if config.InsertMessageFields == "" {
		return fmt.Errorf("config item 'insert_message_fields' cannot be empty string")
	}
	po.insertMessageFields = strings.Split(config.InsertMessageFields, " ")
	if config.InsertTableColumns == "" {
		return fmt.Errorf("config item 'insert_table_columns' cannot be empty string")
	}
	po.insertTableColumns = strings.Split(config.InsertTableColumns, " ")
	po.allowMissingMessageFields = config.AllowMissingMessageFields
	p := postgres.DBConnectionParams{
		Host:           config.DBHost,
		Port:           config.DBPort,
		DBName:         config.DBName,
		User:           config.DBUser,
		Password:       config.DBPassword,
		ConnectTimeout: config.DBConnectionTimeout,
		SSLMode:        config.DBSSLMode,
	}
	db, err := postgres.New(&p)
	if err != nil {
		return err
	}
	db.SetMaxOpenConns(config.DBMaxOpenConnections)
	po.db = db
	return nil
}

func (o *PostgresOutput) Run(or OutputRunner, h PluginHelper) (err error) {
	defer o.db.Close()

	o.runner = or
	o.helper = h

	var wg sync.WaitGroup
	wg.Add(1)

	committers := o.makeCommitters(5, &wg)
	go o.receiver(committers, &wg)

	wg.Wait()
	return
}

// Runs in a separate goroutine, accepting incoming messages, buffering output
// data until the ticker triggers the buffered data should be put onto the
// committer channel.
func (o *PostgresOutput) receiver(committers chan<- [][]interface{}, wg *sync.WaitGroup) {
	var pack *PipelinePack

	ticker := time.Tick(time.Duration(o.flushInterval) * time.Millisecond)
	batch := [][]interface{}{}

	for ok := true; ok; {
		select {
		case pack, ok = <-o.runner.InChan():
			if !ok {
				// Closed inChan => we're shutting down, flush data
				committers <- batch
				close(committers)
				break
			}

			// Read values from message fields
			vals, err := o.convertMessageToValues(pack.Message, o.insertMessageFields)

			o.lastMsgLoopCount = pack.MsgLoopCount // here to help prevent infinite error loops
			pack.Recycle()

			if err != nil {
				o.logError(err)
			} else {
				batch = append(batch, vals)
				if len(batch) >= o.flushCount {
					committers <- batch
					batch = [][]interface{}{}
				}
			}
		case <-ticker:
			committers <- batch
			batch = [][]interface{}{}
		}
	}
	wg.Done()
}

// Runs in a separate goroutine, waits for buffered data on the committer
// channel, bulk inserts it into Postgres, and puts the now empty buffer on the
// return channel for reuse.
func (o *PostgresOutput) makeCommitters(count int, wg *sync.WaitGroup) chan<- [][]interface{} {
	batches := make(chan [][]interface{})

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(i int) {
			for batch := range batches {
				if len(batch) <= 0 {
					continue
				}

				done := o.commit(batch)
				timeout := time.NewTimer(time.Minute)

				select {
				case <-done:
					timeout.Stop()
				case <-timeout.C:
					o.logError(errors.New("Postgres insert took more than 60s."))
				}
			}
			wg.Done()
		}(i)
	}

	return batches
}

func (o *PostgresOutput) commit(batch [][]interface{}) <-chan struct{} {
	done := make(chan struct{})

	go func() {
		err := o.db.Insert(o.insertSchema, o.insertTable, o.insertTableColumns, batch)
		if err != nil {
			o.logError(err)
		}
		done <- struct{}{}
	}()

	return done
}

// convertMessageToValue reads a Heka Message and returns a slice of field values
func (po *PostgresOutput) convertMessageToValues(m *message.Message, insertFields []string) (fieldValues []interface{}, err error) {
	fieldValues = []interface{}{}
	missingFields := []string{}
	for _, field := range insertFields {
		// Special case: get "Timestamp" from Heka message
		if field == "Timestamp" {
			// Convert Heka time (Unix timestamp in nanoseconds) to Golang time
			v := time.Unix(0, m.GetTimestamp())
			fieldValues = append(fieldValues, v)
		} else {
			v, ok := m.GetFieldValue(field)
			if !ok {
				// If configured to do so, write NULL when a FieldValue isn't found in the Heka message
				if po.allowMissingMessageFields {
					v = nil
				} else {
					missingFields = append(missingFields, field)
					continue
				}
			}
			fieldValues = append(fieldValues, v)
		}
	}

	if len(missingFields) > 0 {
		return []interface{}{}, fmt.Errorf("message is missing expected fields: %s", strings.Join(missingFields, ", "))
	}

	return fieldValues, nil
}

// Injects a pack with an error message back into the heka pipeline so it can be alerted on
func (po *PostgresOutput) logError(err error) {
	pack := po.helper.PipelinePack(po.lastMsgLoopCount)
	if pack == nil {
		err = fmt.Errorf(
			"system.postgres-output exceeded MaxMsgLoops = %d, err: %s",
			po.lastMsgLoopCount, err.Error(),
		)
		po.runner.LogError(err)
		return
	}
	pack.Message.SetLogger(po.runner.Name())
	pack.Message.SetType("system.postgres-output")
	pack.Message.SetPayload(err.Error())
	go func() { po.helper.PipelineConfig().Router().InChan() <- pack }() // Don't block sending
}

func init() {
	RegisterPlugin("PostgresOutput", func() interface{} {
		return new(PostgresOutput)
	})
}
<|endoftext|>


<commit_before>/* Go (cgo) interface to libgeoip */
package geoip

/*
#cgo CFLAGS: -I/opt/local/include -I/usr/local/include -I/usr/include
#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib -L/usr/lib
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>

//typedef GeoIP* GeoIP_pnt
*/
import "C"

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"sync"
	"unsafe"
)

type GeoIP struct {
	db *C.GeoIP
	mu sync.Mutex
}

func (gi *GeoIP) Free() {
	if gi == nil {
		return
	}
	if gi.db == nil {
		gi = nil
		return
	}
	C.GeoIP_delete(gi.db)
	gi = nil
	return
}

// Open opens a GeoIP database, all formats supported by libgeoip are supported though
// there are only functions to access the country information in this API.
// The database is opened in MEMORY_CACHE mode, if you need to optimize for memory
// instead of performance you should change this.
// If you don't pass a filename, it will try opening the database from
// a list of common paths.
func Open(files ...string) (*GeoIP, error) {
	if len(files) == 0 {
		files = []string{
			"/usr/share/GeoIP/GeoIP.dat",       // Linux default
			"/usr/share/local/GeoIP/GeoIP.dat", // source install?
			"/usr/local/share/GeoIP/GeoIP.dat", // FreeBSD
			"/opt/local/share/GeoIP/GeoIP.dat", // MacPorts
			"/usr/share/GeoIP/GeoIP.dat",       // ArchLinux
		}
	}

	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).Free)

	var err error

	for _, file := range files {

		// libgeoip prints errors if it can't open the file, so check first
		if _, err := os.Stat(file); err != nil {
			if os.IsExist(err) {
				log.Println(err)
			}
			continue
		}

		cbase := C.CString(file)
		defer C.free(unsafe.Pointer(cbase))

		g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE)
		if g.db != nil && err != nil {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%s): %s", files, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%s)", files)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)
	return g, nil
}

// GetOrg takes an IPv4 address string and returns the org name for that IP.
// Requires the geoip org database
func (gi *GeoIP) GetOrg(ip string) string {
	if gi.db == nil {
		return ""
	}
	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr(gi.db, cip)
	if cname != nil {
		rets := C.GoString(cname)
		return rets
	}
	return ""
}

// GetCountry takes an IPv4 address string and returns the country code for that IP
// and the netmask for that IP range.
func (gi *GeoIP) GetCountry(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock() // Lock to make sure we get the right result from GeoIP_last_netmask
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr(gi.db, cip)

	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// GetCountry_v6 works the same as GetCountry except for IPv6 addresses, be sure to
// load a database with IPv6 data to get any results. Getting the netmask for IPv6
// addresses is not supported by the geoip library.
func (gi *GeoIP) GetCountry_v6(ip string) (cc string) {
	if gi.db == nil {
		return
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr_v6(gi.db, cip)
	if ccountry != nil {
		cc = C.GoString(ccountry)
		return
	}
	return
}
<commit_msg>Make Free private<commit_after>/* Go (cgo) interface to libgeoip */
package geoip

/*
#cgo CFLAGS: -I/opt/local/include -I/usr/local/include -I/usr/include
#cgo LDFLAGS: -lGeoIP -L/opt/local/lib -L/usr/local/lib -L/usr/lib
#include <stdio.h>
#include <errno.h>
#include <GeoIP.h>

//typedef GeoIP* GeoIP_pnt
*/
import "C"

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"sync"
	"unsafe"
)

type GeoIP struct {
	db *C.GeoIP
	mu sync.Mutex
}

func (gi *GeoIP) free() {
	if gi == nil {
		return
	}
	if gi.db == nil {
		gi = nil
		return
	}
	C.GeoIP_delete(gi.db)
	gi = nil
	return
}

// Open opens a GeoIP database, all formats supported by libgeoip are supported though
// there are only functions to access the country information in this API.
// The database is opened in MEMORY_CACHE mode, if you need to optimize for memory
// instead of performance you should change this.
// If you don't pass a filename, it will try opening the database from
// a list of common paths.
func Open(files ...string) (*GeoIP, error) {
	if len(files) == 0 {
		files = []string{
			"/usr/share/GeoIP/GeoIP.dat",       // Linux default
			"/usr/share/local/GeoIP/GeoIP.dat", // source install?
			"/usr/local/share/GeoIP/GeoIP.dat", // FreeBSD
			"/opt/local/share/GeoIP/GeoIP.dat", // MacPorts
			"/usr/share/GeoIP/GeoIP.dat",       // ArchLinux
		}
	}

	g := &GeoIP{}
	runtime.SetFinalizer(g, (*GeoIP).free)

	var err error

	for _, file := range files {

		// libgeoip prints errors if it can't open the file, so check first
		if _, err := os.Stat(file); err != nil {
			if os.IsExist(err) {
				log.Println(err)
			}
			continue
		}

		cbase := C.CString(file)
		defer C.free(unsafe.Pointer(cbase))

		g.db, err = C.GeoIP_open(cbase, C.GEOIP_MEMORY_CACHE)
		if g.db != nil && err != nil {
			break
		}
	}
	if err != nil {
		return nil, fmt.Errorf("Error opening GeoIP database (%s): %s", files, err)
	}

	if g.db == nil {
		return nil, fmt.Errorf("Didn't open GeoIP database (%s)", files)
	}

	C.GeoIP_set_charset(g.db, C.GEOIP_CHARSET_UTF8)
	return g, nil
}

// GetOrg takes an IPv4 address string and returns the org name for that IP.
// Requires the geoip org database
func (gi *GeoIP) GetOrg(ip string) string {
	if gi.db == nil {
		return ""
	}
	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	cname := C.GeoIP_name_by_addr(gi.db, cip)
	if cname != nil {
		rets := C.GoString(cname)
		return rets
	}
	return ""
}

// GetCountry takes an IPv4 address string and returns the country code for that IP
// and the netmask for that IP range.
func (gi *GeoIP) GetCountry(ip string) (cc string, netmask int) {
	if gi.db == nil {
		return
	}

	gi.mu.Lock() // Lock to make sure we get the right result from GeoIP_last_netmask
	defer gi.mu.Unlock()

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr(gi.db, cip)

	if ccountry != nil {
		cc = C.GoString(ccountry)
		netmask = int(C.GeoIP_last_netmask(gi.db))
		return
	}
	return
}

// GetCountry_v6 works the same as GetCountry except for IPv6 addresses, be sure to
// load a database with IPv6 data to get any results. Getting the netmask for IPv6
// addresses is not supported by the geoip library.
func (gi *GeoIP) GetCountry_v6(ip string) (cc string) {
	if gi.db == nil {
		return
	}

	cip := C.CString(ip)
	defer C.free(unsafe.Pointer(cip))
	ccountry := C.GeoIP_country_code_by_addr_v6(gi.db, cip)
	if ccountry != nil {
		cc = C.GoString(ccountry)
		return
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 The Authors. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
//
// Test Run: go run gorun.go
// INSTALL: go install
//
// @Author: wangxian
// @Created: 2013-07-31
//
// Project URL: https://github.com/wangxian/gorun
package main

import (
	"flag"
	"github.com/howeyc/fsnotify"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"
	// "code.google.com/p/go.exp/fsnotify"
)

var (
	cmd     *exec.Cmd
	state   sync.Mutex
	appname string

	// fixed: File change soon
	eventTime = make(map[string]time.Time)
)

func Start() {
	if appname != "" {
		log.Println("Start", appname, "...")

		cmd = exec.Command("go", "run", appname)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		go cmd.Run()
	} else {
		Rebuild()
		log.Println("Start project ...")

		curpath, _ := os.Getwd()
		// log.Println(curpath)

		cmd = exec.Command("./" + filepath.Base(curpath))
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		go cmd.Run()
	}
}

func Rebuild() {
	state.Lock()
	defer state.Unlock()

	log.Println("Start rebuild project...")

	bcmd := exec.Command("go", "build")
	bcmd.Stdout = os.Stdout
	bcmd.Stderr = os.Stderr
	err := bcmd.Run()

	if err != nil {
		log.Println("============== Rebuild project failed ==============")
		return
	}
	log.Println("Rebuild project success ...")
}

func Stop() {
	defer func() {
		if e := recover(); e != nil {
			log.Println("Kill process error:", e)
		}
	}()
	if cmd != nil {
		log.Println("Kill running process:", cmd.Process.Pid)
		cmd.Process.Kill()
		// // cmd.Process.Release()

		// st, _ := cmd.Process.Wait()
		// log.Println("Kill process:", st.Exited())
	}
}

func Restart() {
	Stop()
	Start()
}

func Watch() {
	path, _ := os.Getwd()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	// walk dirs
	walkFn := func(path string, info os.FileInfo, err error) error {

		// Not watch dir, has .
		if info.IsDir() && !strings.Contains(path, ".") {
			log.Println("Watch DIR:", path)

			err = watcher.Watch(path)
			if err != nil {
				log.Fatal(err)
			}
		}
		return nil
	}

	if err := filepath.Walk(path, walkFn); err != nil {
		log.Println(err)
	}

	for {
		select {
		case e := <-watcher.Event:

			changed := true
			if t, ok := eventTime[e.String()]; ok {
				if t.Add(time.Millisecond * 2000).After(time.Now()) {
					changed = false
				}
			}
			eventTime[e.String()] = time.Now()

			if changed && strings.Contains(e.Name, ".go") {
				log.Println(e.String())
				Restart()
			}

		case err := <-watcher.Error:
			log.Fatal("Watcher error:", err)
		}
	}
}

func main() {
	flag.Parse()
	appname = flag.Arg(0)
	Start()

	Watch()
}
<commit_msg>wangxian quick commit.<commit_after>// Copyright 2013 The Authors. All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
//
// Test Run: go run gorun.go
// INSTALL: go install
//
// @Author: wangxian
// @Created: 2013-07-31
//
// Project URL: https://github.com/wangxian/gorun
package main

import (
	"flag"
	"github.com/fsnotify/fsnotify"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"
	// "code.google.com/p/go.exp/fsnotify"
)

var (
	cmd     *exec.Cmd
	state   sync.Mutex
	appname string

	// fixed: File change soon
	eventTime = make(map[string]time.Time)
)

func Start() {
	if appname != "" {
		log.Println("Start", appname, "...")

		cmd = exec.Command("go", "run", appname)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		go cmd.Run()
	} else {
		Rebuild()
		log.Println("Start project ...")

		curpath, _ := os.Getwd()
		// log.Println(curpath)

		cmd = exec.Command("./" + filepath.Base(curpath))
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		go cmd.Run()
	}
}

func Rebuild() {
	state.Lock()
	defer state.Unlock()

	log.Println("Start rebuild project...")

	bcmd := exec.Command("go", "build")
	bcmd.Stdout = os.Stdout
	bcmd.Stderr = os.Stderr
	err := bcmd.Run()

	if err != nil {
		log.Println("============== Rebuild project failed ==============")
		return
	}
	log.Println("Rebuild project success ...")
}

func Stop() {
	defer func() {
		if e := recover(); e != nil {
			log.Println("Kill process error:", e)
		}
	}()
	if cmd != nil {
		log.Println("Kill running process:", cmd.Process.Pid)
		cmd.Process.Kill()
		// // cmd.Process.Release()

		// st, _ := cmd.Process.Wait()
		// log.Println("Kill process:", st.Exited())
	}
}

func Restart() {
	Stop()
	Start()
}

func Watch() {
	path, _ := os.Getwd()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	// walk dirs
	walkFn := func(path string, info os.FileInfo, err error) error {

		// Not watch dir, has .
		if info.IsDir() && !strings.Contains(path, ".") {
			log.Println("Watch DIR:", path)

			err = watcher.Add(path)
			if err != nil {
				log.Fatal(err)
			}
		}
		return nil
	}

	if err := filepath.Walk(path, walkFn); err != nil {
		log.Println(err)
	}

	changed := false
	for {
		select {
		case event := <-watcher.Events:

			changed = true
			if t, ok := eventTime[event.Name]; ok {
				if t.Add(time.Millisecond * 3000).After(time.Now()) {
					changed = false
				}
			}
			eventTime[event.Name] = time.Now()

			log.Println("event", event, "changed=", changed)

			if event.Op == fsnotify.Write && strings.Contains(event.Name, ".go") {
				println("---------------------------------------------------")
				Restart()
			}

		case err := <-watcher.Errors:
			log.Println("error:", err)
		}
	}
}

func main() {
	flag.Parse()
	appname = flag.Arg(0)
	Start()

	Watch()
}
<|endoftext|>


<commit_before>package main

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

	"github.com/PuerkitoBio/goquery"
)

var body *goquery.Selection

func main() {
	var doc *goquery.Document

	if len(os.Args) < 2 {
		fmt.Println("Usage: seo [location] [selector]")
		os.Exit(0)
	}

	loc := os.Args[1]
	if strings.HasPrefix(loc, "http") {
		doc, _ = goquery.NewDocument(loc)
	} else {
		b, _ := ioutil.ReadFile(loc)
		doc, _ = goquery.NewDocumentFromReader(bytes.NewReader(b))
	}

	// find article body
	body = doc.Find("body")
	if len(os.Args) > 2 {
		body = doc.Find(os.Args[2])
	}

	var good []string
	var bad []string
	var c int

	c = countHeadings(body)
	if c == 0 {
		bad = append(bad, "The text does not contain any subheadings.")
	}

	if countWords(body.Text()) <= 300 {
		bad = append(bad, "You have far too little content, please add some content to enable a good analysis.")
	} else {
		good = append(good, "The text contains more than 300 words.")
	}

	c = countParagraphsWithWords(body, 150)
	if c >= 1 {
		bad = append(bad, fmt.Sprintf("%d of the paragraphs contains more than the recommended maximum of 150 words.", c))
	} else {
		good = append(good, "None of the paragraphs contain more than 150 words.")
	}

	c = countHeadingsWithWords(body, 300)
	if c > 1 {
		bad = append(bad, fmt.Sprintf("%d of the subheadings is followed by more than 300 words.", c))
	} else {
		good = append(good, "The amount of words following each subheading doesn't exceed 300 words")
	}

	// long sentences
	sentences := countSentences(body.Text())
	longSentences := countSentencesWithWords(body, 20)
	percentage := float32(longSentences) / float32(sentences) * 100
	if percentage > 25 {
		bad = append(bad, fmt.Sprintf("%.1f%% of of the sentences contain more than 20 words.", percentage))
	} else {
		good = append(good, fmt.Sprintf("%.1f%% of of the sentences contain more than 20 words.", percentage))
	}

	// kincaid
	kc := calculateKincaid(body.Text())
	if kc < 60 {
		bad = append(bad, fmt.Sprintf("The copy scores %1.f in the Flesch Reading Ease test.", kc))
	} else {
		good = append(good, fmt.Sprintf("The copy scores %1.f in the Flesch Reading Ease test.", kc))
	}

	fmt.Printf("Analysing \u001B[4m%s\u001B[24m\n", loc)
	for _, l := range good {
		fmt.Printf("\u001B[32m+\u001B[39m %s\n", l)
	}
	for _, l := range bad {
		fmt.Printf("\u001B[31m+\u001B[39m %s\n", l)
	}

}
<commit_msg>simplify result list<commit_after>package main

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

	"github.com/PuerkitoBio/goquery"
)

var body *goquery.Selection
var results []result

type result struct {
	text string
	good bool
}

func main() {
	var doc *goquery.Document

	if len(os.Args) < 2 {
		fmt.Println("Usage: seo [location] [selector]")
		os.Exit(0)
	}

	loc := os.Args[1]
	if strings.HasPrefix(loc, "http") {
		doc, _ = goquery.NewDocument(loc)
	} else {
		b, _ := ioutil.ReadFile(loc)
		doc, _ = goquery.NewDocumentFromReader(bytes.NewReader(b))
	}

	// find article body
	body = doc.Find("body")
	if len(os.Args) > 2 {
		body = doc.Find(os.Args[2])
	}

	var c int

	c = countWords(body.Text())
	addResult(fmt.Sprintf("The text contains %d words.", c), c >= 300)

	c = countHeadings(body)
	addResult(fmt.Sprintf("The text contains %d subheadings.", c), c > 0)

	c = countHeadingsWithWords(body, 300)
	addResult(fmt.Sprintf("%d of the subheadings is followed by more than 300 words.", c), c <= 1)

	c = countParagraphsWithWords(body, 150)
	addResult(fmt.Sprintf("%d of the paragraphs contains more than 150 words.", c), c < 1)

	// long sentences
	sentences := countSentences(body.Text())
	longSentences := countSentencesWithWords(body, 20)
	percentage := float32(longSentences) / float32(sentences) * 100
	addResult(fmt.Sprintf("%.1f%% of of the sentences contain more than 20 words.", percentage), percentage <= 25)

	// kincaid
	kc := calculateKincaid(body.Text())
	addResult(fmt.Sprintf("The copy scores %1.f in the Flesch Reading Ease test.", kc), kc >= 60)

	fmt.Printf("Analysing \u001B[4m%s\u001B[24m\n", loc)
	for _, r := range results {
		if r.good {
			fmt.Printf("\u001B[32m+\u001B[39m %s\n", r.text)
		} else {
			fmt.Printf("\u001B[31m-\u001B[39m %s\n", r.text)
		}

	}
}

func addResult(t string, g bool) {
	results = append(results, result{text: t, good: g})
}
<|endoftext|>


<commit_before>package graph

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/tidwall/gjson"
)

func issuesToBlocksGraph(issues []issue) map[string][]string {
	blocksGraph := map[string][]string{}
	for _, iss := range issues {
		for _, blockedBy := range iss.blockedByKeys {
			blocksGraph[blockedBy] = append(blocksGraph[blockedBy], iss.Key)
		}

		_, exists := blocksGraph[iss.Key]
		if !exists {
			blocksGraph[iss.Key] = []string{}
		}
	}
	return blocksGraph
}

type errBadStatus struct {
	statusCode int
}

func (e errBadStatus) Error() string {
	return fmt.Sprintf("code: %d", e.statusCode)
}

func getSingleIssue(jc jiraClient, key string) (issue, error) {
	q := url.Values{"fields": jc.getRequestFields()}
	resp, err := jc.Get(fmt.Sprintf("/rest/api/2/issue/%s", key), q)
	if err != nil {
		return issue{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return issue{}, errBadStatus{resp.StatusCode}
	}

	resultBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return issue{}, err
	}
	parsed := gjson.ParseBytes(resultBytes)
	return jc.unmarshallIssue(parsed), nil
}

func getIssues(jc jiraClient, epicKey string) ([]issue, error) {
	jql := fmt.Sprintf(`"Epic Link" = %s`, epicKey)

	result := []issue{}
	for {
		b, err := jc.Search(jql, jc.getRequestFields(), len(result))
		if err != nil {
			return nil, err
		}
		parsed := gjson.ParseBytes(b)

		for _, parsedIssue := range parsed.Get("issues").Array() {
			iss := jc.unmarshallIssue(parsedIssue)

			parsedBlocks := parsedIssue.Get(`fields.issuelinks.#[type.name=="Blocks"]#.inwardIssue.key`).Array()
			iss.blockedByKeys = make([]string, len(parsedBlocks))
			for i := range parsedBlocks {
				iss.blockedByKeys[i] = parsedBlocks[i].String()
			}

			result = append(result, iss)
		}

		total := parsed.Get("total").Int()
		if len(result) >= int(total) {
			break
		}
	}

	return result, nil
}
<commit_msg>Retrieve an epic's color code<commit_after>package graph

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/tidwall/gjson"
)

func issuesToBlocksGraph(issues []issue) map[string][]string {
	blocksGraph := map[string][]string{}
	for _, iss := range issues {
		for _, blockedBy := range iss.blockedByKeys {
			blocksGraph[blockedBy] = append(blocksGraph[blockedBy], iss.Key)
		}

		_, exists := blocksGraph[iss.Key]
		if !exists {
			blocksGraph[iss.Key] = []string{}
		}
	}
	return blocksGraph
}

type errBadStatus struct {
	statusCode int
}

func (e errBadStatus) Error() string {
	return fmt.Sprintf("code: %d", e.statusCode)
}

func getSingleIssue(jc jiraClient, key string) (issue, error) {
	q := url.Values{"fields": jc.getRequestFields()}
	resp, err := jc.Get(fmt.Sprintf("/rest/api/2/issue/%s", key), q)
	if err != nil {
		return issue{}, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return issue{}, errBadStatus{resp.StatusCode}
	}

	resultBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return issue{}, err
	}
	parsed := gjson.ParseBytes(resultBytes)
	return jc.unmarshallIssue(parsed), nil
}

func getEpicColorCode(jc jiraClient, key string) (string, error) {
	resp, err := jc.Get(fmt.Sprintf("/rest/agile/1.0/epic/%s", key), url.Values{})
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	resultBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	parsed := gjson.ParseBytes(resultBytes)

	color := parsed.Get("color.key").String()
	return color, nil
}

func getIssues(jc jiraClient, epicKey string) ([]issue, error) {
	jql := fmt.Sprintf(`"Epic Link" = %s`, epicKey)

	result := []issue{}
	for {
		b, err := jc.Search(jql, jc.getRequestFields(), len(result))
		if err != nil {
			return nil, err
		}
		parsed := gjson.ParseBytes(b)

		for _, parsedIssue := range parsed.Get("issues").Array() {
			iss := jc.unmarshallIssue(parsedIssue)

			parsedBlocks := parsedIssue.Get(`fields.issuelinks.#[type.name=="Blocks"]#.inwardIssue.key`).Array()
			iss.blockedByKeys = make([]string, len(parsedBlocks))
			for i := range parsedBlocks {
				iss.blockedByKeys[i] = parsedBlocks[i].String()
			}

			result = append(result, iss)
		}

		total := parsed.Get("total").Int()
		if len(result) >= int(total) {
			break
		}
	}

	return result, nil
}
<|endoftext|>