<commit_before>package core

import (
	"time"
)

type JobPerf struct {
	Id     string               `bson:"id" json:"id"`
	Queued int64                `bson:"queued" json:"queued"`
	Start  int64                `bson:"start" json:"start"`
	End    int64                `bson:"end" json:"end"`
	Resp   int64                `bson:"resp" json:"resp"` //End - Queued
	Ptasks map[string]*TaskPerf `bson:"task_stats" json: "task_stats"`
	Pworks map[string]*WorkPerf `bson:"work_stats" json: "work_stats"`
}

type TaskPerf struct {
	Queued       int64   `bson:"queued" json:"queued"`
	Start        int64   `bson:"start" json:"start"`
	End          int64   `bson:"end" json:"end"`
	Resp         int64   `bson:"resp" json:"resp"` //End -Queued
	InFileSizes  []int64 `bson:"size_infile" json: "size_infile"`
	OutFileSizes []int64 `bson:"size_outfile" json: "size_outfile"`
}

type WorkPerf struct {
	Queued      int64  `bson:"queued" json:"queued"`               // WQ (queued at server or client, depending on who creates it)
	Done        int64  `bson:"done" json:"done"`                   // WD (done at server)
	Resp        int64  `bson:"resp" json:"resp"`                   // Done - Queued (server metric)
	Checkout    int64  `bson:"checkout" json:"checkout"`           // checkout at client
	Deliver     int64  `bson:"deliver" json:"deliver"`             // done at client
	ClientResp  int64  `bson:"clientresp" json:"clientresp"`       // Deliver - Checkout (client metric)
	DataIn      int64  `bson:"time_data_in" json:"time_data_in"`   // input data move-in at client
	DataOut     int64  `bson:"time_data_out" json:"time_data_out"` // output data move-out at client
	Runtime     int64  `bson:"runtime" json:"runtime"`             // computing time at client
	MaxMemUsage uint64 `bson:"max_mem_usage" json:"max_mem_usage"` // maxium memery consumption
	ClientId    string `bson:"client_id" json:"client_id"`
	PreDataSize int64  `bson:"size_predata" json:"size_predata"`  //predata moved over network
	InFileSize  int64  `bson:"size_infile" json: "size_infile"`   //input file moved over network
	OutFileSize int64  `bson:"size_outfile" json: "size_outfile"` //outpuf file moved over network
}

func NewJobPerf(id string) *JobPerf {
	return &JobPerf{
		Id:     id,
		Queued: time.Now().Unix(),
		Ptasks: map[string]*TaskPerf{},
		Pworks: map[string]*WorkPerf{},
	}
}

func NewTaskPerf(id string) *TaskPerf {
	return &TaskPerf{
		Queued:       time.Now().Unix(),
		InFileSizes:  []int64{},
		OutFileSizes: []int64{},
	}
}

func NewWorkPerf(id string) *WorkPerf {
	return &WorkPerf{
		Queued: time.Now().Unix(),
	}
}
<commit_msg>typo fix<commit_after>package core

import (
	"time"
)

type JobPerf struct {
	Id     string               `bson:"id" json:"id"`
	Queued int64                `bson:"queued" json:"queued"`
	Start  int64                `bson:"start" json:"start"`
	End    int64                `bson:"end" json:"end"`
	Resp   int64                `bson:"resp" json:"resp"` //End - Queued
	Ptasks map[string]*TaskPerf `bson:"task_stats" json:"task_stats"`
	Pworks map[string]*WorkPerf `bson:"work_stats" json:"work_stats"`
}

type TaskPerf struct {
	Queued       int64   `bson:"queued" json:"queued"`
	Start        int64   `bson:"start" json:"start"`
	End          int64   `bson:"end" json:"end"`
	Resp         int64   `bson:"resp" json:"resp"` //End -Queued
	InFileSizes  []int64 `bson:"size_infile" json:"size_infile"`
	OutFileSizes []int64 `bson:"size_outfile" json:"size_outfile"`
}

type WorkPerf struct {
	Queued      int64  `bson:"queued" json:"queued"`               // WQ (queued at server or client, depending on who creates it)
	Done        int64  `bson:"done" json:"done"`                   // WD (done at server)
	Resp        int64  `bson:"resp" json:"resp"`                   // Done - Queued (server metric)
	Checkout    int64  `bson:"checkout" json:"checkout"`           // checkout at client
	Deliver     int64  `bson:"deliver" json:"deliver"`             // done at client
	ClientResp  int64  `bson:"clientresp" json:"clientresp"`       // Deliver - Checkout (client metric)
	DataIn      int64  `bson:"time_data_in" json:"time_data_in"`   // input data move-in at client
	DataOut     int64  `bson:"time_data_out" json:"time_data_out"` // output data move-out at client
	Runtime     int64  `bson:"runtime" json:"runtime"`             // computing time at client
	MaxMemUsage uint64 `bson:"max_mem_usage" json:"max_mem_usage"` // maxium memery consumption
	ClientId    string `bson:"client_id" json:"client_id"`
	PreDataSize int64  `bson:"size_predata" json:"size_predata"` //predata moved over network
	InFileSize  int64  `bson:"size_infile" json:"size_infile"`   //input file moved over network
	OutFileSize int64  `bson:"size_outfile" json:"size_outfile"` //outpuf file moved over network
}

func NewJobPerf(id string) *JobPerf {
	return &JobPerf{
		Id:     id,
		Queued: time.Now().Unix(),
		Ptasks: map[string]*TaskPerf{},
		Pworks: map[string]*WorkPerf{},
	}
}

func NewTaskPerf(id string) *TaskPerf {
	return &TaskPerf{
		Queued:       time.Now().Unix(),
		InFileSizes:  []int64{},
		OutFileSizes: []int64{},
	}
}

func NewWorkPerf(id string) *WorkPerf {
	return &WorkPerf{
		Queued: time.Now().Unix(),
	}
}
<|endoftext|>


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

package pagerduty

import (
	"errors"
	"os"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/zclconf/go-cty/cty"
)

type PagerDutyProvider struct { //nolint
	terraformutils.Provider
	token string
}

func (p *PagerDutyProvider) Init(args []string) error {
	if token := os.Getenv("PAGERDUTY_TOKEN"); token != "" {
		p.token = os.Getenv("PAGERDUTY_TOKEN")
	}
	if len(args) > 0 {
		p.token = args[0]
	}
	return nil
}

func (p *PagerDutyProvider) GetName() string {
	return "pagerduty"
}

func (p *PagerDutyProvider) GetConfig() cty.Value {
	return cty.ObjectVal(map[string]cty.Value{
		"token": cty.StringVal(p.token),
	})
}

func (p *PagerDutyProvider) GetProviderData(arg ...string) map[string]interface{} {
	return map[string]interface{}{
		"provider": map[string]interface{}{
			"pagerduty": map[string]interface{}{
				"token": p.token,
			},
		},
	}
}

func (PagerDutyProvider) GetResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{}
}

func (p *PagerDutyProvider) GetSupportedService() map[string]terraformutils.ServiceGenerator {
	return map[string]terraformutils.ServiceGenerator{
		"business_service":  &BusinessServiceGenerator{},
		"escalation_policy": &EscalationPolicyGenerator{},
		"ruleset":           &RulesetGenerator{},
		"schedule":          &ScheduleGenerator{},
		"service":           &ServiceGenerator{},
		"team":              &TeamGenerator{},
		"user":              &UserGenerator{},
	}
}

func (p *PagerDutyProvider) InitService(serviceName string, verbose bool) error {
	var isSupported bool
	if _, isSupported = p.GetSupportedService()[serviceName]; !isSupported {
		return errors.New(p.GetName() + ": " + serviceName + " not supported service")
	}
	p.Service = p.GetSupportedService()[serviceName]
	p.Service.SetName(serviceName)
	p.Service.SetVerbose(verbose)
	p.Service.SetProviderName(p.GetName())
	p.Service.SetArgs(map[string]interface{}{
		"token": p.token,
	})
	return nil
}
<commit_msg>fix to accept PAGERDUTY_TOKEN in env for PagerDuty (#1035)<commit_after>// Copyright 2019 The Terraformer Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pagerduty

import (
	"errors"
	"os"

	"github.com/GoogleCloudPlatform/terraformer/terraformutils"
	"github.com/zclconf/go-cty/cty"
)

type PagerDutyProvider struct { //nolint
	terraformutils.Provider
	token string
}

func (p *PagerDutyProvider) Init(args []string) error {
	if token := os.Getenv("PAGERDUTY_TOKEN"); token != "" {
		p.token = os.Getenv("PAGERDUTY_TOKEN")
	}
	if len(args) > 0 && args[0] != "" {
		p.token = args[0]
	}
	return nil
}

func (p *PagerDutyProvider) GetName() string {
	return "pagerduty"
}

func (p *PagerDutyProvider) GetConfig() cty.Value {
	return cty.ObjectVal(map[string]cty.Value{
		"token": cty.StringVal(p.token),
	})
}

func (p *PagerDutyProvider) GetProviderData(arg ...string) map[string]interface{} {
	return map[string]interface{}{
		"provider": map[string]interface{}{
			"pagerduty": map[string]interface{}{
				"token": p.token,
			},
		},
	}
}

func (PagerDutyProvider) GetResourceConnections() map[string]map[string][]string {
	return map[string]map[string][]string{}
}

func (p *PagerDutyProvider) GetSupportedService() map[string]terraformutils.ServiceGenerator {
	return map[string]terraformutils.ServiceGenerator{
		"business_service":  &BusinessServiceGenerator{},
		"escalation_policy": &EscalationPolicyGenerator{},
		"ruleset":           &RulesetGenerator{},
		"schedule":          &ScheduleGenerator{},
		"service":           &ServiceGenerator{},
		"team":              &TeamGenerator{},
		"user":              &UserGenerator{},
	}
}

func (p *PagerDutyProvider) InitService(serviceName string, verbose bool) error {
	var isSupported bool
	if _, isSupported = p.GetSupportedService()[serviceName]; !isSupported {
		return errors.New(p.GetName() + ": " + serviceName + " not supported service")
	}
	p.Service = p.GetSupportedService()[serviceName]
	p.Service.SetName(serviceName)
	p.Service.SetVerbose(verbose)
	p.Service.SetProviderName(p.GetName())
	p.Service.SetArgs(map[string]interface{}{
		"token": p.token,
	})
	return nil
}
<|endoftext|>


<commit_before>/*
Package middleware provides a customizable Kayvee logging middleware for HTTP servers.

	logHandler := New(myHandler, myLogger, func(req *http.Request) map[string]interface{} {
		// Add Gorilla mux vars to the log, just because
		return mux.Vars(req)
	})

*/
package middleware

import (
	"net/http"
	"time"

	"gopkg.in/Clever/kayvee-go.v4/logger"
)

var defaultHandler = func(req *http.Request) map[string]interface{} {
	return map[string]interface{}{
		"method": req.Method,
		"path":   req.URL.Path,
		"params": req.URL.RawQuery,
		"ip":     getIP(req),
	}
}

type logHandler struct {
	handlers []func(req *http.Request) map[string]interface{}
	h        http.Handler
	logger   *logger.Logger
}

func (l *logHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	start := time.Now()

	// inject the logger into req.Context
	req = req.WithContext(logger.NewContext(req.Context(), l.logger))

	lrw := &loggedResponseWriter{
		status:         200,
		ResponseWriter: w,
		length:         0,
	}
	l.h.ServeHTTP(lrw, req)
	duration := time.Since(start)

	data := l.applyHandlers(req, map[string]interface{}{
		"response-time": duration,
		"response-size": lrw.length,
		"status-code":   lrw.status,
		"via":           "kayvee-middleware",
	})

	switch logLevelFromStatus(lrw.status) {
	case logger.Error:
		l.logger.ErrorD("request-finished", data)
	default:
		l.logger.InfoD("request-finished", data)
	}
}

func (l *logHandler) applyHandlers(req *http.Request, finalizer map[string]interface{}) map[string]interface{} {
	result := map[string]interface{}{}
	writeData := func(data map[string]interface{}) {
		for key, val := range data {
			result[key] = val
		}
	}

	for _, handler := range l.handlers {
		writeData(handler(req))
	}
	// Write reserved fields last to make sure nothing overwrites them
	writeData(defaultHandler(req))
	writeData(finalizer)

	return result
}

// New takes in an http Handler to wrap with logging, the logger to use, and any amount of
// optional handlers to customize the data that's logged.
func New(h http.Handler, logger *logger.Logger, handlers ...func(*http.Request) map[string]interface{}) http.Handler {
	return &logHandler{
		logger:   logger,
		handlers: handlers,
		h:        h,
	}
}

// HeaderHandler takes in any amount of headers and returns a handler that adds those headers.
func HeaderHandler(headers ...string) func(*http.Request) map[string]interface{} {
	return func(req *http.Request) map[string]interface{} {
		result := map[string]interface{}{}
		for _, header := range headers {
			if val := req.Header.Get(header); val != "" {
				result[header] = val
			}
		}
		return result
	}
}

type loggedResponseWriter struct {
	status int
	http.ResponseWriter
	length int
}

func (w *loggedResponseWriter) WriteHeader(code int) {
	w.status = code
	w.ResponseWriter.WriteHeader(code)
}

func (w *loggedResponseWriter) Write(b []byte) (int, error) {
	n, err := w.ResponseWriter.Write(b)
	w.length += n
	return n, err
}

func getIP(req *http.Request) string {
	forwarded := req.Header.Get("X-Forwarded-For")
	if forwarded != "" {
		return forwarded
	}
	return req.RemoteAddr
}

func logLevelFromStatus(status int) logger.LogLevel {
	if status >= 499 {
		return logger.Error
	}
	return logger.Info
}
<commit_msg>read and set canary flag on middleware messages<commit_after>/*
Package middleware provides a customizable Kayvee logging middleware for HTTP servers.

	logHandler := New(myHandler, myLogger, func(req *http.Request) map[string]interface{} {
		// Add Gorilla mux vars to the log, just because
		return mux.Vars(req)
	})

*/
package middleware

import (
	"net/http"
	"os"
	"time"

	"gopkg.in/Clever/kayvee-go.v4/logger"
)

var defaultHandler = func(req *http.Request) map[string]interface{} {
	return map[string]interface{}{
		"method": req.Method,
		"path":   req.URL.Path,
		"params": req.URL.RawQuery,
		"ip":     getIP(req),
	}
}

type logHandler struct {
	handlers []func(req *http.Request) map[string]interface{}
	h        http.Handler
	logger   *logger.Logger
	isCanary bool
}

func (l *logHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	start := time.Now()

	// inject the logger into req.Context
	req = req.WithContext(logger.NewContext(req.Context(), l.logger))

	lrw := &loggedResponseWriter{
		status:         200,
		ResponseWriter: w,
		length:         0,
	}
	l.h.ServeHTTP(lrw, req)
	duration := time.Since(start)

	data := l.applyHandlers(req, map[string]interface{}{
		"response-time": duration,
		"response-size": lrw.length,
		"status-code":   lrw.status,
		"via":           "kayvee-middleware",
		"canary":        l.isCanary,
	})

	switch logLevelFromStatus(lrw.status) {
	case logger.Error:
		l.logger.ErrorD("request-finished", data)
	default:
		l.logger.InfoD("request-finished", data)
	}
}

func (l *logHandler) applyHandlers(req *http.Request, finalizer map[string]interface{}) map[string]interface{} {
	result := map[string]interface{}{}
	writeData := func(data map[string]interface{}) {
		for key, val := range data {
			result[key] = val
		}
	}

	for _, handler := range l.handlers {
		writeData(handler(req))
	}
	// Write reserved fields last to make sure nothing overwrites them
	writeData(defaultHandler(req))
	writeData(finalizer)

	return result
}

// New takes in an http Handler to wrap with logging, the logger to use, and any amount of
// optional handlers to customize the data that's logged.
func New(h http.Handler, logger *logger.Logger, handlers ...func(*http.Request) map[string]interface{}) http.Handler {
	isCanary := false

	canaryFlag := os.Getenv("_CANARY")
	if canaryFlag == "1" {
		isCanary = true
	}
	return &logHandler{
		logger:   logger,
		handlers: handlers,
		h:        h,
		isCanary: isCanary,
	}
}

// HeaderHandler takes in any amount of headers and returns a handler that adds those headers.
func HeaderHandler(headers ...string) func(*http.Request) map[string]interface{} {
	return func(req *http.Request) map[string]interface{} {
		result := map[string]interface{}{}
		for _, header := range headers {
			if val := req.Header.Get(header); val != "" {
				result[header] = val
			}
		}
		return result
	}
}

type loggedResponseWriter struct {
	status int
	http.ResponseWriter
	length int
}

func (w *loggedResponseWriter) WriteHeader(code int) {
	w.status = code
	w.ResponseWriter.WriteHeader(code)
}

func (w *loggedResponseWriter) Write(b []byte) (int, error) {
	n, err := w.ResponseWriter.Write(b)
	w.length += n
	return n, err
}

func getIP(req *http.Request) string {
	forwarded := req.Header.Get("X-Forwarded-For")
	if forwarded != "" {
		return forwarded
	}
	return req.RemoteAddr
}

func logLevelFromStatus(status int) logger.LogLevel {
	if status >= 499 {
		return logger.Error
	}
	return logger.Info
}
<|endoftext|>


<commit_before>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]string

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// geoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type geoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop and
// checks if the loop is CCW.
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	origin := s2.OriginPoint()
	for i := range pts {
		j := 1 + i
		k := 2 + i
		if i == len(pts)-2 {
			k = 0
		}
		if i == len(pts)-1 {
			j = 0
			k = 1
		}
		if !s2.OrderedCCW(pts[i], pts[j], pts[k], origin) {
			err := fmt.Errorf("Polygon not ordered")
			return *s2.LoopFromPoints(pts), err
		}
	}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (pp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range pp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	f.Properties = prop
	f.Geometry = polygon
	return f
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = "true"
	f.Properties = prop
	f.Geometry = polygon
	return
}

// transforms a loop to a sorted cellID collection
func loopCoverer(loop s2.Loop, precision int) []uint64 {
	debug := false
	// now for bounds
	rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: precision, MaxCells: 100}
	r := s2.Region(loop.RectBound())
	covering := rc.Covering(r)
	// now approximate the bounding rect
	var polygon []uint64
	for _, val := range covering {
		polygon = append(polygon, uint64(val))
	}
	// NOTE ++ TODO
	// is this 100% correct ?
	// in theory it is.
	sort.Sort(intArray(polygon))
	// NOTE this will be the actual polygonsponse
	// when I/google finish updating the s2 library
	// right now the region coverer for loops
	// is buggy in the sense that it reutns
	// just the boundary of the loop.
	// by setting debug to true, one can
	// visualize the json
	if debug {
		min := polygon[0]
		max := polygon[len(polygon)-1]
		rc := &s2.RegionCoverer{MinLevel: 15, MaxLevel: 30, MaxCells: 50}
		covering := rc.Covering(s2.Region(loop))
		var boundary []uint64
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		for _, val := range boundary {
			if val > max {
				log.Printf("strange %v", val)
			}
			if val < min {
				log.Printf("strange %v", val)
			}
		}
		return boundary
	}
	return polygon
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (pp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = pp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon := loopCoverer(loop, precision)
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	for _, id := range polygon {
		feature := cellIDToPolygon(id)
		features = append(features, feature)
	}
	bbox := boundingbox(inner)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (pp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := pp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id)
			features = append(features, feature)
		}
	}
	// add bbox
	for _, inner := range loops {
		bbox := boundingbox(inner)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<commit_msg>Comment CCW check  because it's b0rken.<commit_after>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]string

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// geoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type geoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop and
// checks if the loop is CCW.
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	//origin := s2.OriginPoint()
	//for i := range pts {
	//j := 1 + i
	//k := 2 + i
	//if i == len(pts)-2 {
	//k = 0
	//}
	//if i == len(pts)-1 {
	//j = 0
	//k = 1
	//}
	//if !s2.OrderedCCW(pts[i], pts[j], pts[k], origin) {
	//err := fmt.Errorf("Polygon not ordered")
	//log.Errorf("%v,%v,%v, %v", i, j, k, err)
	////return *s2.LoopFromPoints(pts), err
	//}
	//}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (pp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range pp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	f.Properties = prop
	f.Geometry = polygon
	return f
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = "true"
	f.Properties = prop
	f.Geometry = polygon
	return
}

// transforms a loop to a sorted cellID collection
func loopCoverer(loop s2.Loop, precision int) []uint64 {
	debug := false
	// now for bounds
	rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: precision, MaxCells: 100}
	r := s2.Region(loop.RectBound())
	covering := rc.Covering(r)
	// now approximate the bounding rect
	var polygon []uint64
	for _, val := range covering {
		polygon = append(polygon, uint64(val))
	}
	// NOTE ++ TODO
	// is this 100% correct ?
	// in theory it is.
	sort.Sort(intArray(polygon))
	// NOTE this will be the actual polygonsponse
	// when I/google finish updating the s2 library
	// right now the region coverer for loops
	// is buggy in the sense that it reutns
	// just the boundary of the loop.
	// by setting debug to true, one can
	// visualize the json
	if debug {
		min := polygon[0]
		max := polygon[len(polygon)-1]
		rc := &s2.RegionCoverer{MinLevel: 15, MaxLevel: 30, MaxCells: 50}
		covering := rc.Covering(s2.Region(loop))
		var boundary []uint64
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		for _, val := range boundary {
			if val > max {
				log.Printf("strange %v", val)
			}
			if val < min {
				log.Printf("strange %v", val)
			}
		}
		return boundary
	}
	return polygon
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (pp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = pp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon := loopCoverer(loop, precision)
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	for _, id := range polygon {
		feature := cellIDToPolygon(id)
		features = append(features, feature)
	}
	bbox := boundingbox(inner)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (pp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := pp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id)
			features = append(features, feature)
		}
	}
	// add bbox
	for _, inner := range loops {
		bbox := boundingbox(inner)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<|endoftext|>


<commit_before>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"math/rand"
	"sort"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

// Lon returns the longitude
func (c Coordinate) Lon() float64 {
	return c[0]
}

// Lat returns the latitude
func (c Coordinate) Lat() float64 {
	return c[1]
}

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// IsCW checks if the Coordinates are CW
// it can return true also in edge cases
func (cc Coordinates) IsCW() bool {
	sum := 0.0
	for i, c := range cc[:len(cc)-1] {
		n := cc[i+1]
		sum += (n.Lon() - c.Lon()) * (n.Lat() + c.Lat())
	}
	return sum > 0
}

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]interface{}

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// GeoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type GeoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop
// return error if coordinates are not in CCW order
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	if cc.IsCW() {
		err := fmt.Errorf("Coordinates are not CCW winded")
		return *s2.LoopFromPoints(pts), err
	}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	color := randomCOlor()
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id, color)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (mp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range mp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64, color string) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	prop["fill-opacity"] = 0.2
	prop["fill"] = color
	prop["stroke-width"] = 0
	f.Properties = prop
	f.Geometry = polygon
	return f
}

// randomCOlor return a random hex color
func randomCOlor() string {
	n := 6
	const letters = "1234567890"
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return fmt.Sprintf("#%v", string(b))
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop, id int) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = fmt.Sprint(id)
	f.Properties = prop
	f.Geometry = polygon
	return
}

func loopCoverer(loop s2.Loop, precision int) ([]uint64, error) {
	var boundary intArray
	for i := precision; i < 30; i++ {
		rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: i, MaxCells: 500000}
		covering := rc.InteriorCovering(s2.Region(loop))
		log.Debug("done creating cover")
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		// crude check to make sure we get enough covering
		if len(boundary) > 4 {
			sort.Sort(boundary)
			return boundary, nil
		}
		log.Warnf("Need to upscale precision to %v", i+1)
	}
	return boundary, fmt.Errorf("Can't cover region.")
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon, err := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (mp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = mp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon, err := loopCoverer(loop, precision)
		if err != nil {
			return polygons, loops, err
		}
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	color := randomCOlor()
	for _, id := range polygon {
		feature := cellIDToPolygon(id, color)
		features = append(features, feature)
	}
	bbox := boundingbox(inner, 0)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (mp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := mp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	color := randomCOlor()
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id, color)
			features = append(features, feature)
		}
	}
	//add bbox
	for i, inner := range loops {
		bbox := boundingbox(inner, i)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<commit_msg>Add more logging with time spend calculating covering.<commit_after>// Package geoJSON converts geoJSON TO s2
// 2016 giulio <giulioungaretti@me.com>
package geoJSON

import (
	"fmt"
	"math/rand"
	"sort"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/giulioungaretti/geo/s2"
	"github.com/giulioungaretti/globo/point"
)

//Coordinate is a [longitude, latitude]
type Coordinate [2]float64

// Lon returns the longitude
func (c Coordinate) Lon() float64 {
	return c[0]
}

// Lat returns the latitude
func (c Coordinate) Lat() float64 {
	return c[1]
}

//Coordinates is a set of coordinate
type Coordinates []Coordinate

// IsCW checks if the Coordinates are CW
// it can return true also in edge cases
func (cc Coordinates) IsCW() bool {
	sum := 0.0
	for i, c := range cc[:len(cc)-1] {
		n := cc[i+1]
		sum += (n.Lon() - c.Lon()) * (n.Lat() + c.Lat())
	}
	return sum > 0
}

// Point rapresent a geoJSON point geometry object
type Point struct {
	Type       string `json:"type"`
	Coordinate `json:"coordinates"`
}

// Polygon rapresent a geoJSON polygon geometry object
type Polygon struct {
	Type string        `json:"type"`
	C    []Coordinates `json:"coordinates"`
}

// MultiPolygon rapresent a geoJSON mulitpolygon  geometry object
type MultiPolygon struct {
	Type string          `json:"type"`
	C    [][]Coordinates `json:"coordinates"`
}

// Prop is a geoJSON property
type Prop map[string]interface{}

// Feature is a geoJSON feature
type Feature struct {
	Type       string      `json:"type"`
	Geometry   interface{} `json:"geometry"`
	Properties Prop        `json:"properties"`
}

//FeatureCollection is a geoJSON Feature colelction
type FeatureCollection struct {
	Type string    `json:"type"`
	Feat []Feature `json:"features"`
}

// GeoJSON is the interface that allows any geojson to be unmarshaled
// converted to s2, and marhsaled back to be visualized
type GeoJSON interface {
	ToS2(int) ([][]uint64, []s2.Loop, error)
	ToGeoJSON(int) (FeatureCollection, error)
}

func (c Coordinate) tos2point() s2.Point {
	ll := s2.LatLngFromDegrees(c[1], c[0])
	p := s2.PointFromLatLng(ll)
	return p
}

// IsValid vaildates coordinates (approximate)
func (c Coordinate) IsValid() bool {
	if c[0] == 0 || c[1] == 0 {
		return false
	}
	return true
}

// tos2 transforms Coordinates to a s2 loop
// return error if coordinates are not in CCW order
func (cc Coordinates) tos2() (s2.Loop, error) {
	pts := []s2.Point{}
	if cc.IsCW() {
		err := fmt.Errorf("Coordinates are not CCW winded")
		return *s2.LoopFromPoints(pts), err
	}
	for _, c := range cc {
		p := c.tos2point()
		pts = append(pts, p)
	}
	return *s2.LoopFromPoints(pts), nil
}

// ToS2 converts a Point to S2Cellid.
// If precision is different than 30 (max) then the
// parent cellid at specified level is returned.
// levels go from 0 to 30:
// for reference :
// 30 covers 0.48cm2
// 12 covers 3.31km2
// 0 covers 85,011,012 km2
func (p Point) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	c := p.Coordinate
	ll := s2.LatLngFromDegrees(c[1], c[0])
	cellID := s2.CellIDFromLatLng(ll)
	// approximate returns
	if precision != 30 {
		log.Debug("Rounding down")
		return [][]uint64{[]uint64{uint64(cellID.Parent(precision))}}, loops, nil
	}
	return [][]uint64{[]uint64{uint64(cellID)}}, loops, nil
}

// ToGeoJSON converts point to geoJSON
func (p Point) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	// ingore loops, as a point has obviously no loops
	in, _, err := p.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	color := randomCOlor()
	for _, id := range in[0] {
		// add center point
		feature := cellIDToCenterPoint(id)
		features = append(features, feature)
		// add bounding box
		feature = cellIDToPolygon(id, color)
		features = append(features, feature)
	}
	ff.Feat = features
	return
}

// innerLoop(s) return an s2.loop representation of the inner loop of the
// geoJSON polygon or multi polygon. We do not support holes in polygons, so the inner ring of the geojson is discarded.
// At this point winding order of the loops is not specified but it **must** be
// counterclockwise, else we return an error.

func (p Polygon) innerLoops() (loops []s2.Loop, err error) {
	cds := p.C[0]
	loop, err := cds.tos2()
	loops = append(loops, loop)
	return loops, err
}

func (mp MultiPolygon) innerLoops() ([]s2.Loop, error) {
	var loops []s2.Loop
	var err error
	for _, c := range mp.C {
		cds := c[0]
		loop, err := cds.tos2()
		if err != nil {
			return loops, err
		}
		loops = append(loops, loop)
	}
	return loops, err
}

// Contains check if the polygon contains the  point
// TODO needs polish
func (p Polygon) Contains(point point.Point) bool {
	// TODO this has a bug
	// maybe is the point to s2 conversion
	var contains bool
	ll, cell := point.ToCell()
	loop, err := p.innerLoops()
	if err != nil {
		log.Error(err)
		return false
	}

	contains = loop[0].RectBound().ContainsCell(cell)
	if !contains {
		return false
	}
	contains = loop[0].ContainsPoint(s2.PointFromLatLng(ll))
	return contains
}

func cellIDToPolygon(id uint64, color string) (f Feature) {
	cellid := s2.CellID(id)
	cell := s2.CellFromCellID(cellid)
	rect := cell.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add first point as last point to close polygon
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f.Type = "Feature"
	prop := make(Prop)
	prop["cellid"] = fmt.Sprintf("%v", cellid)
	prop["fill-opacity"] = 0.2
	prop["fill"] = color
	prop["stroke-width"] = 0
	f.Properties = prop
	f.Geometry = polygon
	return f
}

// randomCOlor return a random hex color
func randomCOlor() string {
	n := 6
	const letters = "1234567890"
	b := make([]byte, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return fmt.Sprintf("#%v", string(b))
}

func cellIDToCenterPoint(id uint64) (f Feature) {
	cellid := s2.CellID(id)
	// ll is the center of the  cellid
	ll := cellid.LatLng()
	var ld Coordinate
	ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
	point := Point{}
	point.Type = "Point"
	point.Coordinate = ld
	f.Type = "Feature"
	prop := make(Prop)
	prop["is_center"] = "true"
	f.Properties = prop
	f.Geometry = point
	return f
}

func boundingbox(loop s2.Loop, id int) (f Feature) {
	rect := loop.RectBound()
	var coordinates Coordinates
	var coordinates2 []Coordinates
	for i := 0; i < 4; i++ {
		ll := rect.Vertex(i)
		// ll is the vertex of the  cellid
		var ld Coordinate
		ld = [2]float64{ll.Lng.Degrees(), ll.Lat.Degrees()}
		coordinates = append(coordinates, ld)
	}
	// add last vertex
	coordinates = append(coordinates, coordinates[0])
	coordinates2 = append(coordinates2, coordinates)
	polygon := Polygon{}
	polygon.Type = "Polygon"
	polygon.C = coordinates2
	f = Feature{}
	f.Type = "Feature"
	prop := make(Prop)
	prop["boundingbox"] = fmt.Sprint(id)
	f.Properties = prop
	f.Geometry = polygon
	return
}

func loopCoverer(loop s2.Loop, precision int) ([]uint64, error) {
	var boundary intArray
	t := time.Now()
	for i := precision; i < 30; i++ {
		log.Debug("start creating covering")
		rc := &s2.RegionCoverer{MinLevel: 0, MaxLevel: i, MaxCells: 500000}
		covering := rc.InteriorCovering(s2.Region(loop))
		log.Debugf("done creating covering in %v", time.Since(t))
		// now approximate the polygon
		for _, val := range covering {
			boundary = append(boundary, uint64(val))
		}
		// crude check to make sure we get enough covering
		if len(boundary) > 4 {
			sort.Sort(boundary)
			return boundary, nil
		}
		log.Warnf("Need to upscale precision to %v", i+1)
	}
	return boundary, fmt.Errorf("Can't cover region.")
}

// ToS2 converts a geoJSON polygon to a set of cellUnions
func (p Polygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = p.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	polygon, err := loopCoverer(loops[0], precision)
	polygons = append(polygons, polygon)
	return polygons, loops, err
}

// ToS2 converts a geoJSON multi polygon to a set of cellUnions
func (mp MultiPolygon) ToS2(precision int) (ids [][]uint64, loops []s2.Loop, err error) {
	var polygons [][]uint64
	loops, err = mp.innerLoops()
	if err != nil {
		return polygons, loops, err
	}
	for _, loop := range loops {
		polygon, err := loopCoverer(loop, precision)
		if err != nil {
			return polygons, loops, err
		}
		polygons = append(polygons, polygon)
	}
	return polygons, loops, err
}

// ToGeoJSON converts polygon to geoJSON
func (p Polygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := p.ToS2(precision)
	if err != nil {
		return
	}
	// NOTE  we expect one polygon/ one loop
	polygon := in[0]
	inner := loops[0]
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	// add bbox
	color := randomCOlor()
	for _, id := range polygon {
		feature := cellIDToPolygon(id, color)
		features = append(features, feature)
	}
	bbox := boundingbox(inner, 0)
	features = append(features, bbox)
	ff.Feat = features
	return
}

// ToGeoJSON converts back a multipolygon to geoJSON
func (mp MultiPolygon) ToGeoJSON(precision int) (ff FeatureCollection, err error) {
	in, loops, err := mp.ToS2(precision)
	if err != nil {
		return
	}
	ff = FeatureCollection{}
	ff.Type = "FeatureCollection"
	var features []Feature
	color := randomCOlor()
	for _, polygon := range in {
		for _, id := range polygon {
			feature := cellIDToPolygon(id, color)
			features = append(features, feature)
		}
	}
	//add bbox
	for i, inner := range loops {
		bbox := boundingbox(inner, i)
		features = append(features, bbox)
	}
	ff.Feat = features
	return
}

// HELPERS
// sortable array
type intArray []uint64

func (s intArray) Len() int           { return len(s) }
func (s intArray) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s intArray) Less(i, j int) bool { return s[i] < s[j] }

// convert to string token
func toToken(ci uint64) string {
	s := strings.TrimRight(fmt.Sprintf("%016x", uint64(ci)), "0")
	if len(s) == 0 {
		return "X"
	}
	return s
}
<|endoftext|>


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

package longtest

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"testing"
	"time"

	"cloud.google.com/go/internal/testutil"
	"cloud.google.com/go/pubsub"
	"google.golang.org/api/option"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const (
	timeout                 = time.Minute * 10
	ackDeadline             = time.Second * 10
	nMessages               = 1e4
	acceptableDupPercentage = 1
	numAcceptableDups       = int(nMessages * acceptableDupPercentage / 100)
)

// The end-to-end pumps many messages into a topic and tests that they are all
// delivered to each subscription for the topic. It also tests that messages
// are not unexpectedly redelivered.
func TestEndToEnd_Dupes(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	client, topic, cleanup := prepareEndToEndTest(ctx, t)
	defer cleanup()
	subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())

	// Two subscriptions to the same topic.
	var err error
	var subs [2]*pubsub.Subscription
	for i := 0; i < len(subs); i++ {
		subs[i], err = client.CreateSubscription(ctx, fmt.Sprintf("%s-%d", subPrefix, i), pubsub.SubscriptionConfig{
			Topic:       topic,
			AckDeadline: ackDeadline,
		})
		if err != nil {
			t.Fatalf("CreateSub error: %v", err)
		}
		defer subs[i].Delete(ctx)
	}

	err = publish(ctx, topic, nMessages)
	topic.Stop()
	if err != nil {
		t.Fatalf("publish: %v", err)
	}

	// recv provides an indication that messages are still arriving.
	recv := make(chan struct{})
	// We have two subscriptions to our topic.
	// Each subscription will get a copy of each published message.
	var wg sync.WaitGroup
	cctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	consumers := []*consumer{
		{counts: make(map[string]int), recv: recv, durations: []time.Duration{time.Hour}},
		{counts: make(map[string]int), recv: recv,
			durations: []time.Duration{ackDeadline, ackDeadline, ackDeadline / 2, ackDeadline / 2, time.Hour}},
	}
	for i, con := range consumers {
		con := con
		sub := subs[i]
		wg.Add(1)
		go func() {
			defer wg.Done()
			con.consume(ctx, t, sub)
		}()
	}
	// Wait for a while after the last message before declaring quiescence.
	// We wait a multiple of the ack deadline, for two reasons:
	// 1. To detect if messages are redelivered after having their ack
	//    deadline extended.
	// 2. To wait for redelivery of messages that were en route when a Receive
	//    is canceled. This can take considerably longer than the ack deadline.
	quiescenceDur := ackDeadline * 6
	quiescenceTimer := time.NewTimer(quiescenceDur)

loop:
	for {
		select {
		case <-recv:
			// Reset timer so we wait quiescenceDur after the last message.
			// See https://godoc.org/time#Timer.Reset for why the Stop
			// and channel drain are necessary.
			if !quiescenceTimer.Stop() {
				<-quiescenceTimer.C
			}
			quiescenceTimer.Reset(quiescenceDur)

		case <-quiescenceTimer.C:
			cancel()
			log.Println("quiesced")
			break loop

		case <-cctx.Done():
			t.Fatal("timed out")
		}
	}
	wg.Wait()
	for i, con := range consumers {
		var numDups int
		var zeroes int
		for _, v := range con.counts {
			if v == 0 {
				zeroes++
			}
			numDups += v - 1
		}

		if zeroes > 0 {
			t.Errorf("Consumer %d: %d messages never arrived", i, zeroes)
		} else if numDups > numAcceptableDups {
			t.Errorf("Consumer %d: Willing to accept %d dups (%v duplicated of %d messages), but got %d", i, numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
		}
	}
}

func TestEndToEnd_LongProcessingTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	client, topic, cleanup := prepareEndToEndTest(ctx, t)
	defer cleanup()
	subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())

	// Two subscriptions to the same topic.
	sub, err := client.CreateSubscription(ctx, subPrefix+"-00", pubsub.SubscriptionConfig{
		Topic:       topic,
		AckDeadline: ackDeadline,
	})
	if err != nil {
		t.Fatalf("CreateSub error: %v", err)
	}
	defer sub.Delete(ctx)

	// Tests the issue found in https://github.com/googleapis/google-cloud-go/issues/1247.
	sub.ReceiveSettings.Synchronous = true
	sub.ReceiveSettings.MaxOutstandingMessages = 500

	err = publish(ctx, topic, 1000)
	topic.Stop()
	if err != nil {
		t.Fatalf("publish: %v", err)
	}

	// recv provides an indication that messages are still arriving.
	recv := make(chan struct{})
	consumer := consumer{
		counts:    make(map[string]int),
		recv:      recv,
		durations: []time.Duration{time.Hour},
		processingDelay: func() time.Duration {
			return time.Duration(1+rand.Int63n(120)) * time.Second
		},
	}
	go consumer.consume(ctx, t, sub)
	// Wait for a while after the last message before declaring quiescence.
	// We wait a multiple of the ack deadline, for two reasons:
	// 1. To detect if messages are redelivered after having their ack
	//    deadline extended.
	// 2. To wait for redelivery of messages that were en route when a Receive
	//    is canceled. This can take considerably longer than the ack deadline.
	quiescenceDur := ackDeadline * 6
	quiescenceTimer := time.NewTimer(quiescenceDur)
loop:
	for {
		select {
		case <-recv:
			// Reset timer so we wait quiescenceDur after the last message.
			// See https://godoc.org/time#Timer.Reset for why the Stop
			// and channel drain are necessary.
			if !quiescenceTimer.Stop() {
				<-quiescenceTimer.C
			}
			quiescenceTimer.Reset(quiescenceDur)

		case <-quiescenceTimer.C:
			cancel()
			log.Println("quiesced")
			break loop

		case <-ctx.Done():
			t.Fatal("timed out")
		}
	}
	var numDups int
	var zeroes int
	for _, v := range consumer.counts {
		if v == 0 {
			zeroes++
		}
		numDups += v - 1
	}

	if zeroes > 0 {
		t.Errorf("%d messages never arrived", zeroes)
	} else if numDups > numAcceptableDups {
		t.Errorf("Willing to accept %d dups (%v duplicated of %d messages), but got %d", numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
	}
}

// publish publishes n messages to topic.
func publish(ctx context.Context, topic *pubsub.Topic, n int) error {
	var rs []*pubsub.PublishResult
	for i := 0; i < n; i++ {
		m := &pubsub.Message{Data: []byte(fmt.Sprintf("msg %d", i))}
		rs = append(rs, topic.Publish(ctx, m))
	}
	for _, r := range rs {
		_, err := r.Get(ctx)
		if err != nil {
			return err
		}
	}
	return nil
}

// consumer consumes messages according to its configuration.
type consumer struct {
	// A consumer will spin out a Receive for each duration, which will be
	// canceled after each duration and the next one spun up. For example, if
	// there are 5 3 second durations, then there will be 5 3 second Receives.
	durations []time.Duration

	// A value is sent to recv each time Inc is called.
	recv chan struct{}

	// How long to wait for before acking.
	processingDelay func() time.Duration

	mu         sync.Mutex
	counts     map[string]int // msgID: recvdAmt
	totalRecvd int
}

// consume reads messages from a subscription, and keeps track of what it receives in mc.
// After consume returns, the caller should wait on wg to ensure that no more updates to mc will be made.
func (c *consumer) consume(ctx context.Context, t *testing.T, sub *pubsub.Subscription) {
	for _, dur := range c.durations {
		ctx2, cancel := context.WithTimeout(ctx, dur)
		defer cancel()
		id := sub.String()[len(sub.String())-1:]
		t.Logf("%s: start receive", id)
		prev := c.totalRecvd
		err := sub.Receive(ctx2, c.process)
		t.Logf("%s: end receive; read %d", id, c.totalRecvd-prev)
		if serr, _ := status.FromError(err); serr.Code() != codes.Canceled {
			panic(err)
		}
		select {
		case <-ctx.Done():
			return
		default:
		}
	}
	return
}

// process handles a message and records it in mc.
func (c *consumer) process(_ context.Context, m *pubsub.Message) {
	c.mu.Lock()
	c.counts[m.ID]++
	c.totalRecvd++
	c.mu.Unlock()
	c.recv <- struct{}{}

	var delay time.Duration
	if c.processingDelay == nil {
		delay = time.Duration(rand.Intn(int(ackDeadline * 3)))
	} else {
		delay = c.processingDelay()
	}

	// Simulate time taken to process m, while continuing to process more messages.
	// Some messages will need to have their ack deadline extended due to this delay.
	time.AfterFunc(delay, func() {
		m.Ack()
	})
}

// Remember to call cleanup!
func prepareEndToEndTest(ctx context.Context, t *testing.T) (*pubsub.Client, *pubsub.Topic, func()) {
	if testing.Short() {
		t.Skip("Integration tests skipped in short mode")
	}
	ts := testutil.TokenSource(ctx, pubsub.ScopePubSub, pubsub.ScopeCloudPlatform)
	if ts == nil {
		t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
	}

	now := time.Now()
	topicName := fmt.Sprintf("endtoend-%d", now.UnixNano())

	client, err := pubsub.NewClient(ctx, testutil.ProjID(), option.WithTokenSource(ts))
	if err != nil {
		t.Fatalf("Creating client error: %v", err)
	}

	var topic *pubsub.Topic
	if topic, err = client.CreateTopic(ctx, topicName); err != nil {
		t.Fatalf("CreateTopic error: %v", err)
	}

	return client, topic, func() {
		topic.Delete(ctx)
		client.Close()
	}
}
<commit_msg>internal: small fix to e2e test<commit_after>// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package longtest

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"testing"
	"time"

	"cloud.google.com/go/internal/testutil"
	"cloud.google.com/go/pubsub"
	"google.golang.org/api/option"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

const (
	timeout                 = time.Minute * 10
	ackDeadline             = time.Second * 10
	nMessages               = 1e4
	acceptableDupPercentage = 1
	numAcceptableDups       = int(nMessages * acceptableDupPercentage / 100)
)

// The end-to-end pumps many messages into a topic and tests that they are all
// delivered to each subscription for the topic. It also tests that messages
// are not unexpectedly redelivered.
func TestEndToEnd_Dupes(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	client, topic, cleanup := prepareEndToEndTest(ctx, t)
	defer cleanup()
	subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())

	// Two subscriptions to the same topic.
	var err error
	var subs [2]*pubsub.Subscription
	for i := 0; i < len(subs); i++ {
		subs[i], err = client.CreateSubscription(ctx, fmt.Sprintf("%s-%d", subPrefix, i), pubsub.SubscriptionConfig{
			Topic:       topic,
			AckDeadline: ackDeadline,
		})
		if err != nil {
			t.Fatalf("CreateSub error: %v", err)
		}
		defer subs[i].Delete(ctx)
	}

	err = publish(ctx, topic, nMessages)
	topic.Stop()
	if err != nil {
		t.Fatalf("publish: %v", err)
	}

	// recv provides an indication that messages are still arriving.
	recv := make(chan struct{})
	// We have two subscriptions to our topic.
	// Each subscription will get a copy of each published message.
	var wg sync.WaitGroup
	cctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()

	consumers := []*consumer{
		{counts: make(map[string]int), recv: recv, durations: []time.Duration{time.Hour}},
		{counts: make(map[string]int), recv: recv,
			durations: []time.Duration{ackDeadline, ackDeadline, ackDeadline / 2, ackDeadline / 2, time.Hour}},
	}
	for i, con := range consumers {
		con := con
		sub := subs[i]
		wg.Add(1)
		go func() {
			defer wg.Done()
			con.consume(ctx, t, sub)
		}()
	}
	// Wait for a while after the last message before declaring quiescence.
	// We wait a multiple of the ack deadline, for two reasons:
	// 1. To detect if messages are redelivered after having their ack
	//    deadline extended.
	// 2. To wait for redelivery of messages that were en route when a Receive
	//    is canceled. This can take considerably longer than the ack deadline.
	quiescenceDur := ackDeadline * 6
	quiescenceTimer := time.NewTimer(quiescenceDur)

loop:
	for {
		select {
		case <-recv:
			// Reset timer so we wait quiescenceDur after the last message.
			// See https://godoc.org/time#Timer.Reset for why the Stop
			// and channel drain are necessary.
			if !quiescenceTimer.Stop() {
				<-quiescenceTimer.C
			}
			quiescenceTimer.Reset(quiescenceDur)

		case <-quiescenceTimer.C:
			cancel()
			log.Println("quiesced")
			break loop

		case <-cctx.Done():
			t.Fatal("timed out")
		}
	}
	wg.Wait()
	for i, con := range consumers {
		var numDups int
		var zeroes int
		for _, v := range con.counts {
			if v == 0 {
				zeroes++
			}
			numDups += v - 1
		}

		if zeroes > 0 {
			t.Errorf("Consumer %d: %d messages never arrived", i, zeroes)
		} else if numDups > numAcceptableDups {
			t.Errorf("Consumer %d: Willing to accept %d dups (%v duplicated of %d messages), but got %d", i, numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
		}
	}
}

func TestEndToEnd_LongProcessingTime(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()
	client, topic, cleanup := prepareEndToEndTest(ctx, t)
	defer cleanup()
	subPrefix := fmt.Sprintf("endtoend-%d", time.Now().UnixNano())

	// Two subscriptions to the same topic.
	sub, err := client.CreateSubscription(ctx, subPrefix+"-00", pubsub.SubscriptionConfig{
		Topic:       topic,
		AckDeadline: ackDeadline,
	})
	if err != nil {
		t.Fatalf("CreateSub error: %v", err)
	}
	defer sub.Delete(ctx)

	// Tests the issue found in https://github.com/googleapis/google-cloud-go/issues/1247.
	sub.ReceiveSettings.Synchronous = true
	sub.ReceiveSettings.MaxOutstandingMessages = 500

	err = publish(ctx, topic, 1000)
	topic.Stop()
	if err != nil {
		t.Fatalf("publish: %v", err)
	}

	// recv provides an indication that messages are still arriving.
	recv := make(chan struct{})
	consumer := consumer{
		counts:    make(map[string]int),
		recv:      recv,
		durations: []time.Duration{time.Hour},
		processingDelay: func() time.Duration {
			return time.Duration(1+rand.Int63n(120)) * time.Second
		},
	}
	go consumer.consume(ctx, t, sub)
	// Wait for a while after the last message before declaring quiescence.
	// We wait a multiple of the ack deadline, for two reasons:
	// 1. To detect if messages are redelivered after having their ack
	//    deadline extended.
	// 2. To wait for redelivery of messages that were en route when a Receive
	//    is canceled. This can take considerably longer than the ack deadline.
	quiescenceDur := ackDeadline * 6
	quiescenceTimer := time.NewTimer(quiescenceDur)
loop:
	for {
		select {
		case <-recv:
			// Reset timer so we wait quiescenceDur after the last message.
			// See https://godoc.org/time#Timer.Reset for why the Stop
			// and channel drain are necessary.
			if !quiescenceTimer.Stop() {
				<-quiescenceTimer.C
			}
			quiescenceTimer.Reset(quiescenceDur)

		case <-quiescenceTimer.C:
			cancel()
			log.Println("quiesced")
			break loop

		case <-ctx.Done():
			t.Fatal("timed out")
		}
	}
	var numDups int
	var zeroes int
	for _, v := range consumer.counts {
		if v == 0 {
			zeroes++
		}
		numDups += v - 1
	}

	if zeroes > 0 {
		t.Errorf("%d messages never arrived", zeroes)
	} else if numDups > numAcceptableDups {
		t.Errorf("Willing to accept %d dups (%v duplicated of %d messages), but got %d", numAcceptableDups, acceptableDupPercentage, int(nMessages), numDups)
	}
}

// publish publishes n messages to topic.
func publish(ctx context.Context, topic *pubsub.Topic, n int) error {
	var rs []*pubsub.PublishResult
	for i := 0; i < n; i++ {
		m := &pubsub.Message{Data: []byte(fmt.Sprintf("msg %d", i))}
		rs = append(rs, topic.Publish(ctx, m))
	}
	for _, r := range rs {
		_, err := r.Get(ctx)
		if err != nil {
			return err
		}
	}
	return nil
}

// consumer consumes messages according to its configuration.
type consumer struct {
	// A consumer will spin out a Receive for each duration, which will be
	// canceled after each duration and the next one spun up. For example, if
	// there are 5 3 second durations, then there will be 5 3 second Receives.
	durations []time.Duration

	// A value is sent to recv each time Inc is called.
	recv chan struct{}

	// How long to wait for before acking.
	processingDelay func() time.Duration

	mu         sync.Mutex
	counts     map[string]int // msgID: recvdAmt
	totalRecvd int
}

// consume reads messages from a subscription, and keeps track of what it receives in mc.
// After consume returns, the caller should wait on wg to ensure that no more updates to mc will be made.
func (c *consumer) consume(ctx context.Context, t *testing.T, sub *pubsub.Subscription) {
	for _, dur := range c.durations {
		ctx2, cancel := context.WithTimeout(ctx, dur)
		defer cancel()
		id := sub.String()[len(sub.String())-1:]
		t.Logf("%s: start receive", id)
		prev := c.totalRecvd
		err := sub.Receive(ctx2, c.process)
		t.Logf("%s: end receive; read %d", id, c.totalRecvd-prev)
		if serr, _ := status.FromError(err); err != nil && serr.Code() != codes.Canceled {
			panic(err)
		}
		select {
		case <-ctx.Done():
			return
		default:
		}
	}
	return
}

// process handles a message and records it in mc.
func (c *consumer) process(_ context.Context, m *pubsub.Message) {
	c.mu.Lock()
	c.counts[m.ID]++
	c.totalRecvd++
	c.mu.Unlock()
	c.recv <- struct{}{}

	var delay time.Duration
	if c.processingDelay == nil {
		delay = time.Duration(rand.Intn(int(ackDeadline * 3)))
	} else {
		delay = c.processingDelay()
	}

	// Simulate time taken to process m, while continuing to process more messages.
	// Some messages will need to have their ack deadline extended due to this delay.
	time.AfterFunc(delay, func() {
		m.Ack()
	})
}

// Remember to call cleanup!
func prepareEndToEndTest(ctx context.Context, t *testing.T) (*pubsub.Client, *pubsub.Topic, func()) {
	if testing.Short() {
		t.Skip("Integration tests skipped in short mode")
	}
	ts := testutil.TokenSource(ctx, pubsub.ScopePubSub, pubsub.ScopeCloudPlatform)
	if ts == nil {
		t.Skip("Integration tests skipped. See CONTRIBUTING.md for details")
	}

	now := time.Now()
	topicName := fmt.Sprintf("endtoend-%d", now.UnixNano())

	client, err := pubsub.NewClient(ctx, testutil.ProjID(), option.WithTokenSource(ts))
	if err != nil {
		t.Fatalf("Creating client error: %v", err)
	}

	var topic *pubsub.Topic
	if topic, err = client.CreateTopic(ctx, topicName); err != nil {
		t.Fatalf("CreateTopic error: %v", err)
	}

	return client, topic, func() {
		topic.Delete(ctx)
		client.Close()
	}
}
<|endoftext|>


<commit_before>package neutron

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

const X_AUTH_TOKEN_HEADER = "X-Auth-Token"

type Client struct {
	URL   string
	token string
}

func NewClient(url, token string) (*Client, error) {
	if url == "" {
		return nil, fmt.Errorf("missing URL")
	}
	if token == "" {
		return nil, fmt.Errorf("missing token")
	}
	return &Client{URL: url, token: token}, nil
}

func (c *Client) CreateNetwork(net Network) (Network, error) {
	return c.postNetwork(fmt.Sprintf("%s/v2.0/networks", c.URL), net)
}

func (c *Client) Networks() ([]Network, error) {
	return c.getNetworks(fmt.Sprintf("%s/v2.0/networks", c.URL))
}

func (c *Client) NetworksByName(name string) ([]Network, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}
	return c.getNetworks(fmt.Sprintf("%s/v2.0/networks?name=%s", c.URL, name))
}

func (c *Client) getNetworks(url string) ([]Network, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Error: %s\n", resp.Status)
	}

	body, err := ioutil.ReadAll(resp.Body)

	var r GetNetworks
	err = json.Unmarshal(body, &r)
	if err != nil {
		return nil, err
	}

	return r.Networks, nil
}

func (c *Client) postNetwork(url string, net Network) (Network, error) {
	client := &http.Client{}
	jsonStr, err := json.Marshal(SingleNetwork{Network: net})
	if err != nil {
		return Network{}, fmt.Errorf("invalid network: ", err)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	req.Header.Set("Content-Type", "application/json")

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

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Network{}, err
	}

	// fmt.Printf("resp body: %s\n", body)

	if resp.StatusCode != http.StatusCreated {
		return Network{}, fmt.Errorf("Error: %s\n", resp.Status)
	}

	var r SingleNetwork
	err = json.Unmarshal(body, &r)
	if err != nil {
		return Network{}, err
	}

	return r.Network, nil
}

func (c *Client) Subnets() ([]Subnet, error) {
	return c.getSubnets(fmt.Sprintf("%s/v2.0/subnets", c.URL))
}

func (c *Client) SubnetsByName(name string) ([]Subnet, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}
	return c.getSubnets(fmt.Sprintf("%s/v2.0/subnets?name=%s", c.URL, name))
}

func (c *Client) getSubnets(url string) ([]Subnet, error) {
	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Error: %s\n", resp.Status)
	}

	body, err := ioutil.ReadAll(resp.Body)

	var r GetSubnets
	err = json.Unmarshal(body, &r)
	if err != nil {
		return nil, err
	}

	return r.Subnets, nil
}
func (c *Client) CreateSubnet(s Subnet) (Subnet, error) {
	return c.postSubnet(fmt.Sprintf("%s/v2.0/subnets", c.URL), s)
}

func (c *Client) postSubnet(url string, s Subnet) (Subnet, error) {
	client := &http.Client{}
	jsonStr, err := json.Marshal(SingleSubnet{Subnet: s})
	if err != nil {
		return Subnet{}, fmt.Errorf("invalid subnet: ", err)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	req.Header.Set("Content-Type", "application/json")

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

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Subnet{}, err
	}

	// fmt.Printf("resp body: %s\n", body)

	if resp.StatusCode != http.StatusCreated {
		return Subnet{}, fmt.Errorf("Error: %s\n", resp.Status)
	}

	var r SingleSubnet
	err = json.Unmarshal(body, &r)
	if err != nil {
		return Subnet{}, err
	}

	return r.Subnet, nil
}
<commit_msg>refactor http requests<commit_after>package neutron

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

const X_AUTH_TOKEN_HEADER = "X-Auth-Token"

type Client struct {
	URL   string
	token string
}

func NewClient(url, token string) (*Client, error) {
	if url == "" {
		return nil, fmt.Errorf("missing URL")
	}
	if token == "" {
		return nil, fmt.Errorf("missing token")
	}
	return &Client{URL: url, token: token}, nil
}

type request struct {
	URL          string
	Method       string
	Body         []byte
	OkStatusCode int
}

type response struct {
	Body       []byte
	StatusCode int
}

func (c *Client) doRequest(r request) (response, error) {
	client := &http.Client{}

	req, err := http.NewRequest(r.Method, r.URL, bytes.NewBuffer(r.Body))
	req.Header.Add(X_AUTH_TOKEN_HEADER, c.token)
	req.Header.Set("Content-Type", "application/json")

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

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return response{}, err
	}

	if resp.StatusCode != r.OkStatusCode {
		return response{}, fmt.Errorf("Error: %s\n", resp.Status)
	}
	return response{Body: body, StatusCode: resp.StatusCode}, nil
}

func (c *Client) CreateNetwork(net Network) (Network, error) {
	jsonStr, err := json.Marshal(SingleNetwork{Network: net})
	if err != nil {
		return Network{}, fmt.Errorf("invalid network: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})
	if err != nil {
		return Network{}, err
	}

	var r SingleNetwork
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Network{}, err
	}

	return r.Network, nil
}

func (c *Client) Networks() ([]Network, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) NetworksByName(name string) ([]Network, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/networks?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetNetworks
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Networks, nil
}

func (c *Client) Subnets() ([]Subnet, error) {
	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) SubnetsByName(name string) ([]Subnet, error) {
	if name == "" {
		return nil, fmt.Errorf("empty 'name' parameter")
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets?name=%s", c.URL, name),
		Method:       "GET",
		OkStatusCode: http.StatusOK,
	})
	if err != nil {
		return nil, err
	}

	var r GetSubnets
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return nil, err
	}
	return r.Subnets, nil
}

func (c *Client) CreateSubnet(s Subnet) (Subnet, error) {
	jsonStr, err := json.Marshal(SingleSubnet{Subnet: s})
	if err != nil {
		return Subnet{}, fmt.Errorf("invalid subnet: ", err)
	}

	resp, err := c.doRequest(request{
		URL:          fmt.Sprintf("%s/v2.0/subnets", c.URL),
		Method:       "POST",
		Body:         jsonStr,
		OkStatusCode: http.StatusCreated,
	})

	if err != nil {
		return Subnet{}, err
	}

	var r SingleSubnet
	err = json.Unmarshal(resp.Body, &r)
	if err != nil {
		return Subnet{}, err
	}

	return r.Subnet, nil
}
<|endoftext|>


<commit_before>package event

import (
	"fmt"
	"time"

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

func getUserRole(username string) bool {
	user := uic.ReadUserByName(username)
	if user.Role == 2 {
		return true
	} else {
		return false
	}
}

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

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

	if len(result) == 0 {
		result = []EventCases{}
	} else {
		for indx, event := range result {
			var eventArr []*Events
			q.Raw(fmt.Sprintf("SELECT * FROM `events` WHERE event_caseId = '%s'", event.Id)).QueryRows(&eventArr)
			fmt.Sprintf("%v", eventArr)
			event.Events = eventArr
			result[indx] = event
		}
	}
	return
}

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

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

func CloseEvent(username string, colsed_note string, id string) (err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	userid := uic.ReadUserIdByName(username)
	_, err = q.Raw("Update event_cases SET user_modified = ?, closed_at = ?, status = ?, closed_note = ? WHERE id = ?", userid, time.Now(), "SOLVED", colsed_note, id).Exec()
	return
}

func CountNumOfTlp() (c int, err error) {
	var h []Tpl
	q := orm.NewOrm()
	q.Using("falcon_portal")
	_, err = q.Raw("select * from `tpl`").QueryRows(&h)
	c = len(h)
	return
}
<commit_msg>add sorting by timestamp of evnets for eventcase query<commit_after>package event

import (
	"fmt"
	"time"

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

func getUserRole(username string) bool {
	user := uic.ReadUserByName(username)
	if user.Role == 2 {
		return true
	} else {
		return false
	}
}

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

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

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

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

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

func CloseEvent(username string, colsed_note string, id string) (err error) {
	q := orm.NewOrm()
	q.Using("falcon_portal")
	userid := uic.ReadUserIdByName(username)
	_, err = q.Raw("Update event_cases SET user_modified = ?, closed_at = ?, status = ?, closed_note = ? WHERE id = ?", userid, time.Now(), "SOLVED", colsed_note, id).Exec()
	return
}

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