<commit_before>// Package chronos is a scheduling tool for Go based on:
//  https://github.com/carlescere/scheduler

package chronos

import (
	"sync"
	"time"
)

type Job struct {
	task   func() // Task to be scheduled
	times, // Times that it can be executed, -1 means no limit
	n int // Times that it has been executed
	aux      auxiliar   // Holds the values for following API calls
	schedule *scheduler // Scheduler to determine when to run the job
	quit,    // Channel for quitting the scheduled job
	skip chan struct{} // Channel for executing the task inmediately
	mutex sync.Mutex // Mutex to avoid concurrent executions of the same task
}

// Job construction with task assignment

func Schedule(f func()) *Job {
	return &Job{task: f, times: -1, quit: make(chan struct{}, 1),
		skip: make(chan struct{}, 1)}
}

// Defining the number of times

func (j *Job) NTimes(n int) *Job {
	j.times = n
	return j
}

func (j *Job) Once() *Job {
	return j.NTimes(1)
}

func (j *Job) Twice() *Job {
	return j.NTimes(2)
}

// Defining the period size in units

func (j *Job) Every(times ...int) *Job {
	switch len(times) {
	case 0:
		j.aux.ammount = 1
	case 1:
		j.aux.ammount = times[0]
	default:
		panic("Too many arguments in Job.Every()")
	}
	return j
}

// Defining the period's unit duration

func (j *Job) duration(d time.Duration) *Job {
	j.aux.kind = periodicKind
	j.aux.unit = d
	return j
}

func (j *Job) Nanosecond() *Job {
	return j.duration(time.Nanosecond)
}

func (j *Job) Nanoseconds() *Job {
	return j.Nanosecond()
}

func (j *Job) Microsecond() *Job {
	return j.duration(time.Microsecond)
}

func (j *Job) Microseconds() *Job {
	return j.Microsecond()
}

func (j *Job) Millisecond() *Job {
	return j.duration(time.Millisecond)
}

func (j *Job) Milliseconds() *Job {
	return j.Millisecond()
}

func (j *Job) Second() *Job {
	return j.duration(time.Second)
}

func (j *Job) Seconds() *Job {
	return j.Second()
}

func (j *Job) Minute() *Job {
	return j.duration(time.Minute)
}

func (j *Job) Minutes() *Job {
	return j.Minute()
}

func (j *Job) Hour() *Job {
	return j.duration(time.Hour)
}

func (j *Job) Hours() *Job {
	return j.Hour()
}

func (j *Job) Day() *Job {
	return j.duration(Day)
}

func (j *Job) Days() *Job {
	return j.Day()
}

func (j *Job) Week() *Job {
	return j.duration(Week)
}

func (j *Job) Weeks() *Job {
	return j.Week()
}

func (j *Job) Month() *Job {
	j.aux.kind = monthlyKind
	return j
}

func (j *Job) Months() *Job {
	return j.Month()
}

func (j *Job) Year() *Job {
	j.aux.kind = yearlyKind
	return j
}

func (j *Job) Years() *Job {
	return j.Year()
}

// Defining if it should run at the start of the cycle

func (j *Job) NotInmediately() *Job {
	j.aux.notInmediately = true
	return j
}

// Defining the starting and ending times

func (j *Job) At(t time.Time) *Job {
	j.aux.start = t
	return j
}

func (j *Job) In(d time.Duration) *Job {
	return j.At(time.Now().Add(d))
}

func (j *Job) Until(t time.Time) *Job {
	j.aux.end = t
	return j
}

// Scheduling the task

func (j *Job) Done() (error, chan struct{}, chan struct{}) {
	switch j.aux.kind {
	case periodicKind:
		schedule, err := newPeriodic(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.unit, j.aux.notInmediately)
	case monthlyKind:
		schedule, err := newMonthly(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.notInmediately)
	case yearlyKind:
		schedule, err := newYearly(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.notInmediately)
	}

	if err == nil {
		j.schedule = schedule
		go func(j *Job) {
			select {
			case <-j.quit:
				return
			case <-j.skip:
				go j.run()
			case <-timer.C:
				go j.run()
			}
		}(j)
	}

	return err, j.skip, j.quit
}

func (j *Job) run() {
	j.mutex.Lock()
	defer j.mutex.Unlock()

	if j.times == -1 || j.n < j.times {
		j.n++
		j.task()
	}
}
<commit_msg>Implement the timer logic<commit_after>// Package chronos is a scheduling tool for Go based on:
//  https://github.com/carlescere/scheduler

package chronos

import (
	"sync"
	"time"
)

type Job struct {
	task   func() // Task to be scheduled
	times, // Times that it can be executed, -1 means no limit
	n int // Times that it has been executed
	aux      auxiliar   // Holds the values for following API calls
	schedule *scheduler // Scheduler to determine when to run the job
	quit,    // Channel for quitting the scheduled job
	skip chan struct{} // Channel for executing the task inmediately
	mutex sync.Mutex // Mutex to avoid concurrent executions of the same task
}

// Job construction with task assignment

func Schedule(f func()) *Job {
	return &Job{task: f, times: -1, quit: make(chan struct{}, 1),
		skip: make(chan struct{}, 1)}
}

// Defining the number of times

func (j *Job) NTimes(n int) *Job {
	j.times = n
	return j
}

func (j *Job) Once() *Job {
	return j.NTimes(1)
}

func (j *Job) Twice() *Job {
	return j.NTimes(2)
}

// Defining the period size in units

func (j *Job) Every(times ...int) *Job {
	switch len(times) {
	case 0:
		j.aux.ammount = 1
	case 1:
		j.aux.ammount = times[0]
	default:
		panic("Too many arguments in Job.Every()")
	}
	return j
}

// Defining the period's unit duration

func (j *Job) duration(d time.Duration) *Job {
	j.aux.kind = periodicKind
	j.aux.unit = d
	return j
}

func (j *Job) Nanosecond() *Job {
	return j.duration(time.Nanosecond)
}

func (j *Job) Nanoseconds() *Job {
	return j.Nanosecond()
}

func (j *Job) Microsecond() *Job {
	return j.duration(time.Microsecond)
}

func (j *Job) Microseconds() *Job {
	return j.Microsecond()
}

func (j *Job) Millisecond() *Job {
	return j.duration(time.Millisecond)
}

func (j *Job) Milliseconds() *Job {
	return j.Millisecond()
}

func (j *Job) Second() *Job {
	return j.duration(time.Second)
}

func (j *Job) Seconds() *Job {
	return j.Second()
}

func (j *Job) Minute() *Job {
	return j.duration(time.Minute)
}

func (j *Job) Minutes() *Job {
	return j.Minute()
}

func (j *Job) Hour() *Job {
	return j.duration(time.Hour)
}

func (j *Job) Hours() *Job {
	return j.Hour()
}

func (j *Job) Day() *Job {
	return j.duration(Day)
}

func (j *Job) Days() *Job {
	return j.Day()
}

func (j *Job) Week() *Job {
	return j.duration(Week)
}

func (j *Job) Weeks() *Job {
	return j.Week()
}

func (j *Job) Month() *Job {
	j.aux.kind = monthlyKind
	return j
}

func (j *Job) Months() *Job {
	return j.Month()
}

func (j *Job) Year() *Job {
	j.aux.kind = yearlyKind
	return j
}

func (j *Job) Years() *Job {
	return j.Year()
}

// Defining if it should run at the start of the cycle

func (j *Job) NotInmediately() *Job {
	j.aux.notInmediately = true
	return j
}

// Defining the starting and ending times

func (j *Job) At(t time.Time) *Job {
	j.aux.start = t
	return j
}

func (j *Job) In(d time.Duration) *Job {
	return j.At(time.Now().Add(d))
}

func (j *Job) Until(t time.Time) *Job {
	j.aux.end = t
	return j
}

// Scheduling the task

func (j *Job) Done() (error, chan struct{}, chan struct{}) {
	var (
		err error
		schedule *scheduler
	)
	switch j.aux.kind {
	case periodicKind:
		schedule, err := newPeriodic(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.unit, j.aux.notInmediately)
	case monthlyKind:
		schedule, err := newMonthly(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.notInmediately)
	case yearlyKind:
		schedule, err := newYearly(j.aux.start, j.aux.end, j.aux.ammount,
			j.aux.notInmediately)
	}

	if err == nil {
		j.schedule = schedule

		go func(j *Job) {
			var (
				ok bool
				next time.Duration
				timer time.Timer
			)
			for {
				ok, next = j.scheduler.next()
				if !ok {
					return
				}
				timer = time.NewTimer(next)
				select {
				case <-j.quit:
					return
				case <-j.skip:
					go j.run()
				case <-timer.C:
					go j.run()
				}
			}
		}(j)
	}

	return err, j.skip, j.quit
}

func (j *Job) run() {
	j.mutex.Lock()
	defer j.mutex.Unlock()

	if j.times == -1 || j.n < j.times {
		j.n++
		j.task()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/mholt/binding"
	// "io/ioutil"
	"net/http"
)

type TrackJson struct {
	Visits  int64 `json:"visits"`
	Uniques int64 `json:"uniques"`
}

func (trackJson *TrackJson) FieldMap() binding.FieldMap {
	return binding.FieldMap{
		&trackJson.Visits:  "visits",
		&trackJson.Uniques: "uniques",
	}
}

func apiHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	objectId := vars["objectId"]
	conn := redisPool.Get()
	defer conn.Close()

	uniques, err := redis.Int64(conn.Do("PFCOUNT", "hll_"+objectId))
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var migrated_visits, migrated_uniques, visits int64
	mget, err := redis.Values(conn.Do("MGET", "visits_"+objectId, "uniques_"+objectId, "hits_"+objectId))
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if _, err := redis.Scan(mget, &migrated_visits, &migrated_uniques, &visits); err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	visits += migrated_visits
	uniques += migrated_uniques

	apiResponse := TrackJson{Visits: visits, Uniques: uniques}
	js, _ := json.MarshalIndent(apiResponse, "", "  ")
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func apiMultiHandler(w http.ResponseWriter, req *http.Request) {
	req.ParseForm()

	if len(req.Form["id"]) < 1 {
		http.Error(w, "Must pass id parameter (at least once)", http.StatusBadRequest)
		return
	}

	response, err := GetMulti(req.Form["id"])
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	js, _ := json.MarshalIndent(response, "", "  ")
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func GetMulti(ids []string) (tj TrackJson, err error) {
	conn := redisPool.Get()
	defer conn.Close()

	script_args := redis.Args{}.Add(len(ids)).AddFlat(ids)
	// var visits, uniques int64
	script_result, err := redis.Scan(multi_script.Do(conn, script_args...), &tj.Visits, &tj.Uniques)
	if err != nil {
		return tj, err
	}
	fmt.Println("%q\n", script_result)

	return tj, err
}

func apiWriteHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	objectId := vars["objectId"]
	trackJson := new(TrackJson)
	if binding.Bind(req, trackJson).Handle(w) {
		return
	}
	fmt.Sprintf("%q\n", trackJson)
	conn := redisPool.Get()
	defer conn.Close()
	_, err := conn.Do("MSET", "uniques_"+objectId, trackJson.Uniques, "visits_"+objectId, trackJson.Visits)
	if err != nil {
		fmt.Print(err)
	}
}
<commit_msg>_multi mostly working. Needs tests.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/gorilla/mux"
	"github.com/mholt/binding"
	// "io/ioutil"
	"net/http"
)

type TrackJson struct {
	Visits  int64 `json:"visits"`
	Uniques int64 `json:"uniques"`
}

func (trackJson *TrackJson) FieldMap() binding.FieldMap {
	return binding.FieldMap{
		&trackJson.Visits:  "visits",
		&trackJson.Uniques: "uniques",
	}
}

func apiHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	objectId := vars["objectId"]
	conn := redisPool.Get()
	defer conn.Close()

	uniques, err := redis.Int64(conn.Do("PFCOUNT", "hll_"+objectId))
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var migrated_visits, migrated_uniques, visits int64
	mget, err := redis.Values(conn.Do("MGET", "visits_"+objectId, "uniques_"+objectId, "hits_"+objectId))
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if _, err := redis.Scan(mget, &migrated_visits, &migrated_uniques, &visits); err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	visits += migrated_visits
	uniques += migrated_uniques

	apiResponse := TrackJson{Visits: visits, Uniques: uniques}
	js, _ := json.MarshalIndent(apiResponse, "", "  ")
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func apiMultiHandler(w http.ResponseWriter, req *http.Request) {
	req.ParseForm()

	if len(req.Form["id"]) < 1 {
		http.Error(w, "Must pass id parameter (at least once)", http.StatusBadRequest)
		return
	}

	response, err := GetMulti(req.Form["id"])
	if err != nil {
		fmt.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	js, _ := json.MarshalIndent(response, "", "  ")
	w.Header().Set("Content-Type", "application/json")
	w.Write(js)
}

func GetMulti(ids []string) (tj TrackJson, err error) {
	conn := redisPool.Get()
	defer conn.Close()

	script_args := redis.Args{}.Add(len(ids)).AddFlat(ids)
	// var visits, uniques int64
	script_result, err := redis.Values(multi_script.Do(conn, script_args...))
	if err != nil {
		return tj, err
	}
	_, err = redis.Scan(script_result, &tj.Visits, &tj.Uniques)
	if err != nil {
		return tj, err
	}
	return tj, err
}

func apiWriteHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	objectId := vars["objectId"]
	trackJson := new(TrackJson)
	if binding.Bind(req, trackJson).Handle(w) {
		return
	}
	fmt.Sprintf("%q\n", trackJson)
	conn := redisPool.Get()
	defer conn.Close()
	_, err := conn.Do("MSET", "uniques_"+objectId, trackJson.Uniques, "visits_"+objectId, trackJson.Visits)
	if err != nil {
		fmt.Print(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/mafredri/cdp"
	"github.com/mafredri/cdp/protocol/runtime"
)

// App represents an app in the ASUSTOR Developer Corner.
type App struct {
	ID         int      `json:"id"`
	Package    string   `json:"package"`
	Arch       string   `json:"arch"`
	Categories []string `json:"categories"`
	Status     string   `json:"status"`
	Beta       bool     `json:"beta"`
}

// HasCategory returns true if the app has the category,
// uses case insensitive comparison.
func (a *App) HasCategory(cat string) bool {
	cat = strings.ToLower(cat)
	for _, c := range a.Categories {
		if cat == strings.ToLower(c) {
			return true
		}
	}
	return false
}

// UpdateURL returns the URL for updating the App.
func (a *App) UpdateURL() string {
	return fmt.Sprintf("http://developer.asustor.com/app/updateApp?id=%d", a.ID)
}

type appSlice []App

func (as appSlice) Find(name, arch string) (a App) {
	for _, app := range as {
		if app.Package == name && app.Arch == arch {
			return app
		}
	}
	return a
}

// Parse application data from the table.
const jsParseApps = `
	const selectRows = '#product-table:first-of-type tr';
	const selectColumns = 'td:nth-child(2), td:nth-child(4), td:nth-child(7), td:nth-child(9), td:nth-child(10), a[href*="app/updateApp?id="]';
	const colNames = ['package', 'arch', 'categories', 'lastUpdate', 'status', 'id'];
	const zipObject = (entries, cols) => Object.assign({}, ...entries.map((v, i) => ({[cols[i]]: v})));

	const apps = Array.from(document.querySelectorAll(selectRows))
		.slice(1)
		.map(td => {
			const cols = Array.from(td.querySelectorAll(selectColumns))
				.map(el => {
					if (el.href) {
						return parseInt(el.href.replace(/.*id=/, ''), 10);
					}
					return el.textContent.trim();
				});
			const obj = zipObject(cols, colNames);
			obj.categories = obj.categories.split('\n').map(s => s.trim()).filter(s => s);
			obj.beta = !!td.querySelector('img[src*="beta"]');
			return obj;
		});

	// Filter out the older entries if there are duplicates.
	apps.filter(a => !apps.find(b => b !== a && a.package === b.package && a.arch === b.arch && a.lastUpdate < b.lastUpdate));
`

func getApps(ctx context.Context, c *cdp.Client) ([]App, error) {
	err := navigate(ctx, c.Page, "http://developer.asustor.com/app/mgt", 10*time.Second)
	if err != nil {
		return nil, err
	}

	evalArgs := runtime.NewEvaluateArgs(jsParseApps).SetReturnByValue(true)
	eval, err := c.Runtime.Evaluate(ctx, evalArgs)
	if err != nil {
		return nil, err
	}

	var apps []App
	err = json.Unmarshal(eval.Result.Value, &apps)
	return apps, err
}
<commit_msg>Fetch more app data<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/mafredri/cdp"
	"github.com/mafredri/cdp/protocol/runtime"
	"github.com/olekukonko/tablewriter"
)

// App represents an app in the ASUSTOR Developer Corner.
type App struct {
	ID         int       `json:"id"`
	Package    string    `json:"package"`
	Name       string    `json:"name"`
	Arch       string    `json:"arch"`
	Version    string    `json:"version"`
	Categories []string  `json:"categories"`
	LastUpdate time.Time `json:"lastUpdate"`
	Status     string    `json:"status"`
	Beta       bool      `json:"beta"`
}

// HasCategory returns true if the app has the category,
// uses case insensitive comparison.
func (a *App) HasCategory(cat string) bool {
	cat = strings.ToLower(cat)
	for _, c := range a.Categories {
		if cat == strings.ToLower(c) {
			return true
		}
	}
	return false
}

// UpdateURL returns the URL for updating the App.
func (a *App) UpdateURL() string {
	return fmt.Sprintf("http://developer.asustor.com/app/updateApp?id=%d", a.ID)
}

type appSlice []App

func (as appSlice) Find(name, arch string) (a App) {
	for _, app := range as {
		if app.Package == name && app.Arch == arch {
			return app
		}
	}
	return a
}

// Parse application data from the table.
const jsParseApps = `
	const selectRows = '#product-table:first-of-type tr';
	const selectColumns = 'td:nth-child(2), td:nth-child(3), td:nth-child(4), td:nth-child(6), td:nth-child(7), td:nth-child(9), td:nth-child(10), a[href*="app/updateApp?id="]';
	const colNames = ['package', 'name', 'arch', 'version', 'categories', 'lastUpdate', 'status', 'id'];
	const zipObject = (entries, cols) => Object.assign({}, ...entries.map((v, i) => ({[cols[i]]: v})));

	const apps = Array.from(document.querySelectorAll(selectRows))
		.slice(1)
		.map(td => {
			const cols = Array.from(td.querySelectorAll(selectColumns))
				.map(el => {
					if (el.href) {
						return parseInt(el.href.replace(/.*id=/, ''), 10);
					}
					return el.textContent.trim();
				});
			const obj = zipObject(cols, colNames);
			obj.categories = obj.categories.split('\n').map(s => s.trim()).filter(s => s);
			obj.beta = !!td.querySelector('img[src*="beta"]');
			// Convert to Go time format (UTC).
			obj.lastUpdate = obj.lastUpdate.replace(' ', 'T') + 'Z';
			return obj;
		});

	// Filter out the older entries if there are duplicates.
	apps.filter(a => !apps.find(b => b !== a && a.package === b.package && a.arch === b.arch && a.lastUpdate < b.lastUpdate));
`

func getApps(ctx context.Context, c *cdp.Client) ([]App, error) {
	err := navigate(ctx, c.Page, "http://developer.asustor.com/app/mgt", 10*time.Second)
	if err != nil {
		return nil, err
	}

	evalArgs := runtime.NewEvaluateArgs(jsParseApps).SetReturnByValue(true)
	eval, err := c.Runtime.Evaluate(ctx, evalArgs)
	if err != nil {
		return nil, err
	}

	var apps []App
	err = json.Unmarshal(eval.Result.Value, &apps)
	return apps, err
}
<|endoftext|>


<commit_before>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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.
*/

/*
pzsvc-pdal provides an endpoint for accepting PDAL requests.

Examples

  $ curl -v -X POST -H "Content-Type: application/json" \
    -d '{"source":{"bucket":"venicegeo-sample-data","key":"pointcloud/samp11-utm.laz"},"function":"info"}' http://hostIP:8080/pdal

We shall see where we land with the input and output message for the job manager, but for now, we are expecting something along these lines.

Input:

	{
		"source": {
			"bucket": "venicegeo-sample-data",
			"key": "pointcloud/samp11-utm.laz"
		},
		"function": "ground",
		"options": {
			"slope": 0.5
		},
		"destination": {
			"bucket": "venicegeo-sample-data",
			"key" "temp/output.laz"
		}
	}

Output:

	{
		"input": <echo the input message>,
		"started_at": "2015-12-23T18:07:36.987565884Z",
		"finished_at": "2015-12-23T18:07:38.111658707Z",
		"code": 200,
		"message": "Success!"
	}

These messages are known to be incomplete at the moment. I'm sure there will be things like job IDs, etc. that have not been included at the moment. This is a good starting point though.
*/
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
	"github.com/venicegeo/pzsvc-pdal/functions"
	"github.com/venicegeo/pzsvc-pdal/handlers"
)

// var pdalMetadata = job.ResourceMetadata{
// 	Name:         "pdal",
// 	ServiceID:    "",
// 	Description:  "Process point cloud data using PDAL.",
// 	URL:          "https://api.piazzageo.io/v1/pdal",
// 	Networks:     "TBD",
// 	QoS:          "Development",
// 	Availability: "UP",
// 	Tags:         "point cloud, pdal, lidar",
// 	ClassType:    "Unclassified",
// 	// TermDate:            time.Now(),
// 	// ClientCertRequired:  false,
// 	// CredentialsRequired: false,
// 	// PreAuthRequired:     false,
// 	// Contracts:           "",
// 	// Method:              "",
// 	// MimeType:            "",
// 	// Params:              "",
// 	// Reason:              "",
// }

func main() {
	// For standalone demo purposes, we will start two services: our PDAL service, and a mocked up JobManager.

	router := httprouter.New()

	router.GET("/", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
		fmt.Fprintf(w, "Hi!")
	})

	type ListFuncs struct {
		Functions []string `json:"functions"`
	}
	out := ListFuncs{[]string{"crop", "dart", "dtm", "ground", "height", "info", "radius", "statistical", "translate"}}

	router.GET("/functions/:name", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		var a interface{}
		switch ps.ByName("name") {
		case "crop":
			a = functions.NewCropOptions()
			w.WriteHeader(http.StatusOK)

		case "dart":
			a = functions.NewDartOptions()
			w.WriteHeader(http.StatusOK)

		case "dtm":
			a = functions.NewDtmOptions()
			w.WriteHeader(http.StatusOK)

		case "ground":
			a = functions.NewGroundOptions()
			w.WriteHeader(http.StatusOK)

		case "height":
			w.WriteHeader(http.StatusOK)

		case "info":
			a = functions.NewInfoOptions()
			w.WriteHeader(http.StatusOK)

		case "radius":
			a = functions.NewRadiusOptions()
			w.WriteHeader(http.StatusOK)

		case "statistical":
			a = functions.NewStatisticalOptions()
			w.WriteHeader(http.StatusOK)

		case "translate":
			w.WriteHeader(http.StatusOK)

		default:
			type DefaultMsg struct {
				Message string `json:"message"`
				ListFuncs
			}
			msg := "Unrecognized function " + ps.ByName("name") + "."
			a = DefaultMsg{msg, out}
			w.WriteHeader(http.StatusBadRequest)
		}
		if err := json.NewEncoder(w).Encode(a); err != nil {
			log.Fatal(err)
		}
	})

	router.GET("/functions", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		w.WriteHeader(http.StatusOK)
		if err := json.NewEncoder(w).Encode(out); err != nil {
			log.Fatal(err)
		}
	})

	// Setup the PDAL service.
	router.POST("/pdal", handlers.PdalHandler)

	// Setup the mocked up JobManager.
	// router.POST("/manager", handlers.JobManagerHandler)

	var defaultPort = os.Getenv("PORT")
	if defaultPort == "" {
		defaultPort = "8080"
	}

	log.Println("Starting on port ", defaultPort)
	log.Println(os.Getenv("PATH"))
	log.Println(os.Getenv("LD_LIBRARY_PATH"))
	if err := http.ListenAndServe(":"+defaultPort, router); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Only say hello<commit_after>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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.
*/

/*
pzsvc-pdal provides an endpoint for accepting PDAL requests.

Examples

  $ curl -v -X POST -H "Content-Type: application/json" \
    -d '{"source":{"bucket":"venicegeo-sample-data","key":"pointcloud/samp11-utm.laz"},"function":"info"}' http://hostIP:8080/pdal

We shall see where we land with the input and output message for the job manager, but for now, we are expecting something along these lines.

Input:

	{
		"source": {
			"bucket": "venicegeo-sample-data",
			"key": "pointcloud/samp11-utm.laz"
		},
		"function": "ground",
		"options": {
			"slope": 0.5
		},
		"destination": {
			"bucket": "venicegeo-sample-data",
			"key" "temp/output.laz"
		}
	}

Output:

	{
		"input": <echo the input message>,
		"started_at": "2015-12-23T18:07:36.987565884Z",
		"finished_at": "2015-12-23T18:07:38.111658707Z",
		"code": 200,
		"message": "Success!"
	}

These messages are known to be incomplete at the moment. I'm sure there will be things like job IDs, etc. that have not been included at the moment. This is a good starting point though.
*/
package main

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

	"github.com/julienschmidt/httprouter"
)

// var pdalMetadata = job.ResourceMetadata{
// 	Name:         "pdal",
// 	ServiceID:    "",
// 	Description:  "Process point cloud data using PDAL.",
// 	URL:          "https://api.piazzageo.io/v1/pdal",
// 	Networks:     "TBD",
// 	QoS:          "Development",
// 	Availability: "UP",
// 	Tags:         "point cloud, pdal, lidar",
// 	ClassType:    "Unclassified",
// 	// TermDate:            time.Now(),
// 	// ClientCertRequired:  false,
// 	// CredentialsRequired: false,
// 	// PreAuthRequired:     false,
// 	// Contracts:           "",
// 	// Method:              "",
// 	// MimeType:            "",
// 	// Params:              "",
// 	// Reason:              "",
// }

func main() {
	// For standalone demo purposes, we will start two services: our PDAL service, and a mocked up JobManager.

	router := httprouter.New()

	router.GET("/", func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
		fmt.Fprintf(w, "Hi!")
	})

	// type ListFuncs struct {
	// 	Functions []string `json:"functions"`
	// }
	// out := ListFuncs{[]string{"crop", "dart", "dtm", "ground", "height", "info", "radius", "statistical", "translate"}}
	//
	// router.GET("/functions/:name", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	// 	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	// 	var a interface{}
	// 	switch ps.ByName("name") {
	// 	case "crop":
	// 		a = functions.NewCropOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "dart":
	// 		a = functions.NewDartOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "dtm":
	// 		a = functions.NewDtmOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "ground":
	// 		a = functions.NewGroundOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "height":
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "info":
	// 		a = functions.NewInfoOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "radius":
	// 		a = functions.NewRadiusOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "statistical":
	// 		a = functions.NewStatisticalOptions()
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	case "translate":
	// 		w.WriteHeader(http.StatusOK)
	//
	// 	default:
	// 		type DefaultMsg struct {
	// 			Message string `json:"message"`
	// 			ListFuncs
	// 		}
	// 		msg := "Unrecognized function " + ps.ByName("name") + "."
	// 		a = DefaultMsg{msg, out}
	// 		w.WriteHeader(http.StatusBadRequest)
	// 	}
	// 	if err := json.NewEncoder(w).Encode(a); err != nil {
	// 		log.Fatal(err)
	// 	}
	// })
	//
	// router.GET("/functions", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	// 	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	// 	w.WriteHeader(http.StatusOK)
	// 	if err := json.NewEncoder(w).Encode(out); err != nil {
	// 		log.Fatal(err)
	// 	}
	// })
	//
	// // Setup the PDAL service.
	// router.POST("/pdal", handlers.PdalHandler)

	// Setup the mocked up JobManager.
	// router.POST("/manager", handlers.JobManagerHandler)

	var defaultPort = os.Getenv("PORT")
	if defaultPort == "" {
		defaultPort = "8080"
	}

	log.Println("Starting on port ", defaultPort)
	log.Println(os.Getenv("PATH"))
	log.Println(os.Getenv("LD_LIBRARY_PATH"))
	if err := http.ListenAndServe(":"+defaultPort, router); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package tent

import (
	"crypto/sha256"
	"encoding/json"
	"net/http"

	"github.com/tent/hawk-go"
)

const (
	PostTypeApp         = "https://tent.io/types/app/v0#"
	PostTypeAppAuth     = "https://tent.io/types/app-auth/v0#"
	PostTypeCredentials = "https://tent.io/types/credentials/v0#"
)

type Credentials struct {
	HawkKey       string `json:"hawk_key"`
	HawkAlgorithm string `json:"hawk_algorithm"`

	Post *Post
}

func NewAppPost(app *App) *Post {
	data, _ := json.Marshal(app)
	return &Post{Type: PostTypeApp, Content: data, Permissions: &PostPermissions{PublicFlag: new(bool)}}
}

type AppPostTypes struct {
	Read  []string `json:"read,omitempty"`
	Write []string `json:"write,omitempty"`
}

type App struct {
	Name        string   `json:"name"`
	URL         string   `json:"url"`
	Description string   `json:"description,omitempty"`
	Scopes      []string `json:"scopes,omitempty"`

	PostTypes AppPostTypes `json:"post_types,omitempty"`

	RedirectURI           string   `json:"redirect_uri"`
	NotificationURL       string   `json:"notification_url,omitempty"`
	NotificationPostTypes []string `json:"notification_post_types,omitempty"`

	Post *Post `json:"-"`
}

type AppAuth struct {
	Active bool     `json:"active"`
	Scopes []string `json:"scopes,omitempty"`

	PostTypes AppPostTypes `json:"post_types,omitempty"`

	Post *Post `json:"-"`
}

const TokenTypeHawk = "https://tent.io/oauth/hawk-token"

func oauthTokenURL(server *MetaPostServer) (string, error) { return server.URLs.OAuthToken, nil }

func (client *Client) RequestAccessToken(code string) (*hawk.Credentials, error) {
	data, _ := json.Marshal(&AccessTokenRequest{TokenType: TokenTypeHawk, Code: code})
	tokenRes := &AccessTokenResponse{}
	header := make(http.Header)
	header.Set("Accept", "application/json")
	header.Set("Content-Type", "application/json")
	_, err := client.requestJSON("POST", oauthTokenURL, header, data, tokenRes)
	if err != nil {
		return nil, err
	}
	return tokenRes.HawkCredentials(client.Credentials.App), err
}

type AccessTokenRequest struct {
	Code      string `json:"code"`
	TokenType string `json:"token_type"`
}

type AccessTokenResponse struct {
	HawkID  string `json:"access_token"`
	HawkKey string `json:"hawk_key"`

	HawkAlgorithm string `json:"hawk_algorithm"`
	TokenType     string `json:"token_type"`
}

func (res *AccessTokenResponse) HawkCredentials(app string) *hawk.Credentials {
	return &hawk.Credentials{Key: res.HawkKey, ID: res.HawkID, App: app, Hash: sha256.New}
}
<commit_msg>Rename app/auth PostTypes -> Types<commit_after>package tent

import (
	"crypto/sha256"
	"encoding/json"
	"net/http"

	"github.com/tent/hawk-go"
)

const (
	PostTypeApp         = "https://tent.io/types/app/v0#"
	PostTypeAppAuth     = "https://tent.io/types/app-auth/v0#"
	PostTypeCredentials = "https://tent.io/types/credentials/v0#"
)

type Credentials struct {
	HawkKey       string `json:"hawk_key"`
	HawkAlgorithm string `json:"hawk_algorithm"`

	Post *Post
}

func NewAppPost(app *App) *Post {
	data, _ := json.Marshal(app)
	return &Post{Type: PostTypeApp, Content: data, Permissions: &PostPermissions{PublicFlag: new(bool)}}
}

type AppTypes struct {
	Read  []string `json:"read,omitempty"`
	Write []string `json:"write,omitempty"`
}

type App struct {
	Name        string   `json:"name"`
	URL         string   `json:"url"`
	Description string   `json:"description,omitempty"`
	Scopes      []string `json:"scopes,omitempty"`

	Types AppTypes `json:"types,omitempty"`

	RedirectURI       string   `json:"redirect_uri"`
	NotificationURL   string   `json:"notification_url,omitempty"`
	NotificationTypes []string `json:"notification_types,omitempty"`

	Post *Post `json:"-"`
}

type AppAuth struct {
	Active bool     `json:"active"`
	Scopes []string `json:"scopes,omitempty"`

	Types AppTypes `json:"types,omitempty"`

	Post *Post `json:"-"`
}

const TokenTypeHawk = "https://tent.io/oauth/hawk-token"

func oauthTokenURL(server *MetaPostServer) (string, error) { return server.URLs.OAuthToken, nil }

func (client *Client) RequestAccessToken(code string) (*hawk.Credentials, error) {
	data, _ := json.Marshal(&AccessTokenRequest{TokenType: TokenTypeHawk, Code: code})
	tokenRes := &AccessTokenResponse{}
	header := make(http.Header)
	header.Set("Accept", "application/json")
	header.Set("Content-Type", "application/json")
	_, err := client.requestJSON("POST", oauthTokenURL, header, data, tokenRes)
	if err != nil {
		return nil, err
	}
	return tokenRes.HawkCredentials(client.Credentials.App), err
}

type AccessTokenRequest struct {
	Code      string `json:"code"`
	TokenType string `json:"token_type"`
}

type AccessTokenResponse struct {
	HawkID  string `json:"access_token"`
	HawkKey string `json:"hawk_key"`

	HawkAlgorithm string `json:"hawk_algorithm"`
	TokenType     string `json:"token_type"`
}

func (res *AccessTokenResponse) HawkCredentials(app string) *hawk.Credentials {
	return &hawk.Credentials{Key: res.HawkKey, ID: res.HawkID, App: app, Hash: sha256.New}
}
<|endoftext|>


<commit_before>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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.
*/

/*
pzsvc-pdal provides an endpoint for accepting PDAL requests.

Examples

  $ curl -v -X POST -H "Content-Type: application/json" \
    -d '{"source":{"bucket":"venicegeo-sample-data","key":"pointcloud/samp11-utm.laz"},"function":"info"}' http://hostIP:8080/pdal

We shall see where we land with the input and output message for the job manager, but for now, we are expecting something along these lines.

Input:

	{
		"source": {
			"bucket": "venicegeo-sample-data",
			"key": "pointcloud/samp11-utm.laz"
		},
		"function": "ground",
		"options": {
			"slope": 0.5
		},
		"destination": {
			"bucket": "venicegeo-sample-data",
			"key" "temp/output.laz"
		}
	}

Output:

	{
		"input": <echo the input message>,
		"started_at": "2015-12-23T18:07:36.987565884Z",
		"finished_at": "2015-12-23T18:07:38.111658707Z",
		"code": 200,
		"message": "Success!"
	}

These messages are known to be incomplete at the moment. I'm sure there will be things like job IDs, etc. that have not been included at the moment. This is a good starting point though.
*/
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"

	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/julienschmidt/httprouter"
	"github.com/venicegeo/pzsvc-pdal/functions"
	"github.com/venicegeo/pzsvc-pdal/handlers"
	"github.com/venicegeo/pzsvc-sdk-go/gateway"
)

type appHandler func(http.ResponseWriter, *http.Request) *handlers.AppError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		if awsErr, ok := e.Error.(awserr.Error); ok {
			e.Message = awsErr.Message()
		}

		if err := json.NewEncoder(w).Encode(e); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
		w.WriteHeader(e.Code)
	}
}

func main() {
	m := gateway.ResourceMetadata{
		Name:             "pzsvc-pdal",
		URL:              "http://pzsvc-pdal.cf.piazzageo.io/api/v1/pdal",
		Description:      "Process point cloud data using PDAL",
		Method:           "POST",
		RequestMimeType:  "application/json",
		ResponseMimeType: "application/json",
	}
	if err := gateway.RegisterService(m); err != nil {
		log.Println(err)
	}

	router := httprouter.New()

	router.GET("/",
		func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
			fmt.Fprintf(w, "Hi!")
		})

	router.GET("/api/v1/version",
		func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
			cmd := exec.Command("pdal", "--debug")
			b, _ := cmd.CombinedOutput()
			fmt.Printf(string(b))
			fmt.Fprintf(w, string(b))
		})

	type ListFuncs struct {
		Functions []string `json:"functions"`
	}
	out := ListFuncs{[]string{
		"crop", "dart", "dtm", "ground", "height", "info", "radius", "statistical",
		"translate", "vo",
	}}

	router.GET("/api/v1/functions/:name",
		func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			w.Header().Set("Content-Type", "application/json; charset=UTF-8")
			var a interface{}
			switch ps.ByName("name") {
			case "crop":
				a = functions.NewCropOptions()
				w.WriteHeader(http.StatusOK)

			case "dart":
				a = functions.NewDartOptions()
				w.WriteHeader(http.StatusOK)

			case "dtm":
				a = functions.NewDtmOptions()
				w.WriteHeader(http.StatusOK)

			case "ground":
				a = functions.NewGroundOptions()
				w.WriteHeader(http.StatusOK)

			case "height":
				w.WriteHeader(http.StatusOK)

			case "info":
				a = functions.NewInfoOptions()
				w.WriteHeader(http.StatusOK)

			case "radius":
				a = functions.NewRadiusOptions()
				w.WriteHeader(http.StatusOK)

			case "statistical":
				a = functions.NewStatisticalOptions()
				w.WriteHeader(http.StatusOK)

			case "translate":
				w.WriteHeader(http.StatusOK)

			case "vo":
				w.WriteHeader(http.StatusOK)

			default:
				type DefaultMsg struct {
					Message string `json:"message"`
					ListFuncs
				}
				msg := "Unrecognized function " + ps.ByName("name") + "."
				a = DefaultMsg{msg, out}
				w.WriteHeader(http.StatusBadRequest)
			}
			if err := json.NewEncoder(w).Encode(a); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		})

	router.GET("/api/v1/functions",
		func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
			w.Header().Set("Content-Type", "application/json; charset=UTF-8")
			w.WriteHeader(http.StatusOK)
			if err := json.NewEncoder(w).Encode(out); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		})

	// // Setup the PDAL service.
	router.Handler("POST", "/api/v1/pdal", appHandler(handlers.PdalHandler))

	router.Handler("POST", "/api/v1/pipeline", appHandler(handlers.PipelineHandler))

	var defaultPort = os.Getenv("PORT")
	if defaultPort == "" {
		defaultPort = "8080"
	}
	log.Println()

	log.Println("Starting on port ", defaultPort)
	if err := http.ListenAndServe(":"+defaultPort, router); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>WriteHeader before encoding error response, else multiple http ResponseWriter calls<commit_after>/*
Copyright 2015-2016, RadiantBlue Technologies, Inc.

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.
*/

/*
pzsvc-pdal provides an endpoint for accepting PDAL requests.

Examples

  $ curl -v -X POST -H "Content-Type: application/json" \
    -d '{"source":{"bucket":"venicegeo-sample-data","key":"pointcloud/samp11-utm.laz"},"function":"info"}' http://hostIP:8080/pdal

We shall see where we land with the input and output message for the job manager, but for now, we are expecting something along these lines.

Input:

	{
		"source": {
			"bucket": "venicegeo-sample-data",
			"key": "pointcloud/samp11-utm.laz"
		},
		"function": "ground",
		"options": {
			"slope": 0.5
		},
		"destination": {
			"bucket": "venicegeo-sample-data",
			"key" "temp/output.laz"
		}
	}

Output:

	{
		"input": <echo the input message>,
		"started_at": "2015-12-23T18:07:36.987565884Z",
		"finished_at": "2015-12-23T18:07:38.111658707Z",
		"code": 200,
		"message": "Success!"
	}

These messages are known to be incomplete at the moment. I'm sure there will be things like job IDs, etc. that have not been included at the moment. This is a good starting point though.
*/
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"

	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/julienschmidt/httprouter"
	"github.com/venicegeo/pzsvc-pdal/functions"
	"github.com/venicegeo/pzsvc-pdal/handlers"
	"github.com/venicegeo/pzsvc-sdk-go/gateway"
)

type appHandler func(http.ResponseWriter, *http.Request) *handlers.AppError

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *appError, not os.Error.
		if awsErr, ok := e.Error.(awserr.Error); ok {
			e.Message = awsErr.Message()
		}
		log.Println(e.Message)

		w.WriteHeader(e.Code)
		if err := json.NewEncoder(w).Encode(e); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

func main() {
	m := gateway.ResourceMetadata{
		Name:             "pzsvc-pdal",
		URL:              "http://pzsvc-pdal.cf.piazzageo.io/api/v1/pdal",
		Description:      "Process point cloud data using PDAL",
		Method:           "POST",
		RequestMimeType:  "application/json",
		ResponseMimeType: "application/json",
	}
	if err := gateway.RegisterService(m); err != nil {
		log.Println(err)
	}

	router := httprouter.New()

	router.GET("/",
		func(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
			fmt.Fprintf(w, "Hi!")
		})

	router.GET("/api/v1/version",
		func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
			cmd := exec.Command("pdal", "--debug")
			b, _ := cmd.CombinedOutput()
			fmt.Printf(string(b))
			fmt.Fprintf(w, string(b))
		})

	type ListFuncs struct {
		Functions []string `json:"functions"`
	}
	out := ListFuncs{[]string{
		"crop", "dart", "dtm", "ground", "height", "info", "radius", "statistical",
		"translate", "vo",
	}}

	router.GET("/api/v1/functions/:name",
		func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
			w.Header().Set("Content-Type", "application/json; charset=UTF-8")
			var a interface{}
			switch ps.ByName("name") {
			case "crop":
				a = functions.NewCropOptions()
				w.WriteHeader(http.StatusOK)

			case "dart":
				a = functions.NewDartOptions()
				w.WriteHeader(http.StatusOK)

			case "dtm":
				a = functions.NewDtmOptions()
				w.WriteHeader(http.StatusOK)

			case "ground":
				a = functions.NewGroundOptions()
				w.WriteHeader(http.StatusOK)

			case "height":
				w.WriteHeader(http.StatusOK)

			case "info":
				a = functions.NewInfoOptions()
				w.WriteHeader(http.StatusOK)

			case "radius":
				a = functions.NewRadiusOptions()
				w.WriteHeader(http.StatusOK)

			case "statistical":
				a = functions.NewStatisticalOptions()
				w.WriteHeader(http.StatusOK)

			case "translate":
				w.WriteHeader(http.StatusOK)

			case "vo":
				w.WriteHeader(http.StatusOK)

			default:
				type DefaultMsg struct {
					Message string `json:"message"`
					ListFuncs
				}
				msg := "Unrecognized function " + ps.ByName("name") + "."
				a = DefaultMsg{msg, out}
				w.WriteHeader(http.StatusBadRequest)
			}
			if err := json.NewEncoder(w).Encode(a); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		})

	router.GET("/api/v1/functions",
		func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
			w.Header().Set("Content-Type", "application/json; charset=UTF-8")
			w.WriteHeader(http.StatusOK)
			if err := json.NewEncoder(w).Encode(out); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
		})

	// Setup the PDAL service.
	router.Handler("POST", "/api/v1/pdal", appHandler(handlers.PdalHandler))

	router.Handler("POST", "/api/v1/pipeline", appHandler(handlers.PipelineHandler))

	router.Handler("POST", "/api/v1/vo", appHandler(handlers.VoHandler))

	var defaultPort = os.Getenv("PORT")
	if defaultPort == "" {
		defaultPort = "8080"
	}
	log.Println()

	log.Println("Starting on port ", defaultPort)
	if err := http.ListenAndServe(":"+defaultPort, router); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The Azul3D Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"azul3d.org/semver.v1"
)

const (
	githubOrg = "azul3d"
	host      = "azul3d.org"
	fileHost  = "azul3d.github.io"
	certFile  = "azul3d.org.pem"
	keyFile   = "azul3d.org.key"
)

var (
	lastIdlePurge = time.Now()
	pkgHandler    = &semver.Handler{
		Matcher:  semver.MatcherFunc(compatMatcher),
		Host:     host,
	}
	githubMatcher = semver.GitHub(githubOrg)
)

func compatMatcher(u *url.URL) (r *semver.Repo, err error) {
	// Special case just for glfw.v3.1 -- we made a bad mistake here and this
	// is purely for backwards compatability so you can still write the (bad)
	// import:
	//
	//  "azul3d.org/native/glfw.v3.1"
	//
	// All imports like this should be updated to just ".v3" instead.
	if u.Path == "/native/glfw.v3.1" {
		u.Path = "/native/glfw.v3"
	}

	// Special case just for .dev paths -- previously we suggested importing
	// in-development packages under the .dev extension (which violates semver
	// specification):
	//
	//  "azul3d.org/tmx.dev"
	//
	// Now we redirect those to .v0 -- which has the *same effect*, but only a
	// different meaning under semver specification. In-development packages
	// today are imported under either .v0 (no stable release yet) or .vN-dev:
	//
	//  "azul3d.org/tmx.v2-dev"
	//
	if strings.HasSuffix(u.Path, ".dev") {
		u.Path = strings.TrimSuffix(u.Path, ".dev") + ".v0"
	}

	// Special case for azul3d.org/cmd/foobar -- we just want go get to fetch
	// the latest version for these (which is effectively v0).
	if strings.HasPrefix(u.Path, "/cmd/") {
		u.Path = strings.TrimPrefix(u.Path, "/cmd/")
		u.Path = "/cmd.v0/" + u.Path
	}

	// Now let the github matcher perform the matching.
	return githubMatcher.Match(u)
}

func handler(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v %v\n", r.Method, r.URL)

	// Purge idle connections.
	if time.Since(lastIdlePurge) > 2*time.Hour {
		lastIdlePurge = time.Now()
		http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	}

	// Give our semver handler the ability to handle the request.
	status, err := pkgHandler.Handle(w, r)
	if err != nil {
		log.Println(err)
	}
	if status == semver.Handled {
		return
	}
	if status == semver.PkgPage {
		// Package page, redirect them to godoc.org documentation.
		tmp := *r.URL
		tmp.Scheme = "https"
		tmp.Host = "godoc.org"
		tmp.Path = path.Join(pkgHandler.Host, tmp.Path)
		http.Redirect(w, r, tmp.String(), http.StatusSeeOther)
		return
	}

	// Default to HTTPS scheme.
	if r.URL.Scheme == "" {
		r.URL.Scheme = "https"
	}

	// Just proxy the request to the file host then (it's an actual user -- not
	// the Go tool).
	r.RequestURI = ""
	delete(r.Header, "Content-Length")

	// Change Host in URL so that the request goes to the file host.
	r.URL.Host = fileHost

	// Force the Host header to be the file host (github.io uses this header).
	r.Host = fileHost

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		log.Printf("GET error: %v\n", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer resp.Body.Close()

	// Copy headers over.
	hdr := w.Header()
	for k, v := range resp.Header {
		hdr[k] = v
	}

	// Peek to detect the content type.
	br := bufio.NewReaderSize(resp.Body, 1024)
	if r.Method != "HEAD" && len(resp.Header.Get("If-Modified-Since")) != 0 {
		ident, err := br.Peek(512)
		if err != nil {
			log.Printf("Proxy peek error: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		hdr["Content-Type"] = []string{http.DetectContentType(ident)}
	}

	// Write the header / status code.
	w.WriteHeader(resp.StatusCode)

	// Copy the response to the user.
	_, err = io.Copy(w, br)
	if err != nil {
		log.Printf("Proxy copy error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	return
}

var (
	addr    = flag.String("http", ":80", "HTTP address to serve on")
	tlsaddr = flag.String("https", ":443", "HTTPS address to serve on")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", handler)

	// Start HTTPS server:
	go func() {
		if len(*tlsaddr) > 0 {
			log.Println("Serving on", *tlsaddr)
			err := http.ListenAndServeTLS(*tlsaddr, certFile, keyFile, nil)
			if err != nil {
				log.Fatal(err)
			}
		}
	}()

	// Start HTTP server:
	log.Println("Serving on", *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Remove special /cmd/ handling which broke azul3d.org/cmd/webgen.v0<commit_after>// Copyright 2014 The Azul3D Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"flag"
	"io"
	"log"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"azul3d.org/semver.v1"
)

const (
	githubOrg = "azul3d"
	host      = "azul3d.org"
	fileHost  = "azul3d.github.io"
	certFile  = "azul3d.org.pem"
	keyFile   = "azul3d.org.key"
)

var (
	lastIdlePurge = time.Now()
	pkgHandler    = &semver.Handler{
		Matcher:  semver.MatcherFunc(compatMatcher),
		Host:     host,
	}
	githubMatcher = semver.GitHub(githubOrg)
)

func compatMatcher(u *url.URL) (r *semver.Repo, err error) {
	// Special case just for glfw.v3.1 -- we made a bad mistake here and this
	// is purely for backwards compatability so you can still write the (bad)
	// import:
	//
	//  "azul3d.org/native/glfw.v3.1"
	//
	// All imports like this should be updated to just ".v3" instead.
	if u.Path == "/native/glfw.v3.1" {
		u.Path = "/native/glfw.v3"
	}

	// Special case just for .dev paths -- previously we suggested importing
	// in-development packages under the .dev extension (which violates semver
	// specification):
	//
	//  "azul3d.org/tmx.dev"
	//
	// Now we redirect those to .v0 -- which has the *same effect*, but only a
	// different meaning under semver specification. In-development packages
	// today are imported under either .v0 (no stable release yet) or .vN-dev:
	//
	//  "azul3d.org/tmx.v2-dev"
	//
	if strings.HasSuffix(u.Path, ".dev") {
		u.Path = strings.TrimSuffix(u.Path, ".dev") + ".v0"
	}

	// Now let the github matcher perform the matching.
	return githubMatcher.Match(u)
}

func handler(w http.ResponseWriter, r *http.Request) {
	log.Printf("%v %v\n", r.Method, r.URL)

	// Purge idle connections.
	if time.Since(lastIdlePurge) > 2*time.Hour {
		lastIdlePurge = time.Now()
		http.DefaultTransport.(*http.Transport).CloseIdleConnections()
	}

	// Give our semver handler the ability to handle the request.
	status, err := pkgHandler.Handle(w, r)
	if err != nil {
		log.Println(err)
	}
	if status == semver.Handled {
		return
	}
	if status == semver.PkgPage {
		// Package page, redirect them to godoc.org documentation.
		tmp := *r.URL
		tmp.Scheme = "https"
		tmp.Host = "godoc.org"
		tmp.Path = path.Join(pkgHandler.Host, tmp.Path)
		http.Redirect(w, r, tmp.String(), http.StatusSeeOther)
		return
	}

	// Default to HTTPS scheme.
	if r.URL.Scheme == "" {
		r.URL.Scheme = "https"
	}

	// Just proxy the request to the file host then (it's an actual user -- not
	// the Go tool).
	r.RequestURI = ""
	delete(r.Header, "Content-Length")

	// Change Host in URL so that the request goes to the file host.
	r.URL.Host = fileHost

	// Force the Host header to be the file host (github.io uses this header).
	r.Host = fileHost

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		log.Printf("GET error: %v\n", err)
		w.WriteHeader(http.StatusNotFound)
		return
	}
	defer resp.Body.Close()

	// Copy headers over.
	hdr := w.Header()
	for k, v := range resp.Header {
		hdr[k] = v
	}

	// Peek to detect the content type.
	br := bufio.NewReaderSize(resp.Body, 1024)
	if r.Method != "HEAD" && len(resp.Header.Get("If-Modified-Since")) != 0 {
		ident, err := br.Peek(512)
		if err != nil {
			log.Printf("Proxy peek error: %v\n", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		hdr["Content-Type"] = []string{http.DetectContentType(ident)}
	}

	// Write the header / status code.
	w.WriteHeader(resp.StatusCode)

	// Copy the response to the user.
	_, err = io.Copy(w, br)
	if err != nil {
		log.Printf("Proxy copy error: %v\n", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	return
}

var (
	addr    = flag.String("http", ":80", "HTTP address to serve on")
	tlsaddr = flag.String("https", ":443", "HTTPS address to serve on")
)

func main() {
	flag.Parse()
	http.HandleFunc("/", handler)

	// Start HTTPS server:
	go func() {
		if len(*tlsaddr) > 0 {
			log.Println("Serving on", *tlsaddr)
			err := http.ListenAndServeTLS(*tlsaddr, certFile, keyFile, nil)
			if err != nil {
				log.Fatal(err)
			}
		}
	}()

	// Start HTTP server:
	log.Println("Serving on", *addr)
	err := http.ListenAndServe(*addr, nil)
	if err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>// Package mysql implements MySQL migrations.
package mysql

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/blue-jay/core/storage"
	driver "github.com/blue-jay/core/storage/driver/mysql"
	"github.com/blue-jay/core/storage/migration"
	"github.com/jmoiron/sqlx"
)

// *****************************************************************************
// Thread-Safe Configuration
// *****************************************************************************
/*
var (
	info      database.Info
	infoMutex sync.RWMutex
)

// SetConfig stores the config.
func SetConfig(i database.Info) {
	infoMutex.Lock()
	info = i
	infoMutex.Unlock()
}

// ResetConfig removes the config.
func ResetConfig() {
	infoMutex.Lock()
	info = database.Info{}
	infoMutex.Unlock()
}

// Config returns the config.
func Config() database.Info {
	infoMutex.RLock()
	defer infoMutex.RUnlock()
	return info
}*/

// Configuration defines the shared configuration interface.
type Configuration struct {
	driver.Info
}

/*
// Shared returns the global configuration information.
func Shared() Configuration {
	return Configuration{
		Config(),
	}
}*/

// *****************************************************************************
// Migration Creation
// *****************************************************************************

// New creates a migration connection to the database.
func (c Configuration) New() (*migration.Info, error) {
	var mig *migration.Info

	// Load the config
	i := c.Info

	// Update the config
	i.Parameter = "parseTime=true&multiStatements=true"

	// Create MySQL entity
	mi := &Entity{}

	// Connect to the database
	con, err := i.Connect(true)

	// If the database doesn't exist or can't connect
	if err != nil {
		// Close the open connection (since 'unknown database' is still an
		// active connection)
		con.Close()

		// Connect to database without a database
		con, err = i.Connect(false)
		if err != nil {
			return mig, err
		}

		// Create the database
		err = i.Create(con)
		if err != nil {
			return mig, err
		}

		// Close connection
		con.Close()

		// Reconnect to the database
		con, err = i.Connect(true)
		if err != nil {
			return mig, err
		}
	}

	// Store the connection in the entity
	mi.sql = con

	// Store the migration table name
	mi.table = c.Migration.Table

	if len(mi.table) == 0 {
		return mig, errors.New("MySQL.Migration.Table key is missing in config file.")
	}

	// Setup logic was here
	return migration.New(mi, mi.table, c.Migration.Folder)
}

// *****************************************************************************
// Interface
// *****************************************************************************

// Item defines the migration table.
type Item struct {
	ID        uint32    `db:"id"`
	Name      string    `db:"name"`
	CreatedAt time.Time `db:"created_at"`
}

// Entity defines fulfills the migration interface.
type Entity struct {
	table string
	sql   *sqlx.DB
}

// Extension returns the file extension with a period
func (t *Entity) Extension() string {
	return ".sql"
}

// TableExist returns true if the migration table exists
func (t *Entity) TableExist() error {
	_, err := t.sql.Exec(fmt.Sprintf("SELECT 1 FROM %v LIMIT 1;", t.table))
	if err != nil {
		return err
	}

	return err
}

// CreateTable returns true if the migration was created
func (t *Entity) CreateTable() error {
	_, err := t.sql.Exec(fmt.Sprintf(`CREATE TABLE %v (
		id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  		name VARCHAR(191) NOT NULL,
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
		UNIQUE KEY (name),
  		PRIMARY KEY (id)
		);`, t.table))

	if err != nil {
		return err
	}

	return err
}

// Status returns last migration name
func (t *Entity) Status() (string, error) {
	result := &Item{}
	err := t.sql.Get(result, fmt.Sprintf("SELECT * FROM %v ORDER BY id DESC LIMIT 1;", t.table))

	// If no rows, then set to nil
	if err == sql.ErrNoRows {
		err = nil
	}

	return result.Name, err
}

// statusID returns last migration ID
func (t *Entity) statusID() (uint32, error) {
	result := &Item{}
	err := t.sql.Get(result, fmt.Sprintf("SELECT * FROM %v ORDER BY id DESC LIMIT 1;", t.table))
	return result.ID, err
}

// Migrate runs a query and returns error
func (t *Entity) Migrate(qry string) error {
	_, err := t.sql.Exec(qry)
	return err
}

// RecordUp adds a record to the database
func (t *Entity) RecordUp(name string) error {
	_, err := t.sql.Exec(fmt.Sprintf("INSERT INTO %v (name) VALUES (?);", t.table), name)
	return err
}

// RecordDown removes a record from the database and updates the AUTO_INCREMENT value
func (t *Entity) RecordDown(name string) error {
	_, err := t.sql.Exec(fmt.Sprintf("DELETE FROM %v WHERE name = ? LIMIT 1;", t.table), name)

	// If the record was removed successfully
	if err == nil {
		var ID uint32
		var nextID uint32 = 1

		// Get the last migration record now
		ID, err = t.statusID()

		// If there are no more migrations in the table
		if err == sql.ErrNoRows {
			// Leave ID at 1
		} else if err != nil {
			return err
		} else {
			nextID = ID
		}

		_, err = t.sql.Exec(fmt.Sprintf("ALTER TABLE %v AUTO_INCREMENT = %v;", t.table, nextID))
	}
	return err
}

// *****************************************************************************
// Test Helpers
// *****************************************************************************

// SetUp is a function for unit tests on a separate database.
func SetUp(envPath string, dbName string) (*migration.Info, Configuration) {
	// Get the environment variable
	if len(os.Getenv("JAYCONFIG")) == 0 {
		// Attempt to find env.json
		p, err := filepath.Abs(envPath)
		if err != nil {
			log.Fatalf("%v", err)
		}

		// Set the environment variable
		os.Setenv("JAYCONFIG", p)
	}

	// Load the config
	config, err := storage.LoadConfig(os.Getenv("JAYCONFIG"))
	if err != nil {
		log.Fatalf("%v", err)
	}
	config.MySQL.Database = dbName

	// Create the migration configuration
	conf := Configuration{
		config.MySQL,
	}

	// Create the migration
	mig, err := conf.New()
	if err != nil {
		log.Fatalf("%v", err)
	}

	// Refresh the data
	mig.DownAll()
	mig.UpAll()

	return mig, conf
}

// TearDown removes the unit test database.
func TearDown(db *sqlx.DB, dbName string) error {
	// Drop the database
	_, err := db.Exec(fmt.Sprintf(`DROP DATABASE %v;`, dbName))
	return err
}
<commit_msg>Fix the setup function helper to support relative folders correctly<commit_after>// Package mysql implements MySQL migrations.
package mysql

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/blue-jay/core/storage"
	driver "github.com/blue-jay/core/storage/driver/mysql"
	"github.com/blue-jay/core/storage/migration"
	"github.com/jmoiron/sqlx"
)

// *****************************************************************************
// Thread-Safe Configuration
// *****************************************************************************
/*
var (
	info      database.Info
	infoMutex sync.RWMutex
)

// SetConfig stores the config.
func SetConfig(i database.Info) {
	infoMutex.Lock()
	info = i
	infoMutex.Unlock()
}

// ResetConfig removes the config.
func ResetConfig() {
	infoMutex.Lock()
	info = database.Info{}
	infoMutex.Unlock()
}

// Config returns the config.
func Config() database.Info {
	infoMutex.RLock()
	defer infoMutex.RUnlock()
	return info
}*/

// Configuration defines the shared configuration interface.
type Configuration struct {
	driver.Info
}

/*
// Shared returns the global configuration information.
func Shared() Configuration {
	return Configuration{
		Config(),
	}
}*/

// *****************************************************************************
// Migration Creation
// *****************************************************************************

// New creates a migration connection to the database.
func (c Configuration) New() (*migration.Info, error) {
	var mig *migration.Info

	// Load the config
	i := c.Info

	// Update the config
	i.Parameter = "parseTime=true&multiStatements=true"

	// Create MySQL entity
	mi := &Entity{}

	// Connect to the database
	con, err := i.Connect(true)

	// If the database doesn't exist or can't connect
	if err != nil {
		// Close the open connection (since 'unknown database' is still an
		// active connection)
		con.Close()

		// Connect to database without a database
		con, err = i.Connect(false)
		if err != nil {
			return mig, err
		}

		// Create the database
		err = i.Create(con)
		if err != nil {
			return mig, err
		}

		// Close connection
		con.Close()

		// Reconnect to the database
		con, err = i.Connect(true)
		if err != nil {
			return mig, err
		}
	}

	// Store the connection in the entity
	mi.sql = con

	// Store the migration table name
	mi.table = c.Migration.Table

	if len(mi.table) == 0 {
		return mig, errors.New("MySQL.Migration.Table key is missing in config file.")
	}

	// Setup logic was here
	return migration.New(mi, mi.table, c.Migration.Folder)
}

// *****************************************************************************
// Interface
// *****************************************************************************

// Item defines the migration table.
type Item struct {
	ID        uint32    `db:"id"`
	Name      string    `db:"name"`
	CreatedAt time.Time `db:"created_at"`
}

// Entity defines fulfills the migration interface.
type Entity struct {
	table string
	sql   *sqlx.DB
}

// Extension returns the file extension with a period
func (t *Entity) Extension() string {
	return ".sql"
}

// TableExist returns true if the migration table exists
func (t *Entity) TableExist() error {
	_, err := t.sql.Exec(fmt.Sprintf("SELECT 1 FROM %v LIMIT 1;", t.table))
	if err != nil {
		return err
	}

	return err
}

// CreateTable returns true if the migration was created
func (t *Entity) CreateTable() error {
	_, err := t.sql.Exec(fmt.Sprintf(`CREATE TABLE %v (
		id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  		name VARCHAR(191) NOT NULL,
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
		UNIQUE KEY (name),
  		PRIMARY KEY (id)
		);`, t.table))

	if err != nil {
		return err
	}

	return err
}

// Status returns last migration name
func (t *Entity) Status() (string, error) {
	result := &Item{}
	err := t.sql.Get(result, fmt.Sprintf("SELECT * FROM %v ORDER BY id DESC LIMIT 1;", t.table))

	// If no rows, then set to nil
	if err == sql.ErrNoRows {
		err = nil
	}

	return result.Name, err
}

// statusID returns last migration ID
func (t *Entity) statusID() (uint32, error) {
	result := &Item{}
	err := t.sql.Get(result, fmt.Sprintf("SELECT * FROM %v ORDER BY id DESC LIMIT 1;", t.table))
	return result.ID, err
}

// Migrate runs a query and returns error
func (t *Entity) Migrate(qry string) error {
	_, err := t.sql.Exec(qry)
	return err
}

// RecordUp adds a record to the database
func (t *Entity) RecordUp(name string) error {
	_, err := t.sql.Exec(fmt.Sprintf("INSERT INTO %v (name) VALUES (?);", t.table), name)
	return err
}

// RecordDown removes a record from the database and updates the AUTO_INCREMENT value
func (t *Entity) RecordDown(name string) error {
	_, err := t.sql.Exec(fmt.Sprintf("DELETE FROM %v WHERE name = ? LIMIT 1;", t.table), name)

	// If the record was removed successfully
	if err == nil {
		var ID uint32
		var nextID uint32 = 1

		// Get the last migration record now
		ID, err = t.statusID()

		// If there are no more migrations in the table
		if err == sql.ErrNoRows {
			// Leave ID at 1
		} else if err != nil {
			return err
		} else {
			nextID = ID
		}

		_, err = t.sql.Exec(fmt.Sprintf("ALTER TABLE %v AUTO_INCREMENT = %v;", t.table, nextID))
	}
	return err
}

// *****************************************************************************
// Test Helpers
// *****************************************************************************

// SetUp is a function for unit tests on a separate database.
func SetUp(envPath string, dbName string) (*migration.Info, Configuration) {
	// Get the environment variable
	if len(os.Getenv("JAYCONFIG")) == 0 {
		// Attempt to find env.json
		p, err := filepath.Abs(envPath)
		if err != nil {
			log.Fatalf("%v", err)
		}

		// Set the environment variable
		os.Setenv("JAYCONFIG", p)
	}

	// Get the config file path
	configFile := os.Getenv("JAYCONFIG")

	// Load the config
	config, err := storage.LoadConfig(configFile)
	if err != nil {
		log.Fatalf("%v", err)
	}

	// Set the database name
	config.MySQL.Database = dbName

	// Set the migration folder to the absolute path
	config.MySQL.Migration.Folder = filepath.Join(filepath.Dir(configFile), config.MySQL.Migration.Folder)

	// Create the migration configuration
	conf := Configuration{
		config.MySQL,
	}

	// Create the migration
	mig, err := conf.New()
	if err != nil {
		log.Fatalf("%v", err)
	}

	// Refresh the data
	mig.DownAll()
	mig.UpAll()

	return mig, conf
}

// TearDown removes the unit test database.
func TearDown(db *sqlx.DB, dbName string) error {
	// Drop the database
	_, err := db.Exec(fmt.Sprintf(`DROP DATABASE %v;`, dbName))
	return err
}
<|endoftext|>


<commit_before>package aranGO

import (
    "errors"
    "strconv"
)

// Aql query
type Query struct {
	// mandatory
	Aql string `json:"query,omitempty"`
	//Optional values
	Batch    int                    `json:"batchSize,omitempty"`
	Count    bool                   `json:"count,omitempty"`
	BindVars map[string]interface{} `json:"bindVars,omitempty"`
	Options  map[string]interface{} `json:"options,omitempty"`
  // opetions fullCount bool
  // Note that the fullCount sub-attribute will only be present in the result if the query has a LIMIT clause and the LIMIT clause is actually used in the query.
	// Control
	Validate bool   `json:"-"`
	ErrorMsg string `json:"errorMessage,omitempty"`
}

func NewQuery(query string) *Query {
  var q Query
  // alocate maps
  q.Options = make(map[string]interface{})
  q.BindVars= make(map[string]interface{})

	if query == "" {
		return &q
	} else {
		q.Aql = query
		return &q
	}
}

func (q *Query) Modify(query string) error {
	if query == "" {
		return errors.New("query must not be empty")
	} else {
		q.Aql = query
		return nil
	}
}

// Validate query before execution
func (q *Query) MustCheck() {
	q.Validate = true
	return
}

type AqlStructer interface{
  Generate()     string
}

// Basic structure
type AqlStruct struct {
  // main loop var
  main        string
  list        string
  lines       []AqlStructer
  vars        map[string]string
  // Return
  // could be string or AqlStruct
  //View        `json:"view"`
}

func (aq *AqlStruct) For(obj string) *AqlStruct{
  aq.main = obj
  return aq
}

func (aq *AqlStruct) In(list string) *AqlStruct {
  aq.list = list
  return aq
}

func (aq *AqlStruct) Generate() string{
  q:= "FOR "+aq.main+" IN "+aq.list

  for _,line :=range(aq.lines){
    q+= line.Generate()
  }

  return q
}

// Custom struct while I implement more strutures
func (aq *AqlStruct) Custom(code string) *AqlStruct{
  var c Custom
  c.Code = code
  aq.lines = append(aq.lines,c)
  return aq
}

type Custom struct {
  Code string
}

func (c Custom) Generate() string {
  return `
  `+c.Code
}

func (aq *AqlStruct) Loop(v string,list string) *AqlStruct {
  var l Loop
  if v != "" {
    l.Var = v
    l.List = list
  }
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Return(v map[string]interface{}) *AqlStruct {
  var vie View
  vie = v
  aq.lines = append(aq.lines,vie)
  return aq
}

func (aq *AqlStruct) Group(gs map[string]Var,into string) *AqlStruct{
  if gs != nil {
    var c Collects
    c.Collect = gs
    if into != ""{
      c.Gro = into
    }
    aq.lines = append(aq.lines,c)
  }
  return aq
}

func (aq *AqlStruct) Filter(key string,values []Pair) *AqlStruct{
  var fil Filters
  if key != "" && values != nil{
    fil.Key = key
    fil.Filter = values
    aq.lines = append(aq.lines,fil)
  }
  return aq
}

type View map[string]interface{}

func (v View) Generate() string{
  q:= `
RETURN { `
  i:=0
  for key,inte := range(v) {
    q += key+":"
    switch inte.(type) {
      case Var:
        q += inte.(Var).Obj+"."+inte.(Var).Name
      case string:
        q += "'"+inte.(string)+"'"
      case int:
        q += strconv.Itoa(inte.(int))
      case int32:
        q += strconv.FormatInt(inte.(int64),10)
      case int64:
        q += strconv.FormatInt(inte.(int64),10)
      case AqlStruct,*AqlStruct:
        q += "( "+inte.(*AqlStruct).Generate()+" )"
    }
    if len(v)-1 != i {
      q+=","
    }
    i++
  }
  q+= " }"
  return q
}

type Collects struct{
  // COLLECT key = Obj.Var,..., INTO Gro
  Collect map[string]Var `json:"collect"`
  Gro   string              `json:"group"`
}

type Group struct{
  Obj   string  `json:"obj"`
  Var   string  `json:"var"`
}

func (c Collects) Generate() string{
  if c.Collect == nil {
    return ""
  }
  q:= `
COLLECT `
  i:= 0
  for key,group := range(c.Collect){
    if i == len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name
    }

    if i < len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name+","
    }

    i++
  }
  if c.Gro != ""{
    q += " INTO "+c.Gro
  }
  return q
}

type Limits struct{
  Skip   int64 `json:"skip"`
  Limit  int64 `json:"limit"`
}

func (l Limits) Generate() string {
  skip := strconv.FormatInt(l.Skip,10)
  limit:= strconv.FormatInt(l.Limit,10)
  li := `
LIMIT `+skip+`,`+limit
  return li
}

func (aq *AqlStruct) Limit(skip,limit int64) *AqlStruct{
  var l Limits
  l.Skip = skip
  l.Limit = limit
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Let(v string,i interface{}) *AqlStruct{
  // validate type
  switch i.(type){
    case string:
    case *AqlStruct:
    default:
      return aq
  }

  var f Lets
  if v != ""{
    f.Var = v
    f.Comm = i
  }else{
     return aq
  }
  aq.lines = append(aq.lines,f)
  return aq
}

type Lets struct {
  Var     string         `json:"var"`
  Comm    interface{}    `json:"comm"`
}

func (l Lets) Generate() string {
  q := `
LET `+l.Var+` = (`
  switch l.Comm.(type) {
    case string:
        q += l.Comm.(string)
    case *AqlStruct:
        q += l.Comm.(*AqlStruct).Generate()
  }
  q += `)`
  return q
}

type Filters struct {
  Key    string  `json:"key"`
  Filter []Pair  `json:"conditions"`
}

type Pair struct {
  Obj     string      `json:"obj"`
  Logic   string      `json:"op"`
  Value   interface{} `json:"val"`
}

func (f Filters) Generate() string{
  // check if filters available
  if len(f.Filter) == 0 {
    return ""
  }
  var oper      string

  lenmap := 0
  q := ""

  if f.Filter == nil{
    return ""
  }

  pairs := f.Filter
  key   := f.Key
  // iterate over filters
  // first
  q += `
FILTER ( `
  oper = "||"

  for i,pair := range(pairs){
    if i == len(pairs) -1 {
      oper = ""
    }
    switch pair.Value.(type) {
      case bool:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatBool(pair.Value.(bool))+" "+oper
      case int:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.Itoa(pair.Value.(int))+" "+oper
      case int64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatInt(pair.Value.(int64),10)+" "+oper
      case string:
        q += key+"."+pair.Obj+" "+pair.Logic+" '"+pair.Value.(string)+"' "+oper
      case float32,float64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatFloat(pair.Value.(float64),'f',6,64)+" "+oper
      case Var:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+pair.Value.(Var).Obj+"."+pair.Value.(Var).Name+" "+oper
    }
    if i == len(pairs)-1{
      q += ")"
    }
  }
  // next key
  lenmap++
  return q
}

type Loop struct {
  Var  string
  List string
}

func (l Loop) Generate() string{
  q := `
FOR `+l.Var+` IN `+l.List
  return q
}

// Variable into document
type Var  struct {
  Obj     string      `json:"obj"`
  Name    string      `json:"name"`
}

type AqlUserFunction struct {
  Name string `json:"name"`
  Code string `json:"code"`
  Deterministic bool `json:"isDeterministic"`
}

func NewAqlFunction(name string,code string,deter bool) *AqlUserFunction{
  var aqf AqlUserFunction
  if name != "" && code != ""{
    aqf.Name = name
    aqf.Code = code
    return &aqf
  }
  return nil
}

// Register new user function
func (a *AqlUserFunction) Register(db Database) error{
  res, err := db.send("aqlfunction","","POST",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Failed to create aqlfunction")
    default:
      return nil
  }
}

func (a *AqlUserFunction) Delete(db Database,name string) error{
  res, err := db.send("aqlfunction",name,"DELETE",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Invalid name")
    case 404:
      return errors.New("Function don't exist")
    default:
      return nil
  }
}

type FullTextSearch struct {
  Code  string
  List  string
}

// Generate AQL Code to do fulltext search over multiple indexes using UNION to join lists
func FullText(words []string,indexes []string,col string) *FullTextSearch{
  var t FullTextSearch
  var i,j int

  lindex := 0
  for i = 0 ; i<len(words) ; i++ {
    for j = 0 ; j <len(indexes) ; j++ {
      // add list to array
      lindex++
      t.Code += `LET l`+strconv.Itoa(lindex)+`=(FOR i IN FULLTEXT(`+col+`,"`+indexes[j]+`","prefix:`+words[i]+`,|`+words[i]+`") RETURN i)
      `
    }
  }
  t.List = "UNION_DISTINCT("
  for i = 1 ; i < lindex ;i++{
    t.List +="l"+strconv.Itoa(i)+","
  }

  t.List +="l"+strconv.Itoa(lindex)+")"

  return &t
}

<commit_msg>remove experimental aql builder<commit_after>package aranGO

import (
    "errors"
    "strconv"
)

// Aql query
type Query struct {
	// mandatory
	Aql string `json:"query,omitempty"`
	//Optional values
	Batch    int                    `json:"batchSize,omitempty"`
	Count    bool                   `json:"count,omitempty"`
	BindVars map[string]interface{} `json:"bindVars,omitempty"`
	Options  map[string]interface{} `json:"options,omitempty"`
  // opetions fullCount bool
  // Note that the fullCount sub-attribute will only be present in the result if the query has a LIMIT clause and the LIMIT clause is actually used in the query.
	// Control
	Validate bool   `json:"-"`
	ErrorMsg string `json:"errorMessage,omitempty"`
}

func NewQuery(query string) *Query {
  var q Query
  // alocate maps
  q.Options = make(map[string]interface{})
  q.BindVars= make(map[string]interface{})

	if query == "" {
		return &q
	} else {
		q.Aql = query
		return &q
	}
}

func (q *Query) Modify(query string) error {
	if query == "" {
		return errors.New("query must not be empty")
	} else {
		q.Aql = query
		return nil
	}
}
<|endoftext|>


<commit_before>package bagman

import (
	"archive/tar"
	"path/filepath"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"github.com/APTrust/bagins"
)

type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
}

// Untars the file at the specified path and returns a list
// of files that were untarred from the archive. Check
// result.Error to ensure there were no errors.
func Untar(path string) (result *TarResult) {

	// Set up our result
	tarResult := new(TarResult)
	absInputFile, err := filepath.Abs(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	tarResult.InputFile = absInputFile

	// Open the tar file for reading.
	file, err := os.Open(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	defer file.Close()

	// Untar the file and record the results.
	tarReader := tar.NewReader(file)
	for {
		header, err := tarReader.Next();
		if err != nil && err.Error() == "EOF" {
			break  // end of archive
		}
		if err != nil {
			tarResult.Error = err
			return tarResult
		}
		outputPath := filepath.Join(filepath.Dir(absInputFile), header.Name)
		tarDirectory := strings.Split(header.Name, "/")[0]
		if tarResult.OutputDir == "" {
			tarResult.OutputDir = filepath.Join(filepath.Dir(absInputFile), tarDirectory)
		}

		// Make sure the directory that we're about to write into exists.
		err = os.MkdirAll(filepath.Dir(outputPath), 0755)
		if err != nil {
			tarResult.Error = err
			return tarResult
		}

		// Copy the file, if it's an actual file. Otherwise, ignore it and record
		// a warning. The bag library does not deal with items like symlinks.
		if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA {
			outputWriter, err := os.OpenFile(outputPath, os.O_CREATE | os.O_WRONLY, 0644)
			if err != nil {
				tarResult.Error = err
				return tarResult
			}
			defer outputWriter.Close()
			io.Copy(outputWriter, tarReader);
			outputRelativePath := strings.Replace(outputPath, tarResult.OutputDir + "/", "", 1)
			tarResult.FilesUnpacked = append(tarResult.FilesUnpacked, outputRelativePath)
		} else if header.Typeflag != tar.TypeDir {
			tarResult.Warnings = append(tarResult.Warnings,
				fmt.Sprintf("Ignoring item %s of type %c because it's neither a file nor a directory",
					header.Name, header.Typeflag))
		}
	}
	sort.Strings(tarResult.FilesUnpacked)
	return tarResult
}

type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []bagins.TagField
	ChecksumErrors   []error
}

// Reads an untarred bag. The path parameter should be a path to
// a directory that contains the bag, info and manifest files.
// The bag content should be in the data directory under path.
// Check result.Error to ensure there were no errors.
func ReadBag(path string) (result *BagReadResult) {
	bagReadResult := new(BagReadResult)
	bagReadResult.Path = path

	bag, err := bagins.ReadBag(path, []string{"bagit.txt", "bag-info.txt", "aptrust-info.txt"}, "") //"manifest-md5.txt")
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}

	fileNames, err := bag.ListFiles()
	if err!= nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	bagReadResult.Files = make([]string, len(fileNames))
	copy(bagReadResult.Files, fileNames)

	bagInfo, err := bag.BagInfo()
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	tagFields := bagInfo.Data.Fields()
	bagReadResult.Tags = make([]bagins.TagField, len(tagFields))
	copy(bagReadResult.Tags, tagFields)

	checksumErrors := bag.Manifest.RunChecksums()
	if len(checksumErrors) > 0 {
		bagReadResult.Error = errors.New("One or more checksums were invalid.")
		bagReadResult.ChecksumErrors = make([]error, len(checksumErrors))
		copy(bagReadResult.ChecksumErrors, checksumErrors)
	}
	return bagReadResult
}
<commit_msg>Report missing bagit and manifest files<commit_after>package bagman

import (
	"archive/tar"
	"path/filepath"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"github.com/APTrust/bagins"
)

type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
}

// Untars the file at the specified path and returns a list
// of files that were untarred from the archive. Check
// result.Error to ensure there were no errors.
func Untar(path string) (result *TarResult) {

	// Set up our result
	tarResult := new(TarResult)
	absInputFile, err := filepath.Abs(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	tarResult.InputFile = absInputFile

	// Open the tar file for reading.
	file, err := os.Open(path)
	if err != nil {
		tarResult.Error = err
		return tarResult
	}
	defer file.Close()

	// Untar the file and record the results.
	tarReader := tar.NewReader(file)
	for {
		header, err := tarReader.Next();
		if err != nil && err.Error() == "EOF" {
			break  // end of archive
		}
		if err != nil {
			tarResult.Error = err
			return tarResult
		}
		outputPath := filepath.Join(filepath.Dir(absInputFile), header.Name)
		tarDirectory := strings.Split(header.Name, "/")[0]
		if tarResult.OutputDir == "" {
			tarResult.OutputDir = filepath.Join(filepath.Dir(absInputFile), tarDirectory)
		}

		// Make sure the directory that we're about to write into exists.
		err = os.MkdirAll(filepath.Dir(outputPath), 0755)
		if err != nil {
			tarResult.Error = err
			return tarResult
		}

		// Copy the file, if it's an actual file. Otherwise, ignore it and record
		// a warning. The bag library does not deal with items like symlinks.
		if header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA {
			outputWriter, err := os.OpenFile(outputPath, os.O_CREATE | os.O_WRONLY, 0644)
			if err != nil {
				tarResult.Error = err
				return tarResult
			}
			defer outputWriter.Close()
			io.Copy(outputWriter, tarReader);
			outputRelativePath := strings.Replace(outputPath, tarResult.OutputDir + "/", "", 1)
			tarResult.FilesUnpacked = append(tarResult.FilesUnpacked, outputRelativePath)
		} else if header.Typeflag != tar.TypeDir {
			tarResult.Warnings = append(tarResult.Warnings,
				fmt.Sprintf("Ignoring item %s of type %c because it's neither a file nor a directory",
					header.Name, header.Typeflag))
		}
	}
	sort.Strings(tarResult.FilesUnpacked)
	return tarResult
}

type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []bagins.TagField
	ChecksumErrors   []error
}

// Reads an untarred bag. The path parameter should be a path to
// a directory that contains the bag, info and manifest files.
// The bag content should be in the data directory under path.
// Check result.Error to ensure there were no errors.
func ReadBag(path string) (result *BagReadResult) {
	bagReadResult := new(BagReadResult)
	bagReadResult.Path = path

	// Final param to bagins.ReadBag is the name of the checksum file.
	// That param defaults to manifest-md5.txt, which is what it
	// should be for bags we're fetching from the S3 receiving buckets.
	bag, err := bagins.ReadBag(path, []string{"bagit.txt", "bag-info.txt", "aptrust-info.txt"}, "")
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}

	fileNames, err := bag.ListFiles()
	if err!= nil {
		bagReadResult.Error = err
		return bagReadResult
	}

	errMsg := ""
	bagReadResult.Files = make([]string, len(fileNames))
	hasBagit := false
	hasMd5Manifest := false
	for index, fileName := range fileNames {
		bagReadResult.Files[index] = fileName
		if fileName == "bagit.txt" {
			hasBagit = true
		} else if fileName == "manifest-md5.txt" {
			hasMd5Manifest = true
		}
	}
	if !hasBagit { errMsg += "Bag is missing bagit.txt file. " }
	if !hasMd5Manifest { errMsg += "Bag is missing manifest-md5.txt file. " }

	bagInfo, err := bag.BagInfo()
	if err != nil {
		bagReadResult.Error = err
		return bagReadResult
	}
	tagFields := bagInfo.Data.Fields()
	bagReadResult.Tags = make([]bagins.TagField, len(tagFields))
	copy(bagReadResult.Tags, tagFields)

	checksumErrors := bag.Manifest.RunChecksums()
	if len(checksumErrors) > 0 {
		errMsg += "One or more checksums are invalid."
		bagReadResult.ChecksumErrors = make([]error, len(checksumErrors))
		copy(bagReadResult.ChecksumErrors, checksumErrors)
	}

	if errMsg != "" {
		bagReadResult.Error = errors.New(errMsg)
	}

	return bagReadResult
}
<|endoftext|>


<commit_before>package uiprogress

import (
	"fmt"
	"time"
)

var (
	// Fill is the default character representing completed progress
	Fill byte = '='

	// Head is the default character that moves when progress is updated
	Head byte = '>'

	// Empty is the default character that represents the empty progress
	Empty byte = '-'

	// LeftEnd is the default character in the left most part of the progress indicator
	LeftEnd byte = '['

	// RightEnd is the default character in the right most part of the progress indicator
	RightEnd byte = ']'

	// Width is the default width of the progress bar
	Width = 70
)

// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(s *Statistics) string

type decoratorFuncType uint

const (
	decoratorAppend decoratorFuncType = iota
	decoratorPrepend
)

type decorator struct {
	kind decoratorFuncType
	f    DecoratorFunc
}

// Bar represents a progress bar
type Bar struct {
	// total of the total  for the progress bar
	total int

	// LeftEnd is character in the left most part of the progress indicator. Defaults to '['
	LeftEnd byte

	// RightEnd is character in the right most part of the progress indicator. Defaults to ']'
	RightEnd byte

	// Fill is the character representing completed progress. Defaults to '='
	Fill byte

	// Head is the character that moves when progress is updated.  Defaults to '>'
	Head byte

	// Empty is the character that represents the empty progress. Default is '-'
	Empty byte

	// Width is the width of the progress bar
	Width int

	Alpha float64

	currentIncrCh chan int

	redrawRequestCh chan *redrawRequest

	decoratorCh chan *decorator

	timePerItemEstimate time.Duration
}

type Statistics struct {
	Total, Completed    int
	TimePerItemEstimate time.Duration
}

type redrawRequest struct {
	bufch chan []byte
}

// NewBar returns a new progress bar
func NewBar(total int) *Bar {
	b := &Bar{
		Alpha:           0.25,
		total:           total,
		Width:           Width,
		LeftEnd:         LeftEnd,
		RightEnd:        RightEnd,
		Head:            Head,
		Fill:            Fill,
		Empty:           Empty,
		currentIncrCh:   make(chan int),
		redrawRequestCh: make(chan *redrawRequest),
		decoratorCh:     make(chan *decorator),
	}
	go b.server()
	return b
}

func (b *Bar) Incr(n int) {
	b.currentIncrCh <- n
}

func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorAppend, f}
	return b
}

func (b *Bar) AppendETA() *Bar {
	b.AppendFunc(func(s *Statistics) string {
		eta := time.Duration(s.Total-s.Completed) * s.TimePerItemEstimate
		return fmt.Sprint(time.Duration(eta.Seconds()) * time.Second)
	})
	return b
}

// String returns the string representation of the bar
func (b *Bar) String() string {
	bufch := make(chan []byte)
	b.redrawRequestCh <- &redrawRequest{bufch}
	return string(<-bufch)
}

func (b *Bar) server() {
	var current int
	blockStartTime := time.Now()
	buf := make([]byte, b.Width, b.Width+9)
	var appendFuncs []DecoratorFunc
	var prependFuncs []DecoratorFunc
	for {
		select {
		case i := <-b.currentIncrCh:
			n := current + i
			if n > b.total {
				return
			}
			b.updateTimePerItemEstimate(i, blockStartTime)
			current = n
			blockStartTime = time.Now()
		case d := <-b.decoratorCh:
			switch d.kind {
			case decoratorAppend:
				appendFuncs = append(appendFuncs, d.f)
			case decoratorPrepend:
				prependFuncs = append(prependFuncs, d.f)
			}
		case r := <-b.redrawRequestCh:
			r.bufch <- b.draw(buf, current, appendFuncs)
		}
	}
}

func (b *Bar) draw(buf []byte, current int, appendFuncs []DecoratorFunc) []byte {
	completedWidth := current * b.Width / b.total

	for i := 0; i < completedWidth; i++ {
		buf[i] = b.Fill
	}
	for i := completedWidth; i < b.Width; i++ {
		buf[i] = b.Empty
	}
	// set head bit
	if completedWidth > 0 && completedWidth < b.Width {
		buf[completedWidth-1] = b.Head
	}

	// set left and right ends bits
	buf[0], buf[len(buf)-1] = b.LeftEnd, b.RightEnd

	s := &Statistics{b.total, current, b.timePerItemEstimate}

	// render append functions to the right of the bar
	for _, f := range appendFuncs {
		buf = append(buf, ' ')
		buf = append(buf, []byte(f(s))...)
	}

	// render prepend functions to the left of the bar
	// for _, f := range b.prependFuncs {
	// 	args := []byte(f(b))
	// 	args = append(args, ' ')
	// 	pb = append(args, pb...)
	// }
	return buf
}

func (b *Bar) updateTimePerItemEstimate(items int, blockStartTime time.Time) {
	lastBlockTime := time.Since(blockStartTime)
	lastItemEstimate := float64(lastBlockTime) / float64(items)
	b.timePerItemEstimate = time.Duration((b.Alpha * lastItemEstimate) + (1-b.Alpha)*float64(b.timePerItemEstimate))
}

// func nextTimePerItemEstimate(d time.Duration, blockStartTime time.Time, alpha float64, items int) time.Duration {
// 	lastBlockTime := time.Since(blockStartTime)
// 	lastItemEstimate := float64(lastBlockTime) / float64(items)
// 	return time.Duration((alpha * lastItemEstimate) + (1-alpha)*float64(d))
// }
<commit_msg>AppendETA<commit_after>package uiprogress

import (
	"fmt"
	"time"
)

var (
	// Fill is the default character representing completed progress
	Fill byte = '='

	// Head is the default character that moves when progress is updated
	Head byte = '>'

	// Empty is the default character that represents the empty progress
	Empty byte = '-'

	// LeftEnd is the default character in the left most part of the progress indicator
	LeftEnd byte = '['

	// RightEnd is the default character in the right most part of the progress indicator
	RightEnd byte = ']'

	// Width is the default width of the progress bar
	Width = 70
)

// DecoratorFunc is a function that can be prepended and appended to the progress bar
type DecoratorFunc func(s *Statistics) string

type decoratorFuncType uint

const (
	decoratorAppend decoratorFuncType = iota
	decoratorPrepend
)

type decorator struct {
	kind decoratorFuncType
	f    DecoratorFunc
}

// Bar represents a progress bar
type Bar struct {
	// total of the total  for the progress bar
	total int

	// LeftEnd is character in the left most part of the progress indicator. Defaults to '['
	LeftEnd byte

	// RightEnd is character in the right most part of the progress indicator. Defaults to ']'
	RightEnd byte

	// Fill is the character representing completed progress. Defaults to '='
	Fill byte

	// Head is the character that moves when progress is updated.  Defaults to '>'
	Head byte

	// Empty is the character that represents the empty progress. Default is '-'
	Empty byte

	// Width is the width of the progress bar
	Width int

	Alpha float64

	currentIncrCh chan int

	redrawRequestCh chan *redrawRequest

	decoratorCh chan *decorator

	timePerItemEstimate time.Duration
}

type Statistics struct {
	Total, Completed    int
	TimePerItemEstimate time.Duration
}

type redrawRequest struct {
	bufch chan []byte
}

// NewBar returns a new progress bar
func NewBar(total int) *Bar {
	b := &Bar{
		Alpha:           0.25,
		total:           total,
		Width:           Width,
		LeftEnd:         LeftEnd,
		RightEnd:        RightEnd,
		Head:            Head,
		Fill:            Fill,
		Empty:           Empty,
		currentIncrCh:   make(chan int),
		redrawRequestCh: make(chan *redrawRequest),
		decoratorCh:     make(chan *decorator),
	}
	go b.server()
	return b
}

func (b *Bar) Incr(n int) {
	b.currentIncrCh <- n
}

func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
	b.decoratorCh <- &decorator{decoratorAppend, f}
	return b
}

func (b *Bar) AppendETA() *Bar {
	b.AppendFunc(func(s *Statistics) string {
		eta := time.Duration(s.Total-s.Completed) * s.TimePerItemEstimate
		return fmt.Sprintf("ETA %v", time.Duration(eta.Seconds())*time.Second)
	})
	return b
}

// String returns the string representation of the bar
func (b *Bar) String() string {
	bufch := make(chan []byte)
	b.redrawRequestCh <- &redrawRequest{bufch}
	return string(<-bufch)
}

func (b *Bar) server() {
	var current int
	blockStartTime := time.Now()
	buf := make([]byte, b.Width, b.Width+24)
	var appendFuncs []DecoratorFunc
	var prependFuncs []DecoratorFunc
	for {
		select {
		case i := <-b.currentIncrCh:
			n := current + i
			if n > b.total {
				return
			}
			b.updateTimePerItemEstimate(i, blockStartTime)
			current = n
			blockStartTime = time.Now()
		case d := <-b.decoratorCh:
			switch d.kind {
			case decoratorAppend:
				appendFuncs = append(appendFuncs, d.f)
			case decoratorPrepend:
				prependFuncs = append(prependFuncs, d.f)
			}
		case r := <-b.redrawRequestCh:
			r.bufch <- b.draw(buf, current, appendFuncs, prependFuncs)
		}
	}
}

func (b *Bar) draw(buf []byte, current int, appendFuncs, prependFuncs []DecoratorFunc) []byte {
	completedWidth := current * b.Width / b.total

	for i := 0; i < completedWidth; i++ {
		buf[i] = b.Fill
	}
	for i := completedWidth; i < b.Width; i++ {
		buf[i] = b.Empty
	}
	// set head bit
	if completedWidth > 0 && completedWidth < b.Width {
		buf[completedWidth-1] = b.Head
	}

	// set left and right ends bits
	buf[0], buf[len(buf)-1] = b.LeftEnd, b.RightEnd

	s := &Statistics{b.total, current, b.timePerItemEstimate}

	// render append functions to the right of the bar
	for _, f := range appendFuncs {
		buf = append(buf, ' ')
		buf = append(buf, []byte(f(s))...)
	}

	// render prepend functions to the left of the bar
	// for _, f := range b.prependFuncs {
	// 	args := []byte(f(b))
	// 	args = append(args, ' ')
	// 	pb = append(args, pb...)
	// }
	return buf
}

func (b *Bar) updateTimePerItemEstimate(items int, blockStartTime time.Time) {
	lastBlockTime := time.Since(blockStartTime)
	lastItemEstimate := float64(lastBlockTime) / float64(items)
	b.timePerItemEstimate = time.Duration((b.Alpha * lastItemEstimate) + (1-b.Alpha)*float64(b.timePerItemEstimate))
}

// func nextTimePerItemEstimate(d time.Duration, blockStartTime time.Time, alpha float64, items int) time.Duration {
// 	lastBlockTime := time.Since(blockStartTime)
// 	lastItemEstimate := float64(lastBlockTime) / float64(items)
// 	return time.Duration((alpha * lastItemEstimate) + (1-alpha)*float64(d))
// }
<|endoftext|>