<commit_before>package sqlpaxos

import "net"
import "fmt"
import "net/rpc"
import "log"
import "time"
import "paxos"
import "sync"
import "os"
import "syscall"
import "encoding/gob"
import "math/rand"
import "strconv"
import "math"
import "barista"
import "encoding/json"
import "logger"
import "db"

const Debug=0

func DPrintf(format string, a ...interface{}) (n int, err error) {
        if Debug > 0 {
                log.Printf(format, a...)
        }
        return
}

type LastSeen struct {
  RequestId int
  Reply interface{}
}

type SQLPaxos struct {
  mu sync.Mutex
  l net.Listener
  me int
  dead bool // for testing
  unreliable bool // for testing
  px *paxos.Paxos

  // Your definitions here.
  ops map[int]Op // log of operations
  replies map[int]interface{} // the replies for this sequence number
  done map[int]bool // true if we can delete the data for this sequence number
  data map[string]string // the database
  lastSeen map[int64]LastSeen // the last request/reply for this client
  connections map[int64]*db.DBManager // connections per client. Limited to a single connection per client
  next int // the next sequence number to be executed
  logger *logger.Logger // logger to write paxos log to file
}

func (sp *SQLPaxos) execute(op Op) interface{} {

  testing := false
  if testing {
    args := op.Args
    reply := ExecReply{}

    // @TODO remove this
    if op.NoOp {
       return reply
    }

    // @TODO remove get & put
    key := args.(ExecArgs).Key
    if args.(ExecArgs).Type == Put {
       // execute the put

       prevValue, ok := sp.data[key]
       if ok {
          reply.Value = prevValue
       } else {
          reply.Value = ""
       }

       if args.(ExecArgs).DoHash {
          sp.data[key] = strconv.Itoa(int(hash(reply.Value + args.(ExecArgs).Value)))
       } else {
          sp.data[key] = args.(ExecArgs).Value
       }

       reply.Err = OK

    } else if args.(ExecArgs).Type == Get {
       // execute the get

       value, ok := sp.data[key]
       if ok {
          reply.Value = value
          reply.Err = OK
       } else {
          reply.Value = ""
          reply.Err = ErrNoKey
       }
    }

    return reply

  } else {
    // not testing

    // write op to file
    err := sp.WriteToLog(op)
    if err != nil {
      // log something
    }

    switch {
    case op.Type == Open:
      return sp.OpenHelper(op.Args.(OpenArgs), op.SeqNum)
    case op.Type == Close:
      return sp.CloseHelper(op.Args.(CloseArgs), op.SeqNum)
    case op.Type == Execute:
      return sp.ExecuteHelper(op.Args.(ExecArgs), op.SeqNum)
    }
  }
  return nil
}

func (sp *SQLPaxos) WriteToLog(op Op) error {
  b, err := json.Marshal(op)
  if err != nil {
    return err
  }
  return sp.logger.WriteToLog(b)
}

func (sp *SQLPaxos) ExecuteHelper(args ExecArgs, seqnum int) ExecReply {
  rows, columns, err := sp.UpdateDatabase(args.ClientId, args.Query, args.QueryParams, seqnum)
  if err != OK {
    // log something
    return ExecReply{Err:err}
  }

  tuples := []*barista.Tuple{}
  for _, row := range rows {
    tuple := barista.Tuple{Cells: &row}
    tuples = append(tuples, &tuple)
  }

  result_set := new(barista.ResultSet)
  //result_set.Con = con. @TODO: this will not be populating this
  result_set.Tuples = &tuples
  result_set.FieldNames = &columns
  return ExecReply{Result:result_set, Err:OK}
}

func (sp *SQLPaxos) OpenHelper(args OpenArgs, seqnum int) OpenReply {
  reply := OpenReply{}
  _, ok := sp.connections[args.ClientId]
  if ok {
    reply.Err = ConnAlreadyOpen
  } else {
      manager := new(db.DBManager)
      reply.Err = errorToErr(manager.OpenConnection(args.User, args.Password, args.Database))
      sp.connections[args.ClientId] = manager
  }
  _, _, err := sp.UpdateDatabase(args.ClientId, "", nil, seqnum)
  if err != OK {
    // log something
  }
  return reply
}

func errorToErr(error error) Err {
  if error != nil {
    return Err(error.Error())
  } else {
    return OK
  }
}

func (sp *SQLPaxos) CloseHelper(args CloseArgs, seqnum int) CloseReply {
  _, _, err := sp.UpdateDatabase(args.ClientId, "", nil, seqnum)
  reply := CloseReply{}
  _, ok := sp.connections[args.ClientId]
  if !ok {
    reply.Err = ConnAlreadyClosed
  } else {
    reply.Err = errorToErr(sp.connections[args.ClientId].CloseConnection())
    delete(sp.connections, args.ClientId) //only delete on successful close?
  }
  if err != OK {
    // log something
  }
  return reply
}

func (sp *SQLPaxos) convertQueryParams(query_params [][]byte) []interface{} {
  params := make([]interface{}, len(query_params))
  for i, param := range query_params {
     params[i] = param
  }
  return params
}

// note that NoOps don't update the state table
func (sp *SQLPaxos) UpdateDatabase(clientId int64, query string, query_params [][]byte, seqnum int) ([][][]byte, []string, Err) {
  tx, err := sp.connections[clientId].BeginTxn()

  rows := make([][][]byte, 0)
  columns := make([]string, 0)

  if err != nil || tx == nil {
     return rows, columns, errorToErr(err)
  }

  if query != "" {
     params := sp.convertQueryParams(query_params)
     rows, columns, err = sp.connections[clientId].QueryTxn(tx, query, params...)
  }

  update := "UPDATE SQLPaxosLog SET lastSeqNum=" + strconv.Itoa(seqnum) + ";"
  _, errUpdate := sp.connections[clientId].ExecTxn(tx, update, nil)
  if errUpdate != nil {
     fmt.Println("Error updating SQLPaxosLog: ", errUpdate)
  }

  errEnd := sp.connections[clientId].EndTxn(tx)
  if errEnd != nil {
     fmt.Println("Error committing txn: ", errEnd)
  }

  return rows, columns, errorToErr(err)
}

func (sp *SQLPaxos) fillHoles(next int, seq int) interface{} {

  var reply interface{}

  // make sure there are no holes in the log before our operation
  for i := next; i <= seq; i++ {
     nwaits := 0
     for !sp.dead {
	if _, ok := sp.ops[i]; ok || sp.next > i {
      	   break
        }

        decided, v_i := sp.px.Status(i)
        if decided {
           // the operation in slot i has been decided
           sp.ops[i] = v_i.(Op)
           break
        } else {
           nwaits++
           sp.mu.Unlock()
           if nwaits == 5 || nwaits == 10 {
              // propose a no-op
              sp.px.Start(i, Op{NoOp: true})
           } else if nwaits > 10 {
              time.Sleep(100 * time.Millisecond)
           } else {
              time.Sleep(10 * time.Millisecond)
           }
           sp.mu.Lock()
        }
     }

     if i == sp.next {
        // the operation at slot i is next to be executed
	r, executed := sp.checkIfExecuted(sp.ops[i])
        if executed {
    	   sp.replies[i] = r
	} else {
	   r := sp.execute(sp.ops[i])
	   sp.replies[i] = r
	   sp.lastSeen[getOpClientId(sp.ops[i])] = LastSeen{ RequestId: getOpRequestId(sp.ops[i]), Reply: r }
	}
        sp.next++
     }

     if i == seq {
        reply = sp.replies[i]
     }
  }

  return reply
}

func getOpClientId(op Op) int64 {
  switch {
  case op.Type == Open:
    return op.Args.(OpenArgs).ClientId;
  case op.Type == Close:
    return op.Args.(CloseArgs).ClientId;
  case op.Type == Execute:
    return op.Args.(ExecArgs).ClientId;
  }
  return -1;
}

func getOpRequestId(op Op) int {
  switch {
  case op.Type == Open:
    return op.Args.(OpenArgs).RequestId;
  case op.Type == Close:
    return op.Args.(CloseArgs).RequestId;
  case op.Type == Execute:
    return op.Args.(ExecArgs).RequestId;
  }
  return -1;
}

// @TODO: update to support multiple types of operations
func (sp *SQLPaxos) checkIfExecuted(op Op) (interface{}, bool) {
  // need some casting here
  lastSeen, ok := sp.lastSeen[getOpClientId(op)]
  if ok {
     if lastSeen.RequestId == getOpRequestId(op) {
        return lastSeen.Reply, true
     } else if lastSeen.RequestId > getOpRequestId(op) {
        return nil, true // empty reply since this is an old request
     }
  }

  return nil, false
}

func (sp *SQLPaxos) reserveSlot(op Op) int {

  // propose this operation for slot seq
  seq := sp.px.Max() + 1
  v := op
  sp.px.Start(seq, v)

  nwaits := 0
  for !sp.dead {
     decided, v_a := sp.px.Status(seq)
     if decided && v_a != nil && getOpClientId(v_a.(Op)) == getOpClientId(v) &&
       getOpRequestId(v_a.(Op)) == getOpRequestId(v) {
        // we successfully claimed this slot for our operation
        if _, ok := sp.ops[seq]; !ok {
	   v.SeqNum = seq
           sp.ops[seq] = v
        }
        break
     } else if decided {
        // another proposer got this slot, so try to get our operation in a new slot
        seq = int(math.Max(float64(sp.px.Max() + 1), float64(seq + 1)))
        sp.px.Start(seq, v)
        nwaits = 0
     } else {
        nwaits++
  	sp.mu.Unlock()
        if nwaits == 5 || nwaits == 10 {
           // re-propose our operation
           sp.px.Start(seq, v)
     	} else if nwaits > 10 {
           time.Sleep(100 * time.Millisecond)
        } else {
           time.Sleep(10 * time.Millisecond)
     	}
  	sp.mu.Lock()
     }
  }
  v.SeqNum = seq // update sequence number
  return seq
}

func (sp *SQLPaxos) freeMemory(seq int) {

  sp.done[seq] = true
  minNotDone := seq + 1
  for i := seq; i >= 0; i-- {
     _, ok := sp.ops[i]
     if ok {
        if done, ok := sp.done[i]; ok && done || sp.ops[i].NoOp {
           delete(sp.ops, i)
           delete(sp.replies, i)
           delete(sp.done, i)
        } else {
           minNotDone = i
        }
     }
  }

  sp.px.Done(minNotDone - 1)
}

//@Make it work for multiple types of arguments
func (sp *SQLPaxos) commit(op Op) interface{} {

  sp.mu.Lock()
  defer sp.mu.Unlock()

  // first check if this request has already been executed
  reply, ok := sp.checkIfExecuted(op)
  if ok {
     return reply
  }

  // reserve a slot in the paxos log for this operation
  seq := sp.reserveSlot(op)

  next := sp.next
  if next > seq {
     // our operation has already been executed
     reply = sp.replies[seq]
  } else {
     // fill holes in the log and execute our operation
     reply = sp.fillHoles(next, seq)
  }

  // delete un-needed log entries to free up memory
  sp.freeMemory(seq)

  return reply
}

func (sp *SQLPaxos) ExecuteSQL(args *ExecArgs, reply *ExecReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Execute, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Result = r.(ExecReply).Result
     reply.Value = r.(ExecReply).Value
     reply.Err = r.(ExecReply).Err
  }

  return nil
}

// open the connection to the database
func (sp *SQLPaxos) Open(args *OpenArgs, reply *OpenReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Open, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Err = r.(OpenReply).Err
  }

  return nil
}

// close the connection to the database
func (sp *SQLPaxos) Close(args *CloseArgs, reply *CloseReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Close, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Err = r.(CloseReply).Err
  }

  return nil
}

// tell the server to shut itself down.
func (sp *SQLPaxos) kill() {
  sp.dead = true
  sp.l.Close()
  sp.px.Kill()
}

//
// servers[] contains the ports of the set of
// servers that will cooperate via Paxos to
// form the fault-tolerant key/value service.
// me is the index of the current server in servers[].
//
func StartServer(servers []string, me int) *SQLPaxos {
  // call gob.Register on structures you want
  // Go's RPC library to marshall/unmarshall.
  gob.Register(Op{})
  gob.Register(ExecArgs{})

  sp := new(SQLPaxos)
  sp.me = me

  // Your initialization code here.
  sp.ops = make(map[int]Op)
  sp.data = make(map[string]string)
  sp.replies = make(map[int]interface{})
  sp.done = make(map[int]bool)
  sp.lastSeen = make(map[int64]LastSeen)
  sp.next = 0
  sp.connections = make(map[int64]*db.DBManager)
  sp.logger = logger.Make("sqlpaxos_log.txt")

  rpcs := rpc.NewServer()
  rpcs.Register(sp)

  sp.px = paxos.Make(servers, me, rpcs)

  //os.Remove(servers[me]) // only needed for "unix"
  //l, e := net.Listen("unix", servers[me]);
  l, e := net.Listen("tcp", servers[me]);
  if e != nil {
    log.Fatal("listen error: ", e);
  }
  sp.l = l

  // please do not change any of the following code,
  // or do anything to subvert it.

  go func() {
    for sp.dead == false {
      conn, err := sp.l.Accept()
      if err == nil && sp.dead == false {
      if sp.unreliable && (rand.Int63() % 1000) < 100 {
          // discard the request.
          conn.Close()
        } else if sp.unreliable && (rand.Int63() % 1000) < 200 {
          // process the request but force discard of reply.
          //c1 := conn.(*net.UnixConn)
	  c1 := conn.(*net.TCPConn)
          f, _ := c1.File()
          err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
          if err != nil {
            fmt.Printf("shutdown: %v\n", err)
          }
          go rpcs.ServeConn(conn)
        } else {
          go rpcs.ServeConn(conn)
        }
      } else if err == nil {
        conn.Close()
      }
      if err != nil && sp.dead == false {
        fmt.Printf("SQLPaxos(%v) accept: %v\n", me, err.Error())
	sp.kill()
      }
    }
  }()

  return sp
}

<commit_msg>fixes<commit_after>package sqlpaxos

import "net"
import "fmt"
import "net/rpc"
import "log"
import "time"
import "paxos"
import "sync"
import "os"
import "syscall"
import "encoding/gob"
import "math/rand"
import "strconv"
import "math"
import "barista"
import "encoding/json"
import "logger"
import "db"

const Debug=0

func DPrintf(format string, a ...interface{}) (n int, err error) {
        if Debug > 0 {
                log.Printf(format, a...)
        }
        return
}

type LastSeen struct {
  RequestId int
  Reply interface{}
}

type SQLPaxos struct {
  mu sync.Mutex
  l net.Listener
  me int
  dead bool // for testing
  unreliable bool // for testing
  px *paxos.Paxos

  // Your definitions here.
  ops map[int]Op // log of operations
  replies map[int]interface{} // the replies for this sequence number
  done map[int]bool // true if we can delete the data for this sequence number
  data map[string]string // the database
  lastSeen map[int64]LastSeen // the last request/reply for this client
  connections map[int64]*db.DBManager // connections per client. Limited to a single connection per client
  next int // the next sequence number to be executed
  logger *logger.Logger // logger to write paxos log to file
}

func (sp *SQLPaxos) execute(op Op) interface{} {

  testing := false
  if testing {
    args := op.Args
    reply := ExecReply{}

    // @TODO remove this
    if op.NoOp {
       return reply
    }

    // @TODO remove get & put
    key := args.(ExecArgs).Key
    if args.(ExecArgs).Type == Put {
       // execute the put

       prevValue, ok := sp.data[key]
       if ok {
          reply.Value = prevValue
       } else {
          reply.Value = ""
       }

       if args.(ExecArgs).DoHash {
          sp.data[key] = strconv.Itoa(int(hash(reply.Value + args.(ExecArgs).Value)))
       } else {
          sp.data[key] = args.(ExecArgs).Value
       }

       reply.Err = OK

    } else if args.(ExecArgs).Type == Get {
       // execute the get

       value, ok := sp.data[key]
       if ok {
          reply.Value = value
          reply.Err = OK
       } else {
          reply.Value = ""
          reply.Err = ErrNoKey
       }
    }

    return reply

  } else {
    // not testing

    // write op to file
    err := sp.WriteToLog(op)
    if err != nil {
      // log something
    }

    switch {
    case op.Type == Open:
      return sp.OpenHelper(op.Args.(OpenArgs), op.SeqNum)
    case op.Type == Close:
      return sp.CloseHelper(op.Args.(CloseArgs), op.SeqNum)
    case op.Type == Execute:
      return sp.ExecuteHelper(op.Args.(ExecArgs), op.SeqNum)
    }
  }
  return nil
}

func (sp *SQLPaxos) WriteToLog(op Op) error {
  b, err := json.Marshal(op)
  if err != nil {
    return err
  }
  return sp.logger.WriteToLog(b)
}

func (sp *SQLPaxos) ExecuteHelper(args ExecArgs, seqnum int) ExecReply {
  rows, columns, err := sp.UpdateDatabase(args.ClientId, args.Query, args.QueryParams, seqnum)
  if err != OK {
    // log something
    return ExecReply{Err:err}
  }

  tuples := []*barista.Tuple{}
  for _, row := range rows {
    tuple := barista.Tuple{Cells: &row}
    tuples = append(tuples, &tuple)
  }

  result_set := new(barista.ResultSet)
  //result_set.Con = con. @TODO: this will not be populating this
  result_set.Tuples = &tuples
  result_set.FieldNames = &columns
  return ExecReply{Result:result_set, Err:OK}
}

func (sp *SQLPaxos) OpenHelper(args OpenArgs, seqnum int) OpenReply {
  reply := OpenReply{}
  _, ok := sp.connections[args.ClientId]
  if ok {
    reply.Err = ConnAlreadyOpen
  } else {
      manager := new(db.DBManager)
      reply.Err = errorToErr(manager.OpenConnection(args.User, args.Password, args.Database))
      sp.connections[args.ClientId] = manager
  }
  _, _, err := sp.UpdateDatabase(args.ClientId, "", nil, seqnum)
  if err != OK {
    // log something
  }
  return reply
}

func errorToErr(error error) Err {
  if error != nil {
    return Err(error.Error())
  } else {
    return OK
  }
}

func (sp *SQLPaxos) CloseHelper(args CloseArgs, seqnum int) CloseReply {
  _, _, err := sp.UpdateDatabase(args.ClientId, "", nil, seqnum)
  reply := CloseReply{}
  _, ok := sp.connections[args.ClientId]
  if !ok {
    reply.Err = ConnAlreadyClosed
  } else {
    reply.Err = errorToErr(sp.connections[args.ClientId].CloseConnection())
    delete(sp.connections, args.ClientId) //only delete on successful close?
  }
  if err != OK {
    // log something
  }
  return reply
}

func (sp *SQLPaxos) convertQueryParams(query_params [][]byte) []interface{} {
  params := make([]interface{}, len(query_params))
  for i, param := range query_params {
     params[i] = param
  }
  return params
}

// note that NoOps don't update the state table
func (sp *SQLPaxos) UpdateDatabase(clientId int64, query string, query_params [][]byte, seqnum int) ([][][]byte, []string, Err) {
  tx, err := sp.connections[clientId].BeginTxn()

  rows := make([][][]byte, 0)
  columns := make([]string, 0)

  if err != nil || tx == nil {
     return rows, columns, errorToErr(err)
  }

  if query != "" {
     params := sp.convertQueryParams(query_params)
     rows, columns, err = sp.connections[clientId].QueryTxn(tx, query, params...)
  }

  update := "UPDATE SQLPaxosLog SET lastSeqNum=" + strconv.Itoa(seqnum) + ";"
  _, errUpdate := sp.connections[clientId].ExecTxn(tx, update, nil)
  if errUpdate != nil {
     fmt.Println("Error updating SQLPaxosLog: ", errUpdate)
  }

  errEnd := sp.connections[clientId].EndTxn(tx)
  if errEnd != nil {
     fmt.Println("Error committing txn: ", errEnd)
  }

  return rows, columns, errorToErr(err)
}

func (sp *SQLPaxos) fillHoles(next int, seq int) interface{} {

  var reply interface{}

  // make sure there are no holes in the log before our operation
  for i := next; i <= seq; i++ {
     nwaits := 0
     for !sp.dead {
	if _, ok := sp.ops[i]; ok || sp.next > i {
      	   break
        }

        decided, v_i := sp.px.Status(i)
        if decided {
           // the operation in slot i has been decided
           sp.ops[i] = v_i.(Op)
           break
        } else {
           nwaits++
           sp.mu.Unlock()
           if nwaits == 5 || nwaits == 10 {
              // propose a no-op
              sp.px.Start(i, Op{NoOp: true})
           } else if nwaits > 10 {
              time.Sleep(100 * time.Millisecond)
           } else {
              time.Sleep(10 * time.Millisecond)
           }
           sp.mu.Lock()
        }
     }

     if i == sp.next {
        // the operation at slot i is next to be executed
	r, executed := sp.checkIfExecuted(sp.ops[i])
        if executed {
    	   sp.replies[i] = r
	} else {
	   r := sp.execute(sp.ops[i])
	   sp.replies[i] = r
	   sp.lastSeen[getOpClientId(sp.ops[i])] = LastSeen{ RequestId: getOpRequestId(sp.ops[i]), Reply: r }
	}
        sp.next++
     }

     if i == seq {
        reply = sp.replies[i]
     }
  }

  return reply
}

func getOpClientId(op Op) int64 {
  switch {
  case op.Type == Open:
    return op.Args.(OpenArgs).ClientId;
  case op.Type == Close:
    return op.Args.(CloseArgs).ClientId;
  case op.Type == Execute:
    return op.Args.(ExecArgs).ClientId;
  }
  return -1;
}

func getOpRequestId(op Op) int {
  switch {
  case op.Type == Open:
    return op.Args.(OpenArgs).RequestId;
  case op.Type == Close:
    return op.Args.(CloseArgs).RequestId;
  case op.Type == Execute:
    return op.Args.(ExecArgs).RequestId;
  }
  return -1;
}

// @TODO: update to support multiple types of operations
func (sp *SQLPaxos) checkIfExecuted(op Op) (interface{}, bool) {
  // need some casting here
  lastSeen, ok := sp.lastSeen[getOpClientId(op)]
  if ok {
     if lastSeen.RequestId == getOpRequestId(op) {
        return lastSeen.Reply, true
     } else if lastSeen.RequestId > getOpRequestId(op) {
        return nil, true // empty reply since this is an old request
     }
  }

  return nil, false
}

func (sp *SQLPaxos) reserveSlot(op Op) int {

  // propose this operation for slot seq
  seq := sp.px.Max() + 1
  v := op
  sp.px.Start(seq, v)

  nwaits := 0
  for !sp.dead {
     decided, v_a := sp.px.Status(seq)
     if decided && v_a != nil && getOpClientId(v_a.(Op)) == getOpClientId(v) &&
       getOpRequestId(v_a.(Op)) == getOpRequestId(v) {
        // we successfully claimed this slot for our operation
        if _, ok := sp.ops[seq]; !ok {
	   v.SeqNum = seq
           sp.ops[seq] = v
        }
        break
     } else if decided {
        // another proposer got this slot, so try to get our operation in a new slot
        seq = int(math.Max(float64(sp.px.Max() + 1), float64(seq + 1)))
        sp.px.Start(seq, v)
        nwaits = 0
     } else {
        nwaits++
  	sp.mu.Unlock()
        if nwaits == 5 || nwaits == 10 {
           // re-propose our operation
           sp.px.Start(seq, v)
     	} else if nwaits > 10 {
           time.Sleep(100 * time.Millisecond)
        } else {
           time.Sleep(10 * time.Millisecond)
     	}
  	sp.mu.Lock()
     }
  }
  v.SeqNum = seq // update sequence number
  return seq
}

func (sp *SQLPaxos) freeMemory(seq int) {

  sp.done[seq] = true
  minNotDone := seq + 1
  for i := seq; i >= 0; i-- {
     _, ok := sp.ops[i]
     if ok {
        if done, ok := sp.done[i]; ok && done || sp.ops[i].NoOp {
           delete(sp.ops, i)
           delete(sp.replies, i)
           delete(sp.done, i)
        } else {
           minNotDone = i
        }
     }
  }

  sp.px.Done(minNotDone - 1)
}

//@Make it work for multiple types of arguments
func (sp *SQLPaxos) commit(op Op) interface{} {

  sp.mu.Lock()
  defer sp.mu.Unlock()

  // first check if this request has already been executed
  reply, ok := sp.checkIfExecuted(op)
  if ok {
     return reply
  }

  // reserve a slot in the paxos log for this operation
  seq := sp.reserveSlot(op)

  next := sp.next
  if next > seq {
     // our operation has already been executed
     reply = sp.replies[seq]
  } else {
     // fill holes in the log and execute our operation
     reply = sp.fillHoles(next, seq)
  }

  // delete un-needed log entries to free up memory
  sp.freeMemory(seq)

  return reply
}

func (sp *SQLPaxos) ExecuteSQL(args *ExecArgs, reply *ExecReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Execute, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Result = r.(ExecReply).Result
     reply.Value = r.(ExecReply).Value
     reply.Err = r.(ExecReply).Err
  }

  return nil
}

// open the connection to the database
func (sp *SQLPaxos) Open(args *OpenArgs, reply *OpenReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Open, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Err = r.(OpenReply).Err
  }

  return nil
}

// close the connection to the database
func (sp *SQLPaxos) Close(args *CloseArgs, reply *CloseReply) error {
  // execute this operation and store the response in r
  op := Op{Type:Close, Args: *args}
  r := sp.commit(op)

  if r != nil {
     reply.Err = r.(CloseReply).Err
  }

  return nil
}

// tell the server to shut itself down.
func (sp *SQLPaxos) kill() {
  sp.dead = true
  sp.l.Close()
  sp.px.Kill()
}

//
// servers[] contains the ports of the set of
// servers that will cooperate via Paxos to
// form the fault-tolerant key/value service.
// me is the index of the current server in servers[].
//
func StartServer(servers []string, me int) *SQLPaxos {
  // call gob.Register on structures you want
  // Go's RPC library to marshall/unmarshall.
  gob.Register(Op{})
  gob.Register(ExecArgs{})

  sp := new(SQLPaxos)
  sp.me = me

  // Your initialization code here.
  sp.ops = make(map[int]Op)
  sp.data = make(map[string]string)
  sp.replies = make(map[int]interface{})
  sp.done = make(map[int]bool)
  sp.lastSeen = make(map[int64]LastSeen)
  sp.next = 0
  sp.connections = make(map[int64]*db.DBManager)
  sp.logger = logger.Make("sqlpaxos_log.txt")

  rpcs := rpc.NewServer()
  rpcs.Register(sp)

  sp.px = paxos.Make(servers, me, rpcs)

  os.Remove(servers[me]) // only needed for "unix"
  l, e := net.Listen("unix", servers[me]);
  //l, e := net.Listen("tcp", servers[me]);
  if e != nil {
    log.Fatal("listen error: ", e);
  }
  sp.l = l

  // please do not change any of the following code,
  // or do anything to subvert it.

  go func() {
    for sp.dead == false {
      conn, err := sp.l.Accept()
      if err == nil && sp.dead == false {
      if sp.unreliable && (rand.Int63() % 1000) < 100 {
          // discard the request.
          conn.Close()
        } else if sp.unreliable && (rand.Int63() % 1000) < 200 {
          // process the request but force discard of reply.
          c1 := conn.(*net.UnixConn)
	  //c1 := conn.(*net.TCPConn)
          f, _ := c1.File()
          err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR)
          if err != nil {
            fmt.Printf("shutdown: %v\n", err)
          }
          go rpcs.ServeConn(conn)
        } else {
          go rpcs.ServeConn(conn)
        }
      } else if err == nil {
        conn.Close()
      }
      if err != nil && sp.dead == false {
        fmt.Printf("SQLPaxos(%v) accept: %v\n", me, err.Error())
	sp.kill()
      }
    }
  }()

  return sp
}

<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
)

const VERSION = "v0.0.2"

var logger = log.New(os.Stderr, "", 0)

type OriginConfig struct {
	Addr   string
	Direct bool
}

func main() {
	port := flag.Int("p", 0, "")
	ip := flag.String("i", "127.0.0.1", "")
	originPort := flag.Int("P", 0, "")
	originHost := flag.String("H", "", "")
	originDirect := flag.Bool("D", false, "")
	certPath := flag.String("c", "", "")
	keyPath := flag.String("k", "", "")
	logFormat := flag.String("l", "default", "")
	version := flag.Bool("version", false, "")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n\n", os.Args[0])
		fmt.Println("Options:")
		fmt.Println("  -p:        Port (Default: 443)")
		fmt.Println("  -i:        IP Address (Default: 127.0.0.1)")
		fmt.Println("  -P:        Origin port")
		fmt.Println("  -H:        Origin host")
		fmt.Println("  -D:        Use HTTP/2 direct mode to connect origin")
		fmt.Println("  -c:        Certificate file")
		fmt.Println("  -k:        Certificate key file")
		fmt.Println("  -l:        Log format (default or json, Default: default)")
		fmt.Println("  --version: Display version information and exit.")
		fmt.Println("  --help:    Display this help and exit.")
		os.Exit(1)
	}

	flag.Parse()

	if *version {
		logger.Printf("h2analyzer %s\n", VERSION)
		os.Exit(0)
	}

	if *port == 0 {
		*port = 443
	}
	addr := fmt.Sprintf("%s:%d", *ip, *port)

	if *originPort == 0 {
		logger.Fatalln("Origin port is not specified")
	}
	if *originHost == "" {
		logger.Fatalln("Origin host is not specified")
	}
	originConfig := OriginConfig{
		Addr:   fmt.Sprintf("%s:%d", *originHost, *originPort),
		Direct: *originDirect,
	}

	var formatter Formatter
	if *logFormat == "json" {
		formatter = JSONFormatter
	} else {
		formatter = DefaultFormatter
	}

	cert, err := tls.LoadX509KeyPair(*certPath, *keyPath)
	if err != nil {
		logger.Fatalln("Invalid certificate file")
	}

	config := &tls.Config{}
	config.Certificates = []tls.Certificate{cert}
	config.NextProtos = append(config.NextProtos, "h2", "h2-16", "h2-15", "h2-14")

	server, err := tls.Listen("tcp", addr, config)
	if err != nil {
		logger.Fatalf("Could not bind address - %s\n", addr)
	}

	defer server.Close()

	for {
		remoteConn, err := server.Accept()
		if err != nil {
			logger.Printf("Unable to accept: %s", err)
			continue
		}

		go handlePeer(remoteConn, originConfig, formatter)
	}
}

func handlePeer(remoteConn net.Conn, originConfig OriginConfig, formatter Formatter) {
	var originConn net.Conn
	var err error

	defer remoteConn.Close()

	dumper := NewFrameDumper(remoteConn.RemoteAddr(), formatter)
	defer dumper.Close()

	remoteCh, remoteErrCh := handleConnection(remoteConn)

	select {
	case chunk := <-remoteCh:
		connState := remoteConn.(*tls.Conn).ConnectionState()
		dumper.DumpConnectionState(connState)

		config := &tls.Config{}
		config.NextProtos = append(config.NextProtos, connState.NegotiatedProtocol)
		config.CipherSuites = []uint16{connState.CipherSuite}
		config.ServerName = connState.ServerName
		config.InsecureSkipVerify = true

		if originConfig.Direct {
			originConn, err = net.Dial("tcp", originConfig.Addr)
		} else {
			dialer := new(net.Dialer)
			originConn, err = tls.DialWithDialer(dialer, "tcp", originConfig.Addr, config)
		}
		if err != nil {
			logger.Printf("Unable to connect to the origin: %s", err)
			return
		}

		defer originConn.Close()

		_, err = originConn.Write(chunk)
		if err != nil {
			logger.Printf("Unable to write data to the origin: %s", err)
			return
		}

		dumper.DumpFrame(chunk, true)

	case err := <-remoteErrCh:
		if err != io.EOF {
			logger.Printf("Connection error: %s", err)
		}
		return
	}

	originCh, originErrCh := handleConnection(originConn)

	for {
		select {
		case chunk := <-remoteCh:
			_, err := originConn.Write(chunk)
			if err != nil {
				logger.Printf("Unable to write data to the origin: %s", err)
				return
			}
			dumper.DumpFrame(chunk, true)

		case err := <-remoteErrCh:
			if err != io.EOF {
				logger.Printf("Connection error: %s", err)
			}
			return

		case chunk := <-originCh:
			_, err := remoteConn.Write(chunk)
			if err != nil {
				logger.Printf("Unable to write data to the connection: %s", err)
				return
			}
			dumper.DumpFrame(chunk, false)

		case err := <-originErrCh:
			if err != io.EOF {
				logger.Printf("Origin error: %s", err)
			}
			return
		}
	}
}

func handleConnection(conn net.Conn) (<-chan []byte, <-chan error) {
	dataCh := make(chan []byte)
	errCh := make(chan error, 1)

	go func() {
		for {
			buf := make([]byte, 16384)

			n, err := conn.Read(buf)
			if err != nil {
				errCh <- err
				break
			}

			dataCh <- buf[:n]
		}
	}()

	return dataCh, errCh
}
<commit_msg>Fix command name<commit_after>package main

import (
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
)

const VERSION = "v0.0.2"

var logger = log.New(os.Stderr, "", 0)

type OriginConfig struct {
	Addr   string
	Direct bool
}

func main() {
	port := flag.Int("p", 0, "")
	ip := flag.String("i", "127.0.0.1", "")
	originPort := flag.Int("P", 0, "")
	originHost := flag.String("H", "", "")
	originDirect := flag.Bool("D", false, "")
	certPath := flag.String("c", "", "")
	keyPath := flag.String("k", "", "")
	logFormat := flag.String("l", "default", "")
	version := flag.Bool("version", false, "")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n\n", os.Args[0])
		fmt.Println("Options:")
		fmt.Println("  -p:        Port (Default: 443)")
		fmt.Println("  -i:        IP Address (Default: 127.0.0.1)")
		fmt.Println("  -P:        Origin port")
		fmt.Println("  -H:        Origin host")
		fmt.Println("  -D:        Use HTTP/2 direct mode to connect origin")
		fmt.Println("  -c:        Certificate file")
		fmt.Println("  -k:        Certificate key file")
		fmt.Println("  -l:        Log format (default or json, Default: default)")
		fmt.Println("  --version: Display version information and exit.")
		fmt.Println("  --help:    Display this help and exit.")
		os.Exit(1)
	}

	flag.Parse()

	if *version {
		logger.Printf("h2a %s\n", VERSION)
		os.Exit(0)
	}

	if *port == 0 {
		*port = 443
	}
	addr := fmt.Sprintf("%s:%d", *ip, *port)

	if *originPort == 0 {
		logger.Fatalln("Origin port is not specified")
	}
	if *originHost == "" {
		logger.Fatalln("Origin host is not specified")
	}
	originConfig := OriginConfig{
		Addr:   fmt.Sprintf("%s:%d", *originHost, *originPort),
		Direct: *originDirect,
	}

	var formatter Formatter
	if *logFormat == "json" {
		formatter = JSONFormatter
	} else {
		formatter = DefaultFormatter
	}

	cert, err := tls.LoadX509KeyPair(*certPath, *keyPath)
	if err != nil {
		logger.Fatalln("Invalid certificate file")
	}

	config := &tls.Config{}
	config.Certificates = []tls.Certificate{cert}
	config.NextProtos = append(config.NextProtos, "h2", "h2-16", "h2-15", "h2-14")

	server, err := tls.Listen("tcp", addr, config)
	if err != nil {
		logger.Fatalf("Could not bind address - %s\n", addr)
	}

	defer server.Close()

	for {
		remoteConn, err := server.Accept()
		if err != nil {
			logger.Printf("Unable to accept: %s", err)
			continue
		}

		go handlePeer(remoteConn, originConfig, formatter)
	}
}

func handlePeer(remoteConn net.Conn, originConfig OriginConfig, formatter Formatter) {
	var originConn net.Conn
	var err error

	defer remoteConn.Close()

	dumper := NewFrameDumper(remoteConn.RemoteAddr(), formatter)
	defer dumper.Close()

	remoteCh, remoteErrCh := handleConnection(remoteConn)

	select {
	case chunk := <-remoteCh:
		connState := remoteConn.(*tls.Conn).ConnectionState()
		dumper.DumpConnectionState(connState)

		config := &tls.Config{}
		config.NextProtos = append(config.NextProtos, connState.NegotiatedProtocol)
		config.CipherSuites = []uint16{connState.CipherSuite}
		config.ServerName = connState.ServerName
		config.InsecureSkipVerify = true

		if originConfig.Direct {
			originConn, err = net.Dial("tcp", originConfig.Addr)
		} else {
			dialer := new(net.Dialer)
			originConn, err = tls.DialWithDialer(dialer, "tcp", originConfig.Addr, config)
		}
		if err != nil {
			logger.Printf("Unable to connect to the origin: %s", err)
			return
		}

		defer originConn.Close()

		_, err = originConn.Write(chunk)
		if err != nil {
			logger.Printf("Unable to write data to the origin: %s", err)
			return
		}

		dumper.DumpFrame(chunk, true)

	case err := <-remoteErrCh:
		if err != io.EOF {
			logger.Printf("Connection error: %s", err)
		}
		return
	}

	originCh, originErrCh := handleConnection(originConn)

	for {
		select {
		case chunk := <-remoteCh:
			_, err := originConn.Write(chunk)
			if err != nil {
				logger.Printf("Unable to write data to the origin: %s", err)
				return
			}
			dumper.DumpFrame(chunk, true)

		case err := <-remoteErrCh:
			if err != io.EOF {
				logger.Printf("Connection error: %s", err)
			}
			return

		case chunk := <-originCh:
			_, err := remoteConn.Write(chunk)
			if err != nil {
				logger.Printf("Unable to write data to the connection: %s", err)
				return
			}
			dumper.DumpFrame(chunk, false)

		case err := <-originErrCh:
			if err != io.EOF {
				logger.Printf("Origin error: %s", err)
			}
			return
		}
	}
}

func handleConnection(conn net.Conn) (<-chan []byte, <-chan error) {
	dataCh := make(chan []byte)
	errCh := make(chan error, 1)

	go func() {
		for {
			buf := make([]byte, 16384)

			n, err := conn.Read(buf)
			if err != nil {
				errCh <- err
				break
			}

			dataCh <- buf[:n]
		}
	}()

	return dataCh, errCh
}
<|endoftext|>


<commit_before>package cfitsio

// #include "go-cfitsio.h"
import "C"
import "unsafe"

type HduType int

const (
	ImageHdu  HduType = C.IMAGE_HDU  // Primary Array or IMAGE HDU
	AsciiTbl  HduType = C.ASCII_TBL  // ASCII table HDU
	BinaryTbl HduType = C.BINARY_TBL // Binary table HDU
	AnyHdy    HduType = C.ANY_HDU    // matches any HDU type
)

// MoveAbsHdu moves to a different HDU in the file
func (f *File) MoveAbsHdu(hdu int) (HduType, error) {
	c_hdu := C.int(hdu)
	c_htype := C.int(0)
	c_status := C.int(0)

	C.fits_movabs_hdu(f.c, c_hdu, &c_htype, &c_status)
	if c_status > 0 {
		return HduType(c_htype), to_err(c_status)
	}

	return HduType(c_htype), nil
}

// MoveRelHdu moves to a different HDU in the file
func (f *File) MoveRelHdu(n int) (HduType, error) {
	c_n := C.int(n)
	c_htype := C.int(0)
	c_status := C.int(0)

	C.fits_movrel_hdu(f.c, c_n, &c_htype, &c_status)
	if c_status > 0 {
		return HduType(c_htype), to_err(c_status)
	}

	return HduType(c_htype), nil
}

// MoveNamHdu moves to a different HDU in the file
func (f *File) MoveNamHdu(hdu HduType, extname string, extvers int) error {
	c_hdu := C.int(hdu)
	c_name := C.CString(extname)
	defer C.free(unsafe.Pointer(c_name))
	c_vers := C.int(extvers)
	c_status := C.int(0)

	C.fits_movnam_hdu(f.c, c_hdu, c_name, c_vers, &c_status)
	if c_status > 0 {
		return to_err(c_status)
	}

	return nil
}

// NumHdus returns the total number of HDUs in the FITS file.
// This returns the number of completely defined HDUs in the file. If a new HDU has just been added to the FITS file, then that last HDU will only be counted if it has been closed, or if data has been written to the HDU. The current HDU remains unchanged by this routine.
func (f *File) NumHdus() (int, error) {
	c_n := C.int(0)
	c_status := C.int(0)
	C.fits_get_num_hdus(f.c, &c_n, &c_status)
	if c_status > 0 {
		return 0, to_err(c_status)
	}

	return int(c_n), nil
}

// HduNum returns the number of the current HDU (CHDU) in the FITS file (where the primary array = 1). This function returns the HDU number rather than a status value.
func (f *File) HduNum() int {

	c_n := C.int(0)
	C.fits_get_hdu_num(f.c, &c_n)
	return int(c_n)
}

// HduType returns the type of the current HDU in the FITS file. The possible values for hdutype are: IMAGE_HDU, ASCII_TBL, or BINARY_TBL.
func (f *File) HduType() (HduType, error) {
	c_hdu := C.int(0)
	c_status := C.int(0)
	C.fits_get_hdu_type(f.c, &c_hdu, &c_status)
	if c_status > 0 {
		return 0, to_err(c_status)
	}

	return HduType(c_hdu), nil
}

// EOF
<commit_msg>hdu: wrap fits_copy_file<commit_after>package cfitsio

// #include "go-cfitsio.h"
import "C"
import "unsafe"

type HduType int

const (
	ImageHdu  HduType = C.IMAGE_HDU  // Primary Array or IMAGE HDU
	AsciiTbl  HduType = C.ASCII_TBL  // ASCII table HDU
	BinaryTbl HduType = C.BINARY_TBL // Binary table HDU
	AnyHdy    HduType = C.ANY_HDU    // matches any HDU type
)

// MoveAbsHdu moves to a different HDU in the file
func (f *File) MoveAbsHdu(hdu int) (HduType, error) {
	c_hdu := C.int(hdu)
	c_htype := C.int(0)
	c_status := C.int(0)

	C.fits_movabs_hdu(f.c, c_hdu, &c_htype, &c_status)
	if c_status > 0 {
		return HduType(c_htype), to_err(c_status)
	}

	return HduType(c_htype), nil
}

// MoveRelHdu moves to a different HDU in the file
func (f *File) MoveRelHdu(n int) (HduType, error) {
	c_n := C.int(n)
	c_htype := C.int(0)
	c_status := C.int(0)

	C.fits_movrel_hdu(f.c, c_n, &c_htype, &c_status)
	if c_status > 0 {
		return HduType(c_htype), to_err(c_status)
	}

	return HduType(c_htype), nil
}

// MoveNamHdu moves to a different HDU in the file
func (f *File) MoveNamHdu(hdu HduType, extname string, extvers int) error {
	c_hdu := C.int(hdu)
	c_name := C.CString(extname)
	defer C.free(unsafe.Pointer(c_name))
	c_vers := C.int(extvers)
	c_status := C.int(0)

	C.fits_movnam_hdu(f.c, c_hdu, c_name, c_vers, &c_status)
	if c_status > 0 {
		return to_err(c_status)
	}

	return nil
}

// NumHdus returns the total number of HDUs in the FITS file.
// This returns the number of completely defined HDUs in the file. If a new HDU has just been added to the FITS file, then that last HDU will only be counted if it has been closed, or if data has been written to the HDU. The current HDU remains unchanged by this routine.
func (f *File) NumHdus() (int, error) {
	c_n := C.int(0)
	c_status := C.int(0)
	C.fits_get_num_hdus(f.c, &c_n, &c_status)
	if c_status > 0 {
		return 0, to_err(c_status)
	}

	return int(c_n), nil
}

// HduNum returns the number of the current HDU (CHDU) in the FITS file (where the primary array = 1). This function returns the HDU number rather than a status value.
func (f *File) HduNum() int {

	c_n := C.int(0)
	C.fits_get_hdu_num(f.c, &c_n)
	return int(c_n)
}

// HduType returns the type of the current HDU in the FITS file. The possible values for hdutype are: IMAGE_HDU, ASCII_TBL, or BINARY_TBL.
func (f *File) HduType() (HduType, error) {
	c_hdu := C.int(0)
	c_status := C.int(0)
	C.fits_get_hdu_type(f.c, &c_hdu, &c_status)
	if c_status > 0 {
		return 0, to_err(c_status)
	}

	return HduType(c_hdu), nil
}

// Copy all or part of the HDUs in the FITS file associated with infptr and append them to the end of the FITS file associated with outfptr. If 'previous' is true, then any HDUs preceding the current HDU in the input file will be copied to the output file. Similarly, 'current' and 'following' determine whether the current HDU, and/or any following HDUs in the input file will be copied to the output file. Thus, if all 3 parameters are true, then the entire input file will be copied. On exit, the current HDU in the input file will be unchanged, and the last HDU in the output file will be the current HDU.
func (f *File) Copy(out *File, previous, current, following bool) error {
	c_previous := C.int(0)
	if previous {
		c_previous = C.int(1)
	}
	c_current := C.int(0)
	if current {
		c_current = C.int(1)
	}
	c_following := C.int(0)
	if following {
		c_following = C.int(1)
	}
	c_status := C.int(0)
	C.fits_copy_file(f.c, out.c, c_previous, c_current, c_following, &c_status)
	if c_status > 0 {
		return to_err(c_status)
	}
	return nil
}

// EOF
<|endoftext|>


<commit_before>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequestParsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Device.Location.Coordinates.latitude)

}
<commit_msg>Fix test, incorrect nested field<commit_after>package model_test

import (
	"encoding/json"
	"github.com/crezam/actions-on-google-golang/internal/test"
	"github.com/crezam/actions-on-google-golang/model"
	"os"
	"testing"
	"time"
)

func TestRequestParsing(t *testing.T) {

	var req model.ApiAiRequest

	file, _ := os.Open("./data/sample_request1.json")
	dec := json.NewDecoder(file)

	err := dec.Decode(&req)

	// test if any issues decoding file
	test.Ok(t, err)

	// assert correct parsing
	test.Equals(t, "209eefa7-adb5-4d03-a8b9-9f7ae68a0c11", req.Id)

	expectedTimestamp, _ := time.Parse(time.RFC3339Nano, "2016-10-10T07:41:40.098Z")
	test.Equals(t, expectedTimestamp, req.Timestamp)

	test.Equals(t, "Hi, my name is Sam!", req.Result.ResolvedQuery)
	test.Equals(t, "agent", req.Result.Source)
	test.Equals(t, "greetings", req.Result.Action)
	test.Equals(t, false, req.Result.ActionIncomplete)
	test.Equals(t, "Sam", req.Result.Parameters["user_name"])
	test.Equals(t, "", req.Result.Parameters["school"])

	test.Equals(t, "greetings", req.Result.Contexts[0].Name)
	test.Equals(t, "Sam", req.Result.Contexts[0].Parameters["user_name"])
	test.Equals(t, "Sam!", req.Result.Contexts[0].Parameters["user_name.original"])

	test.Equals(t, "373a354b-c15a-4a60-ac9d-a9f2aee76cb4", req.Result.Metadata.IntentID)
	test.Equals(t, "true", req.Result.Metadata.WebhookUsed)
	test.Equals(t, "greetings", req.Result.Metadata.IntentName)

	test.Equals(t, "Nice to meet you, Sam!", req.Result.Fulfillment.Speech)

	test.Equals(t, float64(1), req.Result.Score)

	test.Equals(t, "...", req.OriginalRequest.Data.User.UserID)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.DisplayName)
	test.Equals(t, "Sam", req.OriginalRequest.Data.User.Profile.GivenName)
	test.Equals(t, "Johnson", req.OriginalRequest.Data.User.Profile.FamilyName)

	test.Equals(t, "...", req.OriginalRequest.Data.User.AccessToken)

	test.Equals(t, 123.456, req.OriginalRequest.Data.Device.Location.Coordinates.Latitude)

}
<|endoftext|>


<commit_before>package models

import (
	"bytes"
	"time"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"encoding/base64"
	"github.com/PuerkitoBio/goquery"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	"github.com/lifei6671/mindoc/conf"
	"github.com/lifei6671/mindoc/converter"
	"github.com/lifei6671/mindoc/utils"
	"github.com/russross/blackfriday"
)

type BookResult struct {
	BookId         int       `json:"book_id"`
	BookName       string    `json:"book_name"`
	Identify       string    `json:"identify"`
	OrderIndex     int       `json:"order_index"`
	Description    string    `json:"description"`
	Publisher      string    `json:"publisher"`
	PrivatelyOwned int       `json:"privately_owned"`
	PrivateToken   string    `json:"private_token"`
	DocCount       int       `json:"doc_count"`
	CommentStatus  string    `json:"comment_status"`
	CommentCount   int       `json:"comment_count"`
	CreateTime     time.Time `json:"create_time"`
	CreateName     string    `json:"create_name"`
	ModifyTime     time.Time `json:"modify_time"`
	Cover          string    `json:"cover"`
	Theme          string    `json:"theme"`
	Label          string    `json:"label"`
	MemberId       int       `json:"member_id"`
	Editor         string    `json:"editor"`
	AutoRelease    bool      `json:"auto_release"`

	RelationshipId int    `json:"relationship_id"`
	RoleId         int    `json:"role_id"`
	RoleName       string `json:"role_name"`
	Status         int

	LastModifyText   string `json:"last_modify_text"`
	IsDisplayComment bool   `json:"is_display_comment"`
}

func NewBookResult() *BookResult {
	return &BookResult{}
}

// 根据项目标识查询项目以及指定用户权限的信息.
func (m *BookResult) FindByIdentify(identify string, member_id int) (*BookResult, error) {
	if identify == "" || member_id <= 0 {
		return m, ErrInvalidParameter
	}
	o := orm.NewOrm()

	book := NewBook()

	err := o.QueryTable(book.TableNameWithPrefix()).Filter("identify", identify).One(book)

	if err != nil {
		return m, err
	}

	relationship := NewRelationship()

	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id", book.BookId).Filter("member_id", member_id).One(relationship)

	if err != nil {
		return m, err
	}
	var relationship2 Relationship

	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id", book.BookId).Filter("role_id", 0).One(&relationship2)

	if err != nil {
		logs.Error("根据项目标识查询项目以及指定用户权限的信息 => ", err)
		return m, ErrPermissionDenied
	}

	member, err := NewMember().Find(relationship2.MemberId)
	if err != nil {
		return m, err
	}

	m = NewBookResult().ToBookResult(*book)

	m.CreateName = member.Account
	m.MemberId = relationship.MemberId
	m.RoleId = relationship.RoleId
	m.RelationshipId = relationship.RelationshipId

	if m.RoleId == conf.BookFounder {
		m.RoleName = "创始人"
	} else if m.RoleId == conf.BookAdmin {
		m.RoleName = "管理员"
	} else if m.RoleId == conf.BookEditor {
		m.RoleName = "编辑者"
	} else if m.RoleId == conf.BookObserver {
		m.RoleName = "观察者"
	}

	doc := NewDocument()

	err = o.QueryTable(doc.TableNameWithPrefix()).Filter("book_id", book.BookId).OrderBy("modify_time").One(doc)

	if err == nil {
		member2 := NewMember()
		member2.Find(doc.ModifyAt)

		m.LastModifyText = member2.Account + " 于 " + doc.ModifyTime.Format("2006-01-02 15:04:05")
	}

	return m, nil
}

func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult, totalCount int, err error) {
	o := orm.NewOrm()

	count, err := o.QueryTable(NewBook().TableNameWithPrefix()).Count()

	if err != nil {
		return
	}
	totalCount = int(count)

	sql := `SELECT
			book.*,rel.relationship_id,rel.role_id,m.account AS create_name
		FROM md_books AS book
			LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.role_id = 0
			LEFT JOIN md_members AS m ON rel.member_id = m.member_id
		ORDER BY book.order_index DESC ,book.book_id DESC  LIMIT ?,?`

	offset := (pageIndex - 1) * pageSize

	_, err = o.Raw(sql, offset, pageSize).QueryRows(&books)

	return
}

//实体转换
func (m *BookResult) ToBookResult(book Book) *BookResult {

	m.BookId = book.BookId
	m.BookName = book.BookName
	m.Identify = book.Identify
	m.OrderIndex = book.OrderIndex
	m.Description = strings.Replace(book.Description, "\r\n", "
", -1)
	m.PrivatelyOwned = book.PrivatelyOwned
	m.PrivateToken = book.PrivateToken
	m.DocCount = book.DocCount
	m.CommentStatus = book.CommentStatus
	m.CommentCount = book.CommentCount
	m.CreateTime = book.CreateTime
	m.ModifyTime = book.ModifyTime
	m.Cover = book.Cover
	m.Label = book.Label
	m.Status = book.Status
	m.Editor = book.Editor
	m.Theme = book.Theme
	m.AutoRelease = book.AutoRelease == 1
	m.Publisher = book.Publisher

	if book.Theme == "" {
		m.Theme = "default"
	}
	if book.Editor == "" {
		m.Editor = "markdown"
	}
	return m
}

func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {

	convertBookResult := ConvertBookResult{}

	outputPath := filepath.Join(conf.WorkingDirectory,"uploads","books", strconv.Itoa(m.BookId))
	viewPath := beego.BConfig.WebConfig.ViewsPath

	pdfpath := filepath.Join(outputPath, "book.pdf")
	epubpath := filepath.Join(outputPath, "book.epub")
	mobipath := filepath.Join(outputPath, "book.mobi")
	docxpath := filepath.Join(outputPath, "book.docx")

	//先将转换的文件储存到临时目录
	tempOutputPath :=  filepath.Join(os.TempDir(),"sessionId") //filepath.Abs(filepath.Join("cache", sessionId))

	os.MkdirAll(outputPath, 0766)
	os.MkdirAll(tempOutputPath, 0766)

	if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath) && utils.FileExists(docxpath) {
		convertBookResult.EpubPath = epubpath
		convertBookResult.MobiPath = mobipath
		convertBookResult.PDFPath = pdfpath
		convertBookResult.WordPath = docxpath
		return convertBookResult, nil
	}

	docs, err := NewDocument().FindListByBookId(m.BookId)
	if err != nil {
		return convertBookResult, err
	}

	tocList := make([]converter.Toc, 0)

	for _, item := range docs {
		if item.ParentId == 0 {
			toc := converter.Toc{
				Id:    item.DocumentId,
				Link:  strconv.Itoa(item.DocumentId) + ".html",
				Pid:   item.ParentId,
				Title: item.DocumentName,
			}

			tocList = append(tocList, toc)
		}
	}
	for _, item := range docs {
		if item.ParentId != 0 {
			toc := converter.Toc{
				Id:    item.DocumentId,
				Link:  strconv.Itoa(item.DocumentId) + ".html",
				Pid:   item.ParentId,
				Title: item.DocumentName,
			}
			tocList = append(tocList, toc)
		}
	}

	ebookConfig := converter.Config{
		Charset:      "utf-8",
		Cover:        m.Cover,
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
		Description:  string(blackfriday.MarkdownBasic([]byte(m.Description))),
		Footer:       "
本文档使用 MinDoc 构建 - _PAGENUM_ -
",
		Header:       "
_SECTION_
",
		Identifier:   "",
		Language:     "zh-CN",
		Creator:      m.CreateName,
		Publisher:    m.Publisher,
		Contributor:  m.Publisher,
		Title:        m.BookName,
		Format:       []string{"epub", "mobi", "pdf", "docx"},
		FontSize:     "14",
		PaperSize:    "a4",
		MarginLeft:   "72",
		MarginRight:  "72",
		MarginTop:    "72",
		MarginBottom: "72",
		Toc:          tocList,
		More:         []string{},
	}

	if tempOutputPath, err = filepath.Abs(tempOutputPath); err != nil {
		beego.Error("导出目录配置错误：" + err.Error())
		return convertBookResult, err
	}

	for _, item := range docs {
		name := strconv.Itoa(item.DocumentId)
		fpath := filepath.Join(tempOutputPath, name+".html")

		f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			return convertBookResult, err
		}
		var buf bytes.Buffer

		if err := beego.ExecuteViewPathTemplate(&buf, "document/export.tpl", viewPath, map[string]interface{}{"Model": m, "Lists": item, "BaseUrl": conf.BaseUrl}); err != nil {
			return convertBookResult, err
		}
		html := buf.String()

		if err != nil {

			f.Close()
			return convertBookResult, err
		}

		bufio := bytes.NewReader(buf.Bytes())

		doc, err := goquery.NewDocumentFromReader(bufio)
		doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
			if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/") {
				//contentSelection.SetAttr("src", baseUrl + src)
				spath := filepath.Join(conf.WorkingDirectory, src)

				if ff, e := ioutil.ReadFile(spath); e == nil {

					encodeString := base64.StdEncoding.EncodeToString(ff)

					src = "data:image/" + filepath.Ext(src) + ";base64," + encodeString

					contentSelection.SetAttr("src", src)
				}

			}
		})

		html, err = doc.Html()
		if err != nil {
			f.Close()
			return convertBookResult, err
		}

		// html = strings.Replace(html, "<img src=\"/uploads", " " + err.Error())
		return convertBookResult, err
	}
	beego.Info("文档转换完成：" + m.BookName)
	defer func(p string) {
		os.RemoveAll(p)
	}(tempOutputPath)

	utils.CopyFile(mobipath, filepath.Join(tempOutputPath, "output", "book.mobi"))
	utils.CopyFile(pdfpath, filepath.Join(tempOutputPath, "output", "book.pdf"))
	utils.CopyFile(epubpath, filepath.Join(tempOutputPath, "output", "book.epub"))
	utils.CopyFile(docxpath, filepath.Join(tempOutputPath, "output", "book.docx"))

	convertBookResult.MobiPath = mobipath
	convertBookResult.PDFPath = pdfpath
	convertBookResult.EpubPath = epubpath
	convertBookResult.WordPath = docxpath

	return convertBookResult, nil
}
<commit_msg>修复导出目录路径BUG<commit_after>package models

import (
	"bytes"
	"time"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"encoding/base64"
	"github.com/PuerkitoBio/goquery"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/logs"
	"github.com/astaxie/beego/orm"
	"github.com/lifei6671/mindoc/conf"
	"github.com/lifei6671/mindoc/converter"
	"github.com/lifei6671/mindoc/utils"
	"github.com/russross/blackfriday"
)

type BookResult struct {
	BookId         int       `json:"book_id"`
	BookName       string    `json:"book_name"`
	Identify       string    `json:"identify"`
	OrderIndex     int       `json:"order_index"`
	Description    string    `json:"description"`
	Publisher      string    `json:"publisher"`
	PrivatelyOwned int       `json:"privately_owned"`
	PrivateToken   string    `json:"private_token"`
	DocCount       int       `json:"doc_count"`
	CommentStatus  string    `json:"comment_status"`
	CommentCount   int       `json:"comment_count"`
	CreateTime     time.Time `json:"create_time"`
	CreateName     string    `json:"create_name"`
	ModifyTime     time.Time `json:"modify_time"`
	Cover          string    `json:"cover"`
	Theme          string    `json:"theme"`
	Label          string    `json:"label"`
	MemberId       int       `json:"member_id"`
	Editor         string    `json:"editor"`
	AutoRelease    bool      `json:"auto_release"`

	RelationshipId int    `json:"relationship_id"`
	RoleId         int    `json:"role_id"`
	RoleName       string `json:"role_name"`
	Status         int

	LastModifyText   string `json:"last_modify_text"`
	IsDisplayComment bool   `json:"is_display_comment"`
}

func NewBookResult() *BookResult {
	return &BookResult{}
}

// 根据项目标识查询项目以及指定用户权限的信息.
func (m *BookResult) FindByIdentify(identify string, member_id int) (*BookResult, error) {
	if identify == "" || member_id <= 0 {
		return m, ErrInvalidParameter
	}
	o := orm.NewOrm()

	book := NewBook()

	err := o.QueryTable(book.TableNameWithPrefix()).Filter("identify", identify).One(book)

	if err != nil {
		return m, err
	}

	relationship := NewRelationship()

	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id", book.BookId).Filter("member_id", member_id).One(relationship)

	if err != nil {
		return m, err
	}
	var relationship2 Relationship

	err = o.QueryTable(relationship.TableNameWithPrefix()).Filter("book_id", book.BookId).Filter("role_id", 0).One(&relationship2)

	if err != nil {
		logs.Error("根据项目标识查询项目以及指定用户权限的信息 => ", err)
		return m, ErrPermissionDenied
	}

	member, err := NewMember().Find(relationship2.MemberId)
	if err != nil {
		return m, err
	}

	m = NewBookResult().ToBookResult(*book)

	m.CreateName = member.Account
	m.MemberId = relationship.MemberId
	m.RoleId = relationship.RoleId
	m.RelationshipId = relationship.RelationshipId

	if m.RoleId == conf.BookFounder {
		m.RoleName = "创始人"
	} else if m.RoleId == conf.BookAdmin {
		m.RoleName = "管理员"
	} else if m.RoleId == conf.BookEditor {
		m.RoleName = "编辑者"
	} else if m.RoleId == conf.BookObserver {
		m.RoleName = "观察者"
	}

	doc := NewDocument()

	err = o.QueryTable(doc.TableNameWithPrefix()).Filter("book_id", book.BookId).OrderBy("modify_time").One(doc)

	if err == nil {
		member2 := NewMember()
		member2.Find(doc.ModifyAt)

		m.LastModifyText = member2.Account + " 于 " + doc.ModifyTime.Format("2006-01-02 15:04:05")
	}

	return m, nil
}

func (m *BookResult) FindToPager(pageIndex, pageSize int) (books []*BookResult, totalCount int, err error) {
	o := orm.NewOrm()

	count, err := o.QueryTable(NewBook().TableNameWithPrefix()).Count()

	if err != nil {
		return
	}
	totalCount = int(count)

	sql := `SELECT
			book.*,rel.relationship_id,rel.role_id,m.account AS create_name
		FROM md_books AS book
			LEFT JOIN md_relationship AS rel ON rel.book_id = book.book_id AND rel.role_id = 0
			LEFT JOIN md_members AS m ON rel.member_id = m.member_id
		ORDER BY book.order_index DESC ,book.book_id DESC  LIMIT ?,?`

	offset := (pageIndex - 1) * pageSize

	_, err = o.Raw(sql, offset, pageSize).QueryRows(&books)

	return
}

//实体转换
func (m *BookResult) ToBookResult(book Book) *BookResult {

	m.BookId = book.BookId
	m.BookName = book.BookName
	m.Identify = book.Identify
	m.OrderIndex = book.OrderIndex
	m.Description = strings.Replace(book.Description, "\r\n", "
", -1)
	m.PrivatelyOwned = book.PrivatelyOwned
	m.PrivateToken = book.PrivateToken
	m.DocCount = book.DocCount
	m.CommentStatus = book.CommentStatus
	m.CommentCount = book.CommentCount
	m.CreateTime = book.CreateTime
	m.ModifyTime = book.ModifyTime
	m.Cover = book.Cover
	m.Label = book.Label
	m.Status = book.Status
	m.Editor = book.Editor
	m.Theme = book.Theme
	m.AutoRelease = book.AutoRelease == 1
	m.Publisher = book.Publisher

	if book.Theme == "" {
		m.Theme = "default"
	}
	if book.Editor == "" {
		m.Editor = "markdown"
	}
	return m
}

func (m *BookResult) Converter(sessionId string) (ConvertBookResult, error) {

	convertBookResult := ConvertBookResult{}

	outputPath := filepath.Join(conf.WorkingDirectory,"uploads","books", strconv.Itoa(m.BookId))
	viewPath := beego.BConfig.WebConfig.ViewsPath

	pdfpath := filepath.Join(outputPath, "book.pdf")
	epubpath := filepath.Join(outputPath, "book.epub")
	mobipath := filepath.Join(outputPath, "book.mobi")
	docxpath := filepath.Join(outputPath, "book.docx")

	//先将转换的文件储存到临时目录
	tempOutputPath :=  filepath.Join(os.TempDir(),sessionId) //filepath.Abs(filepath.Join("cache", sessionId))

	os.MkdirAll(outputPath, 0766)
	os.MkdirAll(tempOutputPath, 0766)

	defer func(p string) {
		os.RemoveAll(p)
	}(tempOutputPath)

	if utils.FileExists(pdfpath) && utils.FileExists(epubpath) && utils.FileExists(mobipath) && utils.FileExists(docxpath) {
		convertBookResult.EpubPath = epubpath
		convertBookResult.MobiPath = mobipath
		convertBookResult.PDFPath = pdfpath
		convertBookResult.WordPath = docxpath
		return convertBookResult, nil
	}

	docs, err := NewDocument().FindListByBookId(m.BookId)
	if err != nil {
		return convertBookResult, err
	}

	tocList := make([]converter.Toc, 0)

	for _, item := range docs {
		if item.ParentId == 0 {
			toc := converter.Toc{
				Id:    item.DocumentId,
				Link:  strconv.Itoa(item.DocumentId) + ".html",
				Pid:   item.ParentId,
				Title: item.DocumentName,
			}

			tocList = append(tocList, toc)
		}
	}
	for _, item := range docs {
		if item.ParentId != 0 {
			toc := converter.Toc{
				Id:    item.DocumentId,
				Link:  strconv.Itoa(item.DocumentId) + ".html",
				Pid:   item.ParentId,
				Title: item.DocumentName,
			}
			tocList = append(tocList, toc)
		}
	}

	ebookConfig := converter.Config{
		Charset:      "utf-8",
		Cover:        m.Cover,
		Timestamp:    time.Now().Format("2006-01-02 15:04:05"),
		Description:  string(blackfriday.MarkdownBasic([]byte(m.Description))),
		Footer:       "
本文档使用 MinDoc 构建 - _PAGENUM_ -
",
		Header:       "
_SECTION_
",
		Identifier:   "",
		Language:     "zh-CN",
		Creator:      m.CreateName,
		Publisher:    m.Publisher,
		Contributor:  m.Publisher,
		Title:        m.BookName,
		Format:       []string{"epub", "mobi", "pdf", "docx"},
		FontSize:     "14",
		PaperSize:    "a4",
		MarginLeft:   "72",
		MarginRight:  "72",
		MarginTop:    "72",
		MarginBottom: "72",
		Toc:          tocList,
		More:         []string{},
	}

	if tempOutputPath, err = filepath.Abs(tempOutputPath); err != nil {
		beego.Error("导出目录配置错误：" + err.Error())
		return convertBookResult, err
	}

	for _, item := range docs {
		name := strconv.Itoa(item.DocumentId)
		fpath := filepath.Join(tempOutputPath, name+".html")

		f, err := os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			return convertBookResult, err
		}
		var buf bytes.Buffer

		if err := beego.ExecuteViewPathTemplate(&buf, "document/export.tpl", viewPath, map[string]interface{}{"Model": m, "Lists": item, "BaseUrl": conf.BaseUrl}); err != nil {
			return convertBookResult, err
		}
		html := buf.String()

		if err != nil {

			f.Close()
			return convertBookResult, err
		}

		bufio := bytes.NewReader(buf.Bytes())

		doc, err := goquery.NewDocumentFromReader(bufio)
		doc.Find("img").Each(func(i int, contentSelection *goquery.Selection) {
			if src, ok := contentSelection.Attr("src"); ok && strings.HasPrefix(src, "/") {
				//contentSelection.SetAttr("src", baseUrl + src)
				spath := filepath.Join(conf.WorkingDirectory, src)

				if ff, e := ioutil.ReadFile(spath); e == nil {

					encodeString := base64.StdEncoding.EncodeToString(ff)

					src = "data:image/" + filepath.Ext(src) + ";base64," + encodeString

					contentSelection.SetAttr("src", src)
				}

			}
		})

		html, err = doc.Html()
		if err != nil {
			f.Close()
			return convertBookResult, err
		}

		// html = strings.Replace(html, "<img src=\"/uploads", " " + err.Error())
		return convertBookResult, err
	}
	beego.Info("文档转换完成：" + m.BookName)

	utils.CopyFile(mobipath, filepath.Join(tempOutputPath, "output", "book.mobi"))
	utils.CopyFile(pdfpath, filepath.Join(tempOutputPath, "output", "book.pdf"))
	utils.CopyFile(epubpath, filepath.Join(tempOutputPath, "output", "book.epub"))
	utils.CopyFile(docxpath, filepath.Join(tempOutputPath, "output", "book.docx"))

	convertBookResult.MobiPath = mobipath
	convertBookResult.PDFPath = pdfpath
	convertBookResult.EpubPath = epubpath
	convertBookResult.WordPath = docxpath

	return convertBookResult, nil
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"
	"sync"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/dag"
)

// ContextGraphWalker is the GraphWalker implementation used with the
// Context struct to walk and evaluate the graph.
type ContextGraphWalker struct {
	NullGraphWalker

	// Configurable values
	Context   *Context
	Operation walkOperation

	// Outputs, do not set these. Do not read these while the graph
	// is being walked.
	ValidationWarnings []string
	ValidationErrors   []error

	errorLock           sync.Mutex
	once                sync.Once
	contexts            map[string]*BuiltinEvalContext
	contextLock         sync.Mutex
	interpolaterVars    map[string]map[string]string
	interpolaterVarLock sync.Mutex
	providerCache       map[string]ResourceProvider
	providerConfigCache map[string]*ResourceConfig
	providerLock        sync.Mutex
	provisionerCache    map[string]ResourceProvisioner
	provisionerLock     sync.Mutex
}

func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
	w.once.Do(w.init)

	w.contextLock.Lock()
	defer w.contextLock.Unlock()

	// If we already have a context for this path cached, use that
	key := PathCacheKey(path)
	if ctx, ok := w.contexts[key]; ok {
		return ctx
	}

	// Setup the variables for this interpolater
	variables := make(map[string]string)
	if len(path) <= 1 {
		for k, v := range w.Context.variables {
			variables[k] = v
		}
	}
	w.interpolaterVarLock.Lock()
	if m, ok := w.interpolaterVars[key]; ok {
		for k, v := range m {
			variables[k] = v
		}
	}
	w.interpolaterVars[key] = variables
	w.interpolaterVarLock.Unlock()

	ctx := &BuiltinEvalContext{
		PathValue:           path,
		Hooks:               w.Context.hooks,
		InputValue:          w.Context.uiInput,
		Providers:           w.Context.providers,
		ProviderCache:       w.providerCache,
		ProviderConfigCache: w.providerConfigCache,
		ProviderInputConfig: w.Context.providerInputConfig,
		ProviderLock:        &w.providerLock,
		Provisioners:        w.Context.provisioners,
		ProvisionerCache:    w.provisionerCache,
		ProvisionerLock:     &w.provisionerLock,
		DiffValue:           w.Context.diff,
		DiffLock:            &w.Context.diffLock,
		StateValue:          w.Context.state,
		StateLock:           &w.Context.stateLock,
		Interpolater: &Interpolater{
			Operation: w.Operation,
			Module:    w.Context.module,
			State:     w.Context.state,
			StateLock: &w.Context.stateLock,
			Variables: variables,
		},
		InterpolaterVars:    w.interpolaterVars,
		InterpolaterVarLock: &w.interpolaterVarLock,
	}

	w.contexts[key] = ctx
	return ctx
}

func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
	log.Printf("[TRACE] Entering eval tree: %s", dag.VertexName(v))

	// Acquire a lock on the semaphore
	w.Context.parallelSem.Acquire()

	// We want to filter the evaluation tree to only include operations
	// that belong in this operation.
	return EvalFilter(n, EvalNodeFilterOp(w.Operation))
}

func (w *ContextGraphWalker) ExitEvalTree(
	v dag.Vertex, output interface{}, err error) error {
	log.Printf("[TRACE] Exiting eval tree: %s", dag.VertexName(v))

	// Release the semaphore
	w.Context.parallelSem.Release()

	if err == nil {
		return nil
	}

	// Acquire the lock because anything is going to require a lock.
	w.errorLock.Lock()
	defer w.errorLock.Unlock()

	// Try to get a validation error out of it. If its not a validation
	// error, then just record the normal error.
	verr, ok := err.(*EvalValidateError)
	if !ok {
		return err
	}

	for _, msg := range verr.Warnings {
		w.ValidationWarnings = append(
			w.ValidationWarnings,
			fmt.Sprintf("%s: %s", dag.VertexName(v), msg))
	}
	for _, e := range verr.Errors {
		w.ValidationErrors = append(
			w.ValidationErrors,
			errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e))
	}

	return nil
}

func (w *ContextGraphWalker) init() {
	w.contexts = make(map[string]*BuiltinEvalContext, 5)
	w.providerCache = make(map[string]ResourceProvider, 5)
	w.providerConfigCache = make(map[string]*ResourceConfig, 5)
	w.provisionerCache = make(map[string]ResourceProvisioner, 5)
	w.interpolaterVars = make(map[string]map[string]string, 5)
}
<commit_msg>core: log eval tree operations<commit_after>package terraform

import (
	"fmt"
	"log"
	"sync"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/dag"
)

// ContextGraphWalker is the GraphWalker implementation used with the
// Context struct to walk and evaluate the graph.
type ContextGraphWalker struct {
	NullGraphWalker

	// Configurable values
	Context   *Context
	Operation walkOperation

	// Outputs, do not set these. Do not read these while the graph
	// is being walked.
	ValidationWarnings []string
	ValidationErrors   []error

	errorLock           sync.Mutex
	once                sync.Once
	contexts            map[string]*BuiltinEvalContext
	contextLock         sync.Mutex
	interpolaterVars    map[string]map[string]string
	interpolaterVarLock sync.Mutex
	providerCache       map[string]ResourceProvider
	providerConfigCache map[string]*ResourceConfig
	providerLock        sync.Mutex
	provisionerCache    map[string]ResourceProvisioner
	provisionerLock     sync.Mutex
}

func (w *ContextGraphWalker) EnterPath(path []string) EvalContext {
	w.once.Do(w.init)

	w.contextLock.Lock()
	defer w.contextLock.Unlock()

	// If we already have a context for this path cached, use that
	key := PathCacheKey(path)
	if ctx, ok := w.contexts[key]; ok {
		return ctx
	}

	// Setup the variables for this interpolater
	variables := make(map[string]string)
	if len(path) <= 1 {
		for k, v := range w.Context.variables {
			variables[k] = v
		}
	}
	w.interpolaterVarLock.Lock()
	if m, ok := w.interpolaterVars[key]; ok {
		for k, v := range m {
			variables[k] = v
		}
	}
	w.interpolaterVars[key] = variables
	w.interpolaterVarLock.Unlock()

	ctx := &BuiltinEvalContext{
		PathValue:           path,
		Hooks:               w.Context.hooks,
		InputValue:          w.Context.uiInput,
		Providers:           w.Context.providers,
		ProviderCache:       w.providerCache,
		ProviderConfigCache: w.providerConfigCache,
		ProviderInputConfig: w.Context.providerInputConfig,
		ProviderLock:        &w.providerLock,
		Provisioners:        w.Context.provisioners,
		ProvisionerCache:    w.provisionerCache,
		ProvisionerLock:     &w.provisionerLock,
		DiffValue:           w.Context.diff,
		DiffLock:            &w.Context.diffLock,
		StateValue:          w.Context.state,
		StateLock:           &w.Context.stateLock,
		Interpolater: &Interpolater{
			Operation: w.Operation,
			Module:    w.Context.module,
			State:     w.Context.state,
			StateLock: &w.Context.stateLock,
			Variables: variables,
		},
		InterpolaterVars:    w.interpolaterVars,
		InterpolaterVarLock: &w.interpolaterVarLock,
	}

	w.contexts[key] = ctx
	return ctx
}

func (w *ContextGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode {
	log.Printf("[TRACE] [%s] Entering eval tree: %s",
		w.Operation, dag.VertexName(v))

	// Acquire a lock on the semaphore
	w.Context.parallelSem.Acquire()

	// We want to filter the evaluation tree to only include operations
	// that belong in this operation.
	return EvalFilter(n, EvalNodeFilterOp(w.Operation))
}

func (w *ContextGraphWalker) ExitEvalTree(
	v dag.Vertex, output interface{}, err error) error {
	log.Printf("[TRACE] [%s] Exiting eval tree: %s",
		w.Operation, dag.VertexName(v))

	// Release the semaphore
	w.Context.parallelSem.Release()

	if err == nil {
		return nil
	}

	// Acquire the lock because anything is going to require a lock.
	w.errorLock.Lock()
	defer w.errorLock.Unlock()

	// Try to get a validation error out of it. If its not a validation
	// error, then just record the normal error.
	verr, ok := err.(*EvalValidateError)
	if !ok {
		return err
	}

	for _, msg := range verr.Warnings {
		w.ValidationWarnings = append(
			w.ValidationWarnings,
			fmt.Sprintf("%s: %s", dag.VertexName(v), msg))
	}
	for _, e := range verr.Errors {
		w.ValidationErrors = append(
			w.ValidationErrors,
			errwrap.Wrapf(fmt.Sprintf("%s: {{err}}", dag.VertexName(v)), e))
	}

	return nil
}

func (w *ContextGraphWalker) init() {
	w.contexts = make(map[string]*BuiltinEvalContext, 5)
	w.providerCache = make(map[string]ResourceProvider, 5)
	w.providerConfigCache = make(map[string]*ResourceConfig, 5)
	w.provisionerCache = make(map[string]ResourceProvisioner, 5)
	w.interpolaterVars = make(map[string]map[string]string, 5)
}
<|endoftext|>


<commit_before>package shard

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sync"

	"github.com/unigraph/rdb"
)

var ShardNameFn = func(i uint) string { return fmt.Sprintf("%03d", i) }

type Shard struct {
	dbs []*rdb.DB
}

func Open(opts *rdb.Options, name string, shardsNum uint) (*Shard, error) {
	if err := checkValid(name, shardsNum); err != nil {
		return nil, err
	}
	s := &Shard{}
	for i := uint(0); i < shardsNum; i++ {
		sName := filepath.Join(name, ShardNameFn(i))
		db, err := rdb.OpenDb(opts, sName)
		if err != nil {
			s.Close()
			return nil, err
		}
		s.dbs = append(s.dbs, db)
	}
	return s, nil
}

func OpenForReadOnly(opts *rdb.Options, name string, shardsNum uint, errorIfLogFileExist bool) (*Shard, error) {
	if err := checkValid(name, shardsNum); err != nil {
		return nil, err
	}
	s := &Shard{}
	for i := uint(0); i < shardsNum; i++ {
		sName := filepath.Join(name, ShardNameFn(i))
		db, err := rdb.OpenDbForReadOnly(opts, sName, errorIfLogFileExist)
		if err != nil {
			s.Close()
			return nil, err
		}
		s.dbs = append(s.dbs, db)
	}
	return s, nil
}

type errors []error

func (e errors) Error() string {
	res := ""
	for _, err := range e {
		res += err.Error()
	}
	return res
}

func (s *Shard) Flush(opts *rdb.FlushOptions) error {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	err := errors(nil)
	l := sync.RWMutex{}
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			if e := db.Flush(opts); e != nil {
				l.Lock()
				err = append(err, e)
				l.Unlock()
			}
			wg.Done()
		}(db)
	}
	wg.Wait()
	if len(err) == 0 {
		return nil
	}
	return err
}

func (s *Shard) CompactRange(r rdb.Range) {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			db.CompactRange(r)
			wg.Done()
		}(db)
	}
	wg.Wait()
}

func (s *Shard) DBs() []*rdb.DB {
	return append([]*rdb.DB(nil), s.dbs...)
}

func (s *Shard) Close() {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			db.Close()
			wg.Done()
		}(db)
	}
	wg.Wait()
}

func GetShardNum(name string) uint {
	if files, err := ioutil.ReadDir(name); os.IsNotExist(err) {
		return 0
	} else {
		shards := map[string]bool{}
		for _, file := range files {
			if matched, _ := regexp.MatchString(`\d{3}`, file.Name()); matched {
				shards[file.Name()] = true
			}
		}
		return uint(len(shards))
	}
}

func checkValid(name string, shardsNum uint) error {
	if shardsNum == 0 || shardsNum > 999 {
		return fmt.Errorf("Number of shards has to be bigger than 0 and lower than 1000")
	}
	files, err := ioutil.ReadDir(name)
	if os.IsNotExist(err) { // does not exists, let's create empty
		return os.Mkdir(name, 0700)
	} else if err != nil { // some other error related to ReadDir
		return err
	} else { // exists, let's check the content
		shards := map[string]bool{}
		for _, file := range files {
			if matched, _ := regexp.MatchString(`\d{3}`, file.Name()); matched {
				shards[file.Name()] = true
			}
		}
		if len(shards) != 0 {
			if uint(len(shards)) != shardsNum {
				return fmt.Errorf("Wrong number of shards provided (%v)", len(shards))
			}
			for i := uint(0); i < shardsNum; i++ {
				sName := ShardNameFn(i)
				if !shards[sName] {
					return fmt.Errorf("Wrong number of shards provided (%v)", len(shards))
				}
			}
		}
	}
	return nil
}
<commit_msg>update<commit_after>package shard

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"sync"

	"github.com/unigraph/rdb"
)

var ShardNameFn = func(i uint) string { return fmt.Sprintf("%03d", i) }

type Shard struct {
	dbs []*rdb.DB
}

func Open(opts *rdb.Options, name string, shardsNum uint) (*Shard, error) {
	if err := checkValid(name, shardsNum); err != nil {
		return nil, err
	}
	s := &Shard{}
	for i := uint(0); i < shardsNum; i++ {
		sName := filepath.Join(name, ShardNameFn(i))
		db, err := rdb.OpenDb(opts, sName)
		if err != nil {
			s.Close()
			return nil, err
		}
		s.dbs = append(s.dbs, db)
	}
	return s, nil
}

func OpenForReadOnly(opts *rdb.Options, name string, shardsNum uint, errorIfLogFileExist bool) (*Shard, error) {
	if err := checkValid(name, shardsNum); err != nil {
		return nil, err
	}
	s := &Shard{}
	for i := uint(0); i < shardsNum; i++ {
		sName := filepath.Join(name, ShardNameFn(i))
		db, err := rdb.OpenDbForReadOnly(opts, sName, errorIfLogFileExist)
		if err != nil {
			s.Close()
			return nil, err
		}
		s.dbs = append(s.dbs, db)
	}
	return s, nil
}

type errors []error

func (e errors) Error() string {
	res := ""
	for _, err := range e {
		res += err.Error()
	}
	return res
}

func (s *Shard) Flush(opts *rdb.FlushOptions) error {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	err := errors(nil)
	l := sync.RWMutex{}
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			if e := db.Flush(opts); e != nil {
				l.Lock()
				err = append(err, e)
				l.Unlock()
			}
			wg.Done()
		}(db)
	}
	wg.Wait()
	if len(err) == 0 {
		return nil
	}
	return err
}

func (s *Shard) CompactRange(r rdb.Range) {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			db.CompactRange(r)
			wg.Done()
		}(db)
	}
	wg.Wait()
}

func (s *Shard) DBs() []*rdb.DB {
	return append([]*rdb.DB(nil), s.dbs...)
}

func (s *Shard) Close() {
	wg := sync.WaitGroup{}
	wg.Add(len(s.dbs))
	for _, db := range s.dbs {
		go func(db *rdb.DB) {
			db.Close()
			wg.Done()
		}(db)
	}
	wg.Wait()
}

func GetShardNum(name string) uint {
	if files, err := ioutil.ReadDir(name); os.IsNotExist(err) {
		return 0
	} else {
		shards := map[string]bool{}
		for _, file := range files {
			shards[file.Name()] = true
		}
		i := 0
		for shards[ShardNameFn(uint(i))] {
			i++
		}
		if len(shards) != i {
			return 0
		}
		return uint(i)
	}
}

func checkValid(name string, shardsNum uint) error {
	if shardsNum == 0 || shardsNum > 999 {
		return fmt.Errorf("Number of shards has to be bigger than 0 and lower than 1000")
	}
	files, err := ioutil.ReadDir(name)
	if os.IsNotExist(err) { // does not exists, let's create empty
		return os.Mkdir(name, 0700)
	} else if err != nil { // some other error related to ReadDir
		return err
	} else { // exists, let's check the content
		shards := map[string]bool{}
		for _, file := range files {
			if matched, _ := regexp.MatchString(`\d{3}`, file.Name()); matched {
				shards[file.Name()] = true
			}
		}
		if len(shards) != 0 {
			if uint(len(shards)) != shardsNum {
				return fmt.Errorf("Wrong number of shards provided (%v)", len(shards))
			}
			for i := uint(0); i < shardsNum; i++ {
				sName := ShardNameFn(i)
				if !shards[sName] {
					return fmt.Errorf("Wrong number of shards provided (%v)", len(shards))
				}
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package logger

import (
	"io"
	"os"

	log "github.com/inconshreveable/log15"
	"github.com/xgfone/go-tools/log/handler"
)

type closingHandler struct {
	io.WriteCloser
	log.Handler
}

func (h *closingHandler) Close() error {
	return h.WriteCloser.Close()
}

func TimedRotatingFileHandler(fmtr log.Format, filename string, backupCount, interval int) (h log.Handler, err error) {
	defer func() {
		if _err := recover(); _err != nil {
			err = _err.(error)
			return
		}
	}()

	_h := handler.NewTimedRotatingFile(filename)
	_h.SetBackupCount(backupCount).SetInterval(interval)

	return closingHandler{_h, log.StreamHandler(_h, fmtr)}, nil
}

type Logger struct {
	log.Logger
}

func NewLogger(level, filepath string) (logger *Logger, err error) {
	// Logger := log.New(os.Stderr, "app", log.LstdFlags|log.Lshortfile)

	var lvl log.Lvl
	if _level, _err := log.LvlFromString(level); _err != nil {
		err = _err
		return
	} else {
		lvl = _level
	}

	var handler log.Handler
	if filepath == "" {
		handler = log.StreamHandler(os.Stderr, log.LogfmtFormat())
	} else {
		handler, err = TimedRotatingFileHandler(log.LogfmtFormat(), filepath, 31, 1)
		// handler, err = log.FileHandler(filepath, log.LogfmtFormat())
		if err != nil {
			return
		}
	}
	// handler = log.SyncHandler(handler)

	//shandler := log.CallerFuncHandler(handler)
	shandler := log.CallerFileHandler(handler)
	chandler := log.CallerStackHandler("%v", handler)

	handlers := log.MultiHandler(
		log.LvlFilterHandler(log.LvlCrit, chandler),
		log.LvlFilterHandler(lvl, shandler),
	)

	_logger := log.New()
	_logger.SetHandler(handlers)

	logger = &Logger{Logger: _logger}

	return
}
<commit_msg>Remove go logger<commit_after><|endoftext|>


<commit_before>package chartplugin

import (
	"bytes"
	"fmt"
	"image/color"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/gonum/plot"
	"github.com/gonum/plot/plotter"
	"github.com/gonum/plot/plotutil"
	"github.com/gonum/plot/vg"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

type chartPlugin struct {
	bruxism.SimplePlugin

	cooldown map[string]time.Time
}

var randomDirection = []string{
	"up",
	"down",
	"flat",
}

var randomY = []string{
	"interest",
	"care",
	"success",
	"fail",
	"happiness",
	"sadness",
	"money",
}

var randomX = []string{
	"time",
	"releases",
	"days",
	"years",
}

func (p *chartPlugin) random(list []string) string {
	return list[rand.Intn(len(list))]
}

func (p *chartPlugin) randomChart(service bruxism.Service) string {
	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}

	return fmt.Sprintf("%s%schart %s %s, %s%s", ticks, service.CommandPrefix(), p.random(randomDirection), p.random(randomY), p.random(randomX), ticks)
}

func (p *chartPlugin) helpFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	help := bruxism.CommandHelp(service, "chart", "<up|down|flat> <vertical message>, <horizontal message>", "Creates a chart trending in the desired direction.")

	if detailed {
		help = append(help, []string{
			"Examples:",
			bruxism.CommandHelp(service, "chart", "down interest, time", "Creates a chart showing declining interest over time")[0],
		}...)
	}

	return help
}

func (p *chartPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if service.IsMe(message) {
		return
	}

	if !bruxism.MatchesCommand(service, "chart", message) {
		return
	}

	cooldown := p.cooldown[message.UserID()]
	if cooldown.After(time.Now()) {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, you need to wait %s before creating another chart.", message.UserName(), humanize.Time(cooldown)))
		return
	}
	p.cooldown[message.UserID()] = time.Now().Add(10 * time.Second)

	query, parts := bruxism.ParseCommand(service, message)
	if len(parts) == 0 {
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart eg: %s", p.randomChart(service)))
		return
	}

	start, end := 0.5, 0.5

	switch parts[0] {
	case "up":
		start, end = 0, 1
	case "down":
		start, end = 1, 0
	case "flat":
	case "straight":
	default:
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart direction. eg: %s", p.randomChart(service)))
		return
	}

	axes := strings.Split(query[len(parts[0]):], ",")
	if len(axes) != 2 {
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart axis labels eg: %s", p.randomChart(service)))
		return
	}

	pl, err := plot.New()
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Error making chart, sorry! eg: %s", p.randomChart(service)))
		return
	}

	service.Typing(message.Channel())

	pl.Y.Label.Text = axes[0]
	pl.X.Label.Text = axes[1]

	num := 5 + rand.Intn(15)

	start *= float64(num)
	end *= float64(num)

	pts := make(plotter.XYs, num)
	for i := range pts {
		pts[i].X = float64(i) + rand.Float64()*0.5 - 0.2
		pts[i].Y = start + float64(end-start)/float64(num-1)*float64(i) + rand.Float64()*0.5 - 0.25
	}

	pl.X.Tick.Label.Color = color.Transparent
	pl.Y.Tick.Label.Color = color.Transparent

	pl.X.Min = -0.5
	pl.X.Max = float64(num) + 0.5

	pl.Y.Min = -0.5
	pl.Y.Max = float64(num) + 0.5

	lpLine, lpPoints, err := plotter.NewLinePoints(pts)
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName()))
	}
	lpLine.Color = plotutil.Color(rand.Int())
	lpLine.Width = vg.Points(1 + 0.5*rand.Float64())
	lpLine.Dashes = plotutil.Dashes(rand.Int())
	lpPoints.Shape = plotutil.Shape(rand.Int())
	lpPoints.Color = lpLine.Color

	pl.Add(lpLine, lpPoints)

	w, err := pl.WriterTo(320, 240, "png")
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName()))
		return
	}

	b := &bytes.Buffer{}
	w.WriteTo(b)

	go func() {
		if service.Name() == bruxism.DiscordServiceName {
			discord := service.(*bruxism.Discord)
			p, err := discord.UserChannelPermissions(message.UserID(), message.Channel())
			if err == nil && p&discordgo.PermissionAttachFiles == discordgo.PermissionAttachFiles {
				service.SendFile(message.Channel(), "chart.png", b)
				return
			}
		}

		url, err := bot.UploadToImgur(b, "chart.png")
		if err != nil {
			service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem uploading the chart to imgur.", message.UserName()))
			log.Println("Error uploading chart: ", err)
			return
		}

		if service.Name() == bruxism.DiscordServiceName {
			service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart <@%s>: %s", message.UserID(), url))
		} else {
			service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart %s: %s", message.UserName(), url))
		}
	}()
}

// New will create a new chart plugin.
func New() bruxism.Plugin {
	p := &chartPlugin{
		SimplePlugin: *bruxism.NewSimplePlugin("Chart"),
		cooldown:     map[string]time.Time{},
	}
	p.MessageFunc = p.messageFunc
	p.HelpFunc = p.helpFunc
	return p
}
<commit_msg>Update gonum.<commit_after>package chartplugin

import (
	"bytes"
	"fmt"
	"image/color"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/dustin/go-humanize"
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
	"github.com/iopred/bruxism"
	"github.com/iopred/discordgo"
)

type chartPlugin struct {
	bruxism.SimplePlugin

	cooldown map[string]time.Time
}

var randomDirection = []string{
	"up",
	"down",
	"flat",
}

var randomY = []string{
	"interest",
	"care",
	"success",
	"fail",
	"happiness",
	"sadness",
	"money",
}

var randomX = []string{
	"time",
	"releases",
	"days",
	"years",
}

func (p *chartPlugin) random(list []string) string {
	return list[rand.Intn(len(list))]
}

func (p *chartPlugin) randomChart(service bruxism.Service) string {
	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}

	return fmt.Sprintf("%s%schart %s %s, %s%s", ticks, service.CommandPrefix(), p.random(randomDirection), p.random(randomY), p.random(randomX), ticks)
}

func (p *chartPlugin) helpFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	help := bruxism.CommandHelp(service, "chart", "<up|down|flat> <vertical message>, <horizontal message>", "Creates a chart trending in the desired direction.")

	if detailed {
		help = append(help, []string{
			"Examples:",
			bruxism.CommandHelp(service, "chart", "down interest, time", "Creates a chart showing declining interest over time")[0],
		}...)
	}

	return help
}

func (p *chartPlugin) messageFunc(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if service.IsMe(message) {
		return
	}

	if !bruxism.MatchesCommand(service, "chart", message) {
		return
	}

	cooldown := p.cooldown[message.UserID()]
	if cooldown.After(time.Now()) {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, you need to wait %s before creating another chart.", message.UserName(), humanize.Time(cooldown)))
		return
	}
	p.cooldown[message.UserID()] = time.Now().Add(10 * time.Second)

	query, parts := bruxism.ParseCommand(service, message)
	if len(parts) == 0 {
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart eg: %s", p.randomChart(service)))
		return
	}

	start, end := 0.5, 0.5

	switch parts[0] {
	case "up":
		start, end = 0, 1
	case "down":
		start, end = 1, 0
	case "flat":
	case "straight":
	default:
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart direction. eg: %s", p.randomChart(service)))
		return
	}

	axes := strings.Split(query[len(parts[0]):], ",")
	if len(axes) != 2 {
		service.SendMessage(message.Channel(), fmt.Sprintf("Invalid chart axis labels eg: %s", p.randomChart(service)))
		return
	}

	pl, err := plot.New()
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Error making chart, sorry! eg: %s", p.randomChart(service)))
		return
	}

	service.Typing(message.Channel())

	pl.Y.Label.Text = axes[0]
	pl.X.Label.Text = axes[1]

	num := 5 + rand.Intn(15)

	start *= float64(num)
	end *= float64(num)

	pts := make(plotter.XYs, num)
	for i := range pts {
		pts[i].X = float64(i) + rand.Float64()*0.5 - 0.2
		pts[i].Y = start + float64(end-start)/float64(num-1)*float64(i) + rand.Float64()*0.5 - 0.25
	}

	pl.X.Tick.Label.Color = color.Transparent
	pl.Y.Tick.Label.Color = color.Transparent

	pl.X.Min = -0.5
	pl.X.Max = float64(num) + 0.5

	pl.Y.Min = -0.5
	pl.Y.Max = float64(num) + 0.5

	lpLine, lpPoints, err := plotter.NewLinePoints(pts)
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName()))
	}
	lpLine.Color = plotutil.Color(rand.Int())
	lpLine.Width = vg.Points(1 + 0.5*rand.Float64())
	lpLine.Dashes = plotutil.Dashes(rand.Int())
	lpPoints.Shape = plotutil.Shape(rand.Int())
	lpPoints.Color = lpLine.Color

	pl.Add(lpLine, lpPoints)

	w, err := pl.WriterTo(320, 240, "png")
	if err != nil {
		service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem creating your chart.", message.UserName()))
		return
	}

	b := &bytes.Buffer{}
	w.WriteTo(b)

	go func() {
		if service.Name() == bruxism.DiscordServiceName {
			discord := service.(*bruxism.Discord)
			p, err := discord.UserChannelPermissions(message.UserID(), message.Channel())
			if err == nil && p&discordgo.PermissionAttachFiles == discordgo.PermissionAttachFiles {
				service.SendFile(message.Channel(), "chart.png", b)
				return
			}
		}

		url, err := bot.UploadToImgur(b, "chart.png")
		if err != nil {
			service.SendMessage(message.Channel(), fmt.Sprintf("Sorry %s, there was a problem uploading the chart to imgur.", message.UserName()))
			log.Println("Error uploading chart: ", err)
			return
		}

		if service.Name() == bruxism.DiscordServiceName {
			service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart <@%s>: %s", message.UserID(), url))
		} else {
			service.SendMessage(message.Channel(), fmt.Sprintf("Here's your chart %s: %s", message.UserName(), url))
		}
	}()
}

// New will create a new chart plugin.
func New() bruxism.Plugin {
	p := &chartPlugin{
		SimplePlugin: *bruxism.NewSimplePlugin("Chart"),
		cooldown:     map[string]time.Time{},
	}
	p.MessageFunc = p.messageFunc
	p.HelpFunc = p.helpFunc
	return p
}
<|endoftext|>


<commit_before>package parse // import "github.com/tdewolff/parse"

import (
	"errors"
	"io"
)

// MinBuf and MaxBuf are the initial and maximal internal buffer size.
var MinBuf = 1024
var MaxBuf = 1048576 // upper limit 1MB

// ErrBufferExceeded is returned when the internal buffer exceeds 4096 bytes, a string or comment must thus be smaller than 4kB!
var ErrBufferExceeded = errors.New("max buffer exceeded")

////////////////////////////////////////////////////////////////

// ShiftBuffer is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
type ShiftBuffer struct {
	r   io.Reader
	err error

	buf []byte
	pos int
	end int
}

// NewShiftBufferReader returns a new ShiftBuffer for a given io.Reader.
func NewShiftBuffer(r io.Reader) *ShiftBuffer {
	// If reader has the bytes in memory already, use that instead!
	if buffer, ok := r.(interface {
		Bytes() []byte
	}); ok {
		return &ShiftBuffer{
			err: io.EOF,
			buf: buffer.Bytes(),
		}
	}
	z := &ShiftBuffer{
		r:   r,
		buf: make([]byte, 0, MinBuf),
	}
	z.Peek(0)
	return z
}

// Err returns the error.
func (z *ShiftBuffer) Err() error {
	if z.err == io.EOF && z.end < len(z.buf) {
		return nil
	}
	return z.err
}

// IsEOF returns true when it has encountered EOF and thus loaded the last buffer in memory.
func (z *ShiftBuffer) IsEOF() bool {
	return z.err == io.EOF
}

// Move advances the 0 position of read.
func (z *ShiftBuffer) Move(n int) {
	z.end += n
}

// MoveTo sets the 0 position of read.
func (z *ShiftBuffer) MoveTo(n int) {
	z.end = z.pos + n
}

// Pos returns the 0 position of read.
func (z *ShiftBuffer) Pos() int {
	return z.end - z.pos
}

// Peek returns the ith byte and possible does a reallocation
func (z *ShiftBuffer) Peek(i int) byte {
	end := z.end + i
	if end >= len(z.buf) {
		if z.err != nil {
			return 0
		}

		// reallocate a new buffer (possibly larger)
		c := cap(z.buf)
		d := end - z.pos
		var buf1 []byte
		if 2*d > c {
			if 2*c > MaxBuf {
				z.err = ErrBufferExceeded
				return 0
			}
			buf1 = make([]byte, d, 2*c)
		} else {
			buf1 = z.buf[:d]
		}
		copy(buf1, z.buf[z.pos:end])

		// Read in to fill the buffer till capacity
		var n int
		n, z.err = z.r.Read(buf1[d:cap(buf1)])
		end -= z.pos
		z.end -= z.pos
		z.pos, z.buf = 0, buf1[:d+n]
		if n == 0 {
			return 0
		}
	}
	return z.buf[end]
}

// PeekRune returns the rune of the ith byte.
func (z *ShiftBuffer) PeekRune(i int) rune {
	// from unicode/utf8
	c := z.Peek(i)
	if c < 0xC0 {
		return rune(c)
	} else if c < 0xE0 {
		return rune(c&0x1F)<<6 | rune(z.Peek(i+1)&0x3F)
	} else if c < 0xF0 {
		return rune(c&0x0F)<<12 | rune(z.Peek(i+1)&0x3F)<<6 | rune(z.Peek(i+2)&0x3F)
	} else {
		return rune(c&0x07)<<18 | rune(z.Peek(i+1)&0x3F)<<12 | rune(z.Peek(i+2)&0x3F)<<6 | rune(z.Peek(i+3)&0x3F)
	}
}

// Bytes returns the bytes of the current selection.
func (z *ShiftBuffer) Bytes() []byte {
	return z.buf[z.pos:z.end]
}

// Shift returns the bytes of the current selection and advances the position.
func (z *ShiftBuffer) Shift() []byte {
	b := z.buf[z.pos:z.end]
	z.pos = z.end
	return b
}
<commit_msg>Skip functionality<commit_after>package parse // import "github.com/tdewolff/parse"

import (
	"errors"
	"io"
)

// MinBuf and MaxBuf are the initial and maximal internal buffer size.
var MinBuf = 1024
var MaxBuf = 1048576 // upper limit 1MB

// ErrBufferExceeded is returned when the internal buffer exceeds 4096 bytes, a string or comment must thus be smaller than 4kB!
var ErrBufferExceeded = errors.New("max buffer exceeded")

////////////////////////////////////////////////////////////////

// ShiftBuffer is a buffered reader that allows peeking forward and shifting, taking an io.Reader.
type ShiftBuffer struct {
	r   io.Reader
	err error

	buf []byte
	pos int
	end int
}

// NewShiftBufferReader returns a new ShiftBuffer for a given io.Reader.
func NewShiftBuffer(r io.Reader) *ShiftBuffer {
	// If reader has the bytes in memory already, use that instead!
	if buffer, ok := r.(interface {
		Bytes() []byte
	}); ok {
		return &ShiftBuffer{
			err: io.EOF,
			buf: buffer.Bytes(),
		}
	}
	z := &ShiftBuffer{
		r:   r,
		buf: make([]byte, 0, MinBuf),
	}
	z.Peek(0)
	return z
}

// Err returns the error.
func (z *ShiftBuffer) Err() error {
	if z.err == io.EOF && z.end < len(z.buf) {
		return nil
	}
	return z.err
}

// IsEOF returns true when it has encountered EOF and thus loaded the last buffer in memory.
func (z *ShiftBuffer) IsEOF() bool {
	return z.err == io.EOF
}

// Move advances the 0 position of read.
func (z *ShiftBuffer) Move(n int) {
	z.end += n
}

// MoveTo sets the 0 position of read.
func (z *ShiftBuffer) MoveTo(n int) {
	z.end = z.pos + n
}

// Pos returns the 0 position of read.
func (z *ShiftBuffer) Pos() int {
	return z.end - z.pos
}

// Peek returns the ith byte and possible does a reallocation
func (z *ShiftBuffer) Peek(i int) byte {
	end := z.end + i
	if end >= len(z.buf) {
		if z.err != nil {
			return 0
		}

		// reallocate a new buffer (possibly larger)
		c := cap(z.buf)
		d := end - z.pos
		var buf1 []byte
		if 2*d > c {
			if 2*c > MaxBuf {
				z.err = ErrBufferExceeded
				return 0
			}
			buf1 = make([]byte, d, 2*c)
		} else {
			buf1 = z.buf[:d]
		}
		copy(buf1, z.buf[z.pos:end])

		// Read in to fill the buffer till capacity
		var n int
		n, z.err = z.r.Read(buf1[d:cap(buf1)])
		end -= z.pos
		z.end -= z.pos
		z.pos, z.buf = 0, buf1[:d+n]
		if n == 0 {
			return 0
		}
	}
	return z.buf[end]
}

// PeekRune returns the rune of the ith byte.
func (z *ShiftBuffer) PeekRune(i int) rune {
	// from unicode/utf8
	c := z.Peek(i)
	if c < 0xC0 {
		return rune(c)
	} else if c < 0xE0 {
		return rune(c&0x1F)<<6 | rune(z.Peek(i+1)&0x3F)
	} else if c < 0xF0 {
		return rune(c&0x0F)<<12 | rune(z.Peek(i+1)&0x3F)<<6 | rune(z.Peek(i+2)&0x3F)
	} else {
		return rune(c&0x07)<<18 | rune(z.Peek(i+1)&0x3F)<<12 | rune(z.Peek(i+2)&0x3F)<<6 | rune(z.Peek(i+3)&0x3F)
	}
}

// Bytes returns the bytes of the current selection.
func (z *ShiftBuffer) Bytes() []byte {
	return z.buf[z.pos:z.end]
}

// Shift returns the bytes of the current selection and collapses the position.
func (z *ShiftBuffer) Shift() []byte {
	b := z.buf[z.pos:z.end]
	z.pos = z.end
	return b
}

// Skip collapses the position.
func (z *ShiftBuffer) Skip() {
	z.pos = z.end
}
<|endoftext|>