<commit_before>package gautomator

import (
	"github.com/gonum/matrix/mat64" // Matrix
	"log"
	"math/rand" // Temp
	//"strconv"
	"sync"
	"time"
)

func random(min int, max int) int {
	var bytes int
	bytes = min + rand.Intn(max)
	return int(bytes)
	//rand.Seed(time.Now().UTC().UnixNano())
	//return rand.Intn(max - min) + min
}

// Ths runner goroutine is a goroutinei which:
// Consume the TaskGraphStructure from a channel
// run the task given as arguments if the deps are done
// Post the task to the doncChannel once done
//
func Runner(task *Task, doneChan chan<- *Task, wg *sync.WaitGroup) {
	log.Printf("[%v:%v] Queued", task.Id, task.Name)
	for {
		letsGo := <-task.TaskCanRunChan
		// For each dependency of the task
		// We can run if the sum of the element of the column Id of the current task is 0

		if letsGo == true {
			proto := "tcp"
			socket := task.Node
			// Stupid trick to make shell works... A Shell module will be implemented later"
			if task.Module == "shell" {
				task.Module = "echo"
				task.Args = append(task.Args, "|")
				task.Args = append(task.Args, "/bin/ksh")
			}
			task.Status = -1
			log.Printf("[%v:%v] Running (%v %v)", task.Id, task.Name, task.Module, task.Args[0])
			log.Printf("[%v] Connecting in %v on %v", task.Name, proto, socket)
			task.StartTime = time.Now()
			if task.Module != "dummy" && task.Module != "meta" && task.Node != "null" {
				log.Printf("Sending command on %v", task.Node)
				task.Status = Client(task, &proto, &socket)
			} else {
				task.Status = 0
			}
			task.EndTime = time.Now()
			// ... Do a lot of stufs...
			//time.Sleep(time.Duration(sleepTime) * time.Second)
			// Adjust the Status
			//task.Status = 2
			// Send it on the channel
			log.Printf("[%v:%v] Done", task.Id, task.Name)
			doneChan <- task
			wg.Done()
			return
		}
	}
}

// The advertize goroutine, reads the tasks from doneChannel and write the TaskGraphStructure back to the taskStructureChan
func Advertize(taskStructure *TaskGraphStructure, doneChan <-chan *Task) {
	// Let's launch the task that can initially run
	rowSize, _ := taskStructure.AdjacencyMatrix.Dims()
	for taskIndex, _ := range taskStructure.Tasks {
		sum := float64(0)
		for r := 0; r < rowSize; r++ {
			sum += taskStructure.AdjacencyMatrix.At(r, taskIndex)
		}
		if sum == 0 && taskStructure.Tasks[taskIndex].Status < 0 {
			taskStructure.Tasks[taskIndex].TaskCanRunChan <- true
		}
	}
	doneAdjacency := mat64.DenseCopyOf(taskStructure.AdjacencyMatrix)
	// Store the task that we have already advertized
	var advertized []int
	for {
		task := <-doneChan

		// TaskId is finished, it cannot be the source of any task anymore
		// Set the row at 0
		rowSize, colSize := doneAdjacency.Dims()
		for c := 0; c < colSize; c++ {
			doneAdjacency.Set(task.Id, c, float64(0))
		}
		// For each dependency of the task
		// We can run if the sum of the element of the column Id of the current task is 0
		for taskIndex, _ := range taskStructure.Tasks {
			sum := float64(0)
			for r := 0; r < rowSize; r++ {
				sum += doneAdjacency.At(r, taskIndex)
			}

			// This task can be advertized...
			if sum == 0 && taskStructure.Tasks[taskIndex].Status < -2 {
				taskStructure.Tasks[taskIndex].Status = -2
				// ... if it has not been advertized already
				advertized = append(advertized, taskIndex)
				taskStructure.Tasks[taskIndex].TaskCanRunChan <- true
			}
		}
	}
}
<commit_msg>Formatting<commit_after>package gautomator

import (
	"github.com/gonum/matrix/mat64" // Matrix
	"log"
	"math/rand" // Temp
	//"strconv"
	"sync"
	"time"
)

func random(min int, max int) int {
	var bytes int
	bytes = min + rand.Intn(max)
	return int(bytes)
	//rand.Seed(time.Now().UTC().UnixNano())
	//return rand.Intn(max - min) + min
}

// Ths runner goroutine is a goroutinei which:
// Consume the TaskGraphStructure from a channel
// run the task given as arguments if the deps are done
// Post the task to the doncChannel once done
//
func Runner(task *Task, doneChan chan<- *Task, wg *sync.WaitGroup) {
	log.Printf("[%v:%v] Queued", task.Id, task.Name)
	for {
		letsGo := <-task.TaskCanRunChan
		// For each dependency of the task
		// We can run if the sum of the element of the column Id of the current task is 0

		if letsGo == true {
			proto := "tcp"
			socket := task.Node
			// Stupid trick to make shell works... A Shell module will be implemented later"
			if task.Module == "shell" {
				task.Module = "echo"
				task.Args = append(task.Args, "|")
				task.Args = append(task.Args, "/bin/ksh")
			}
			task.Status = -1
			log.Printf("[%v:%v] Running (%v %v)", task.Id, task.Name, task.Module, task.Args[0])
			log.Printf("[%v] Connecting in %v on %v", task.Name, proto, socket)
			task.StartTime = time.Now()
			if task.Module != "dummy" && task.Module != "meta" && task.Node != "null" {
				log.Printf("Sending command on %v", task.Node)
				task.Status = Client(task, &proto, &socket)
			} else {
				task.Status = 0
			}
			task.EndTime = time.Now()
			// ... Do a lot of stufs...
			//time.Sleep(time.Duration(sleepTime) * time.Second)
			// Adjust the Status
			//task.Status = 2
			// Send it on the channel
			log.Printf("[%v:%v] Done", task.Id, task.Name)
			doneChan <- task
			wg.Done()
			return
		}
	}
}

// The advertize goroutine, reads the tasks from doneChannel and write the TaskGraphStructure back to the taskStructureChan
func Advertize(taskStructure *TaskGraphStructure, doneChan <-chan *Task) {
	// Let's launch the task that can initially run
	rowSize, _ := taskStructure.AdjacencyMatrix.Dims()
	for taskIndex, _ := range taskStructure.Tasks {
		sum := float64(0)
		for r := 0; r < rowSize; r++ {
			sum += taskStructure.AdjacencyMatrix.At(r, taskIndex)
		}
		if sum == 0 && taskStructure.Tasks[taskIndex].Status < 0 {
			taskStructure.Tasks[taskIndex].TaskCanRunChan <- true
		}
	}
	doneAdjacency := mat64.DenseCopyOf(taskStructure.AdjacencyMatrix)
	// Store the task that we have already advertized
	var advertized []int
	for {
		task := <-doneChan

		// TaskId is finished, it cannot be the source of any task anymore
		// Set the row at 0 if status is 0
		rowSize, colSize := doneAdjacency.Dims()
		if task.Status == 0 {
			for c := 0; c < colSize; c++ {
				doneAdjacency.Set(task.Id, c, float64(0))
			}
		}
		// For each dependency of the task
		// We can run if the sum of the element of the column Id of the current task is 0
		for taskIndex, _ := range taskStructure.Tasks {
			sum := float64(0)
			for r := 0; r < rowSize; r++ {
				sum += doneAdjacency.At(r, taskIndex)
			}

			// This task can be advertized...
			if sum == 0 && taskStructure.Tasks[taskIndex].Status < -2 {
				taskStructure.Tasks[taskIndex].Status = -2
				// ... if it has not been advertized already
				advertized = append(advertized, taskIndex)
				taskStructure.Tasks[taskIndex].TaskCanRunChan <- true
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/bentranter/chalk"
	"github.com/yhat/scrape"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

// CourseMatcher looks for any `<tr>` element with the
// class `timetable-course-one` or `timetable-course-two`
var CourseMatcher = func(n *html.Node) bool {
	if n.DataAtom == atom.Tr {
		return scrape.Attr(n, "class") == "timetable-course-two" || scrape.Attr(n, "class") == "timetable-course-one"
	}
	return false
}

// URLs are all the absolute URLs that need to be scraped
var URLs = []string{
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/anth.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/apbi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/biol.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/busi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/chem.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/clas.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/comp.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/crim.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/econ.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/educ.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/engi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/finn.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/fren.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geoa.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geog.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geol.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/gero.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/gsci.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/hist.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/indi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/intd.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ital.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/kine.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/lang.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/laws.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ling.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/math.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/mdst.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/meds.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/musi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nacc.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nort.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nrmt.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nurs.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ojib.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/outd.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/phil.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/phys.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/poli.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/psyc.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/reli.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/soci.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/sowk.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/span.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/visu.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/wate.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/wome.html",
}

// Course holds all the data for a single course at
// Lakehead. Some of these will need to change into maps
// in order to have more usable data (ie, `Dates` should
// become `StartDate` and `EndDate`).
type Course struct {
	Code         string  // PHYS-1030-FA
	Title        string  // Intro Appl Phys I (Mechanics)
	Type         string  // LEC
	RoomNo       string  // RB 1046
	Weekdays     string  // MWF
	Times        string  // 1:00PM - 2:00PM
	Dates        string  // 09/14/15 - 03/12/15
	Weight       float64 // 0.5
	Synonym      int     // 643369
	Instructor   string  // Dr. Mark C. Gallagher
	TextBookLink string  // https://textbooks.lakeheadu.ca...
	TextBook     string  // some struct with textbook info
}

// HTTPResponse is the struct that holds our response
// data
type HTTPResponse struct {
	url      string
	response *http.Response
	err      error
}

// Scrape finds and serializes the data from Lakehead's
// site. Eventually, it should return an array of
// `Match` reponses.
func Scrape(resp *HTTPResponse) {

	root, err := html.Parse(resp.response.Body)
	if err != nil {
		fmt.Println("Error :", err)
		return
	}

	// Yhat's package expects atomic values for tags, see
	// https://godoc.org/golang.org/x/net/html/atom if you
	// need a different tag.
	data := scrape.FindAll(root, CourseMatcher)

	// Current course flag for looping, and counter for
	// total courses.
	currentCourse := ""
	counter := 0

	// currentCourseCounter counts which row of the `<tr>`
	// element we're in, so that we don't scrape out of
	// bounds.
	currentCourseCounter := 0

	// This actually works
	for _, match := range data {

		thisCourse := scrape.Attr(match, "class")

		// Set the courrentCourse flag
		switch scrape.Attr(match, "class") {
		case "timetable-course-two":
			currentCourse = "timetable-course-two"
		case "timetable-course-one":
			currentCourse = "timetable-course-two"
		default:
			// Do nothing
		}

		currentCourseCounter++

		if currentCourse != thisCourse {
			counter++
			currentCourseCounter = 0
		}

		// This actually works. Each case is a number that
		// matches a row (a `<tr>` element) in the current
		// course. Since attempting to scrape data that
		// doesn't exist in a row results in an out of
		// bounds error, this makes sure that we're never
		// scraping where there is no data. Keep in mind
		// scraping text from an element that has no value
		// but does exist won't throw an error, but trying
		// to scrape a non-existent attribute will throw an
		// error.
		switch currentCourseCounter {
		case 1:
			// Get the course code (string, since there's
			// no benefit from grabbing the int from it)
			courseCode := scrape.Text(match.FirstChild.NextSibling)
			fmt.Println(chalk.White(courseCode))

			// Get the course title
			courseTitle := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseTitle)

			// Get the course type. It _should_ be only one
			// of either `LEC`, `LAB`, or `WEB`... is a
			// boolean a better idea?
			courseType := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseType)

			// Get the course room number. It might be
			// worth it to parse the first two numbers to
			// get the building code, and then grab the
			// rest of the entry to get the room number.
			// Not sure how consistent all that is though.
			courseRoomNo := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseRoomNo)

			// Get the course days. This is the hardest one
			// to parse (probably) because it needs to be
			// seperated into an array of week days.
			//
			// @TODO: Parse into array of week days.
			courseWeekdays := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseWeekdays)

			// Get the course time. Needs to be parsed into
			// a start time and end time - shouldn't be
			// hard at all using `trim`.
			//
			// @TODO: Parse into start and end time.
			courseTimes := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseTimes)

			// Get the course dates. Needs to be parsed
			// into a start and end date.
			//
			// @TODO: Parse into start and end date.
			courseDates := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(courseDates)

			// Get the credit weight. Gets parsed into a
			// a float.
			courseWeightRaw := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			courseWeight, err := strconv.ParseFloat(courseWeightRaw, 64)
			if err != nil {
				fmt.Println("Couldn't parse float: ", err)
			}
			fmt.Println(courseWeight)

		case 2:
			// Get the synonym, and turn it into an int
			courseSynonymRaw := scrape.Text(match.FirstChild.NextSibling)
			synonymNumStr := strings.TrimLeft(courseSynonymRaw, "Synonym: ")
			courseSynonym, err := strconv.ParseInt(synonymNumStr, 0, 64)
			if err != nil {
				fmt.Println("Couldn't convert string")
			}
			fmt.Println(courseSynonym)

			// Get the instructor name
			courseInstructorRaw := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling)
			courseInstructor := strings.TrimLeft(courseInstructorRaw, "Instructor: ")
			fmt.Println(courseInstructor)

		case 3:
			// Get the course textbook link. This one is
			// annoying because you have to check to see
			// if there is actually anything there for you
			// to scrape.
			if scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling) == "BOOKS" {
				courseTextbookLink := scrape.Attr(match.FirstChild.NextSibling.NextSibling.NextSibling.FirstChild, "href")
				fmt.Println(courseTextbookLink)

				// Fun fact: Right here, you'll need to
				// make a `GET` request to that URL and
				// scrape the textbook page to get the
				// textbook info.
			} else {
				fmt.Println("No textbook exists for this course yet.")
			}
		case 4:
			fmt.Println("\n\n")
			// There's a fourth row, but never anything in
			// it
		default:
			// Do nothing
		}
	}

	return
}
<commit_msg>Add scraped data to Course struct instance<commit_after>package main

import (
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"github.com/bentranter/chalk"
	"github.com/yhat/scrape"
	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

// CourseMatcher looks for any `<tr>` element with the
// class `timetable-course-one` or `timetable-course-two`
var CourseMatcher = func(n *html.Node) bool {
	if n.DataAtom == atom.Tr {
		return scrape.Attr(n, "class") == "timetable-course-two" || scrape.Attr(n, "class") == "timetable-course-one"
	}
	return false
}

// URLs are all the absolute URLs that need to be scraped
var URLs = []string{
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/anth.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/apbi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/biol.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/busi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/chem.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/clas.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/comp.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/crim.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/econ.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/educ.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/engi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/finn.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/fren.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geoa.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geog.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/geol.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/gero.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/gsci.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/hist.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/indi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/intd.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ital.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/kine.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/lang.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/laws.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ling.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/math.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/mdst.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/meds.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/musi.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nacc.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nort.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nrmt.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/nurs.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/ojib.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/outd.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/phil.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/phys.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/poli.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/psyc.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/reli.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/soci.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/sowk.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/span.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/visu.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/wate.html",
	"http://timetable.lakeheadu.ca/2015FW_UG_TBAY/wome.html",
}

// Course holds all the data for a single course at
// Lakehead. Some of these will need to change into maps
// in order to have more usable data (ie, `Dates` should
// become `StartDate` and `EndDate`).
type Course struct {
	Code         string  // PHYS-1030-FA
	Title        string  // Intro Appl Phys I (Mechanics)
	Type         string  // LEC
	RoomNo       string  // RB 1046
	Weekdays     string  // MWF
	Times        string  // 1:00PM - 2:00PM
	Dates        string  // 09/14/15 - 03/12/15
	Weight       float64 // 0.5
	Synonym      int64   // 643369
	Instructor   string  // Dr. Mark C. Gallagher
	TextBookLink string  // https://textbooks.lakeheadu.ca...
	TextBook     string  // some struct with textbook info
}

// HTTPResponse is the struct that holds our response
// data
type HTTPResponse struct {
	url      string
	response *http.Response
	err      error
}

// Scrape finds and serializes the data from Lakehead's
// site. Eventually, it should return an array of
// `Match` reponses.
func Scrape(resp *HTTPResponse) {

	root, err := html.Parse(resp.response.Body)
	if err != nil {
		fmt.Println("Error :", err)
		return
	}

	// Yhat's package expects atomic values for tags, see
	// https://godoc.org/golang.org/x/net/html/atom if you
	// need a different tag.
	data := scrape.FindAll(root, CourseMatcher)

	// Current course flag for looping, and counter for
	// total courses.
	currentCourse := ""
	counter := 0

	// currentCourseCounter counts which row of the `<tr>`
	// element we're in, so that we don't scrape out of
	// bounds.
	currentCourseCounter := 0

	// This actually works
	for _, match := range data {

		thisCourse := scrape.Attr(match, "class")

		// Set the courrentCourse flag
		switch scrape.Attr(match, "class") {
		case "timetable-course-two":
			currentCourse = "timetable-course-two"
		case "timetable-course-one":
			currentCourse = "timetable-course-two"
		default:
			// Do nothing
		}

		currentCourseCounter++

		if currentCourse != thisCourse {
			counter++
			currentCourseCounter = 0
		}

		// Make a new Course struct to hold each match
		course := &Course{
			Code:         "",
			Title:        "",
			Type:         "",
			RoomNo:       "",
			Weekdays:     "",
			Times:        "",
			Dates:        "",
			Weight:       0,
			Synonym:      0,
			Instructor:   "",
			TextBookLink: "",
			TextBook:     "",
		}

		// This actually works. Each case is a number that
		// matches a row (a `<tr>` element) in the current
		// course. Since attempting to scrape data that
		// doesn't exist in a row results in an out of
		// bounds error, this makes sure that we're never
		// scraping where there is no data. Keep in mind
		// scraping text from an element that has no value
		// but does exist won't throw an error, but trying
		// to scrape a non-existent attribute will throw an
		// error.
		switch currentCourseCounter {
		case 1:
			// Get the course code (string, since there's
			// no benefit from grabbing the int from it)
			course.Code = scrape.Text(match.FirstChild.NextSibling)
			fmt.Println(chalk.White(course.Code))

			// Get the course title
			course.Title = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.Title)

			// Get the course type. It _should_ be only one
			// of either `LEC`, `LAB`, or `WEB`... is a
			// boolean a better idea?
			course.Type = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.Type)

			// Get the course room number. It might be
			// worth it to parse the first two numbers to
			// get the building code, and then grab the
			// rest of the entry to get the room number.
			// Not sure how consistent all that is though.
			course.RoomNo = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.RoomNo)

			// Get the course days. This is the hardest one
			// to parse (probably) because it needs to be
			// seperated into an array of week days.
			//
			// @TODO: Parse into array of week days.
			course.Weekdays = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.Weekdays)

			// Get the course time. Needs to be parsed into
			// a start time and end time - shouldn't be
			// hard at all using `trim`.
			//
			// @TODO: Parse into start and end time.
			course.Times = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.Times)

			// Get the course dates. Needs to be parsed
			// into a start and end date.
			//
			// @TODO: Parse into start and end date.
			course.Dates = scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			fmt.Println(course.Dates)

			// Get the credit weight. Gets parsed into a
			// a float.
			courseWeightRaw := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling.NextSibling)
			course.Weight, err = strconv.ParseFloat(courseWeightRaw, 64)
			if err != nil {
				fmt.Println("Couldn't parse float: ", err)
			}
			fmt.Println(course.Weight)

		case 2:
			// Get the synonym, and turn it into an int
			courseSynonymRaw := scrape.Text(match.FirstChild.NextSibling)
			synonymNumStr := strings.TrimLeft(courseSynonymRaw, "Synonym: ")
			course.Synonym, err = strconv.ParseInt(synonymNumStr, 0, 64)
			if err != nil {
				fmt.Println("Couldn't convert string")
			}
			fmt.Println(course.Synonym)

			// Get the instructor name
			courseInstructorRaw := scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling)
			course.Instructor = strings.TrimLeft(courseInstructorRaw, "Instructor: ")
			fmt.Println(course.Instructor)

		case 3:
			// Get the course textbook link. This one is
			// annoying because you have to check to see
			// if there is actually anything there for you
			// to scrape.
			if scrape.Text(match.FirstChild.NextSibling.NextSibling.NextSibling) == "BOOKS" {
				courseTextbookLink := scrape.Attr(match.FirstChild.NextSibling.NextSibling.NextSibling.FirstChild, "href")
				fmt.Println(courseTextbookLink)

				// Fun fact: Right here, you'll need to
				// make a `GET` request to that URL and
				// scrape the textbook page to get the
				// textbook info.
			} else {
				fmt.Println("No textbook exists for this course yet.")
			}
		case 4:
			fmt.Println("\n\n")
			// There's a fourth row, but never anything in
			// it
		default:
			// Do nothing
		}
	}

	return
}
<|endoftext|>


<commit_before>package sellers

import (
	"github.com/luistm/go-bank-cli/lib"
	"github.com/luistm/go-bank-cli/lib/customerrors"
)

// NewInteractor creates a new Interactor object for sellers
func NewInteractor(storage lib.SQLDatabaseHandler) *Interactor {
	return &Interactor{
		repository: &repository{SQLStorage: storage},
	}
}

// Interactor ...
type Interactor struct {
	repository lib.Repository
}

// Create adds a new seller and persists it
func (i *Interactor) Create(name string) (lib.Entity, error) {

	if name == "" {
		return &Seller{}, customerrors.ErrBadInput
	}

	if i.repository == nil {
		return &Seller{}, customerrors.ErrRepositoryUndefined
	}

	s := &Seller{slug: name}
	if err := i.repository.Save(s); err != nil {
		return &Seller{}, &customerrors.ErrRepository{Msg: err.Error()}
	}

	return s, nil
}

// GetAll returns all the sellers available in the system
func (i *Interactor) GetAll() ([]lib.Entity, error) {

	sellers := []lib.Entity{}
	if i.repository == nil {
		return sellers, customerrors.ErrRepositoryUndefined
	}

	s, err := i.repository.GetAll()
	if err != nil {
		return sellers, &customerrors.ErrRepository{Msg: err.Error()}
	}

	return s, nil
}

// Updates a seller given i'ts slug
func (i *Interactor) Update(slug string, name string) error {

	if slug == "" || name == "" {
		return customerrors.ErrBadInput
	}
	if i.repository == nil {
		return customerrors.ErrRepositoryUndefined
	}

	s := &Seller{slug, name}
	err := i.repository.Save(s)
	if err != nil {
		return &customerrors.ErrRepository{Msg: err.Error()}
	}

	return nil
}
<commit_msg>Fix typo<commit_after>package sellers

import (
	"github.com/luistm/go-bank-cli/lib"
	"github.com/luistm/go-bank-cli/lib/customerrors"
)

// NewInteractor creates a new Interactor object for sellers
func NewInteractor(storage lib.SQLDatabaseHandler) *Interactor {
	return &Interactor{
		repository: &repository{SQLStorage: storage},
	}
}

// Interactor ...
type Interactor struct {
	repository lib.Repository
}

// Create adds a new seller and persists it
func (i *Interactor) Create(name string) (lib.Entity, error) {

	if name == "" {
		return &Seller{}, customerrors.ErrBadInput
	}

	if i.repository == nil {
		return &Seller{}, customerrors.ErrRepositoryUndefined
	}

	s := &Seller{slug: name}
	if err := i.repository.Save(s); err != nil {
		return &Seller{}, &customerrors.ErrRepository{Msg: err.Error()}
	}

	return s, nil
}

// GetAll returns all the sellers available in the system
func (i *Interactor) GetAll() ([]lib.Entity, error) {

	sellers := []lib.Entity{}
	if i.repository == nil {
		return sellers, customerrors.ErrRepositoryUndefined
	}

	s, err := i.repository.GetAll()
	if err != nil {
		return sellers, &customerrors.ErrRepository{Msg: err.Error()}
	}

	return s, nil
}

// Update a seller given it's slug
func (i *Interactor) Update(slug string, name string) error {

	if slug == "" || name == "" {
		return customerrors.ErrBadInput
	}
	if i.repository == nil {
		return customerrors.ErrRepositoryUndefined
	}

	s := &Seller{slug, name}
	err := i.repository.Save(s)
	if err != nil {
		return &customerrors.ErrRepository{Msg: err.Error()}
	}

	return nil
}
<|endoftext|>


<commit_before>package testutil

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"time"

	"v.io/tools/lib/collect"
	"v.io/tools/lib/runutil"
	"v.io/tools/lib/util"
)

const (
	defaultJSTestTimeout = 10 * time.Minute
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to the target directory.
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	opts := ctx.Run().Opts()
	for key, value := range env {
		opts.Env[key] = value
	}
	if err := ctx.Run().CommandWithOpts(opts, "make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	var stderr bytes.Buffer
	opts = ctx.Run().Opts()
	opts.Stderr = &stderr
	if err := ctx.Run().TimedCommandWithOpts(defaultJSTestTimeout, opts, "make", target); err != nil {
		fmt.Fprintf(ctx.Stderr(), "Stderr:\n%s\n", stderr.String())
		if err == runutil.CommandTimedOutErr {
			return &TestResult{
				Status:       TestTimedOut,
				TimeoutValue: defaultJSTestTimeout,
			}, nil
		}
		// Generate an xunit report if none exists.
		// This can happen when errors are not in javascript tests themselves.
		xunitFilePath := XUnitReportPath(testName)
		if _, err := os.Stat(xunitFilePath); err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
			s := createTestSuiteWithFailure(testName, "Test", "failure", stderr.String(), 0)
			if err := createXUnitReport(ctx, testName, []testSuite{*s}); err != nil {
				return nil, err
			}
		}
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// vanadiumJSBuildExtension tests the vanadium javascript build extension.
func vanadiumJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "extension/veyron.crx"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}

// vanadiumJSDoc (re)generates the content of the vanadium javascript
// documentation server.
func vanadiumJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "docs"
	webDir, jsDocDir := "/usr/share/nginx/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().RemoveAll(webDir); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	// Move generated js documentation to the web server directory.
	if err := ctx.Run().Rename(jsDocDir, webDir); err != nil {
		return nil, err
	}
	return result, nil
}

// vanadiumJSBrowserIntegration runs the vanadium javascript integration test in a browser environment using nacl plugin.
func vanadiumJSBrowserIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-integration-browser"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["BROWSER_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSNodeIntegration runs the vanadium javascript integration test in NodeJS environment using wspr.
func vanadiumJSNodeIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-integration-node"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSUnit runs the vanadium javascript unit test.
func vanadiumJSUnit(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-unit"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSVdl runs the vanadium javascript vdl test.
func vanadiumJSVdl(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-vdl"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSVom runs the vanadium javascript vom test.
func vanadiumJSVom(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "vom")
	target := "test"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

func setCommonJSEnv(env map[string]string) {
	env["XUNIT"] = "true"
	env["NOVDLGEN"] = "true"
}
<commit_msg>tools/testutil: include more useful output for failed javascript tests.<commit_after>package testutil

import (
	"bytes"
	"io"
	"math"
	"os"
	"path/filepath"
	"strings"
	"time"

	"v.io/tools/lib/collect"
	"v.io/tools/lib/runutil"
	"v.io/tools/lib/util"
)

const (
	defaultJSTestTimeout = 10 * time.Minute
)

// runJSTest is a harness for executing javascript tests.
func runJSTest(ctx *util.Context, testName, testDir, target string, cleanFn func() error, env map[string]string) (_ *TestResult, e error) {
	// Initialize the test.
	cleanup, err := initTest(ctx, testName, []string{"web"})
	if err != nil {
		return nil, err
	}
	defer collect.Error(func() error { return cleanup() }, &e)

	// Navigate to the target directory.
	if err := ctx.Run().Chdir(testDir); err != nil {
		return nil, err
	}

	// Clean up after previous instances of the test.
	opts := ctx.Run().Opts()
	for key, value := range env {
		opts.Env[key] = value
	}
	if err := ctx.Run().CommandWithOpts(opts, "make", "clean"); err != nil {
		return nil, err
	}
	if cleanFn != nil {
		if err := cleanFn(); err != nil {
			return nil, err
		}
	}

	// Run the test target.
	var out bytes.Buffer
	opts = ctx.Run().Opts()
	opts.Stdout = io.MultiWriter(&out, opts.Stdout)
	opts.Stderr = io.MultiWriter(&out, opts.Stderr)
	if err := ctx.Run().TimedCommandWithOpts(defaultJSTestTimeout, opts, "make", target); err != nil {
		if err == runutil.CommandTimedOutErr {
			return &TestResult{
				Status:       TestTimedOut,
				TimeoutValue: defaultJSTestTimeout,
			}, nil
		}
		// Generate an xunit report if none exists.
		// This can happen when errors are not in javascript tests themselves.
		// In the error message, we inlcude the last 15 lines of stdout+stderr.
		xunitFilePath := XUnitReportPath(testName)
		if _, err := os.Stat(xunitFilePath); err != nil {
			if !os.IsNotExist(err) {
				return nil, err
			}
			lines := strings.Split(out.String(), "\n")
			startLine := int(math.Max(0, float64(len(lines)-15)))
			errMsg := "......\n" + strings.Join(lines[startLine:], "\n")
			s := createTestSuiteWithFailure(testName, "Test", "failure", errMsg, 0)
			if err := createXUnitReport(ctx, testName, []testSuite{*s}); err != nil {
				return nil, err
			}
		}
		return &TestResult{Status: TestFailed}, nil
	}

	return &TestResult{Status: TestPassed}, nil
}

// vanadiumJSBuildExtension tests the vanadium javascript build extension.
func vanadiumJSBuildExtension(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "extension/veyron.crx"
	return runJSTest(ctx, testName, testDir, target, nil, nil)
}

// vanadiumJSDoc (re)generates the content of the vanadium javascript
// documentation server.
func vanadiumJSDoc(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "docs"
	webDir, jsDocDir := "/usr/share/nginx/www/jsdoc", filepath.Join(testDir, "docs")
	cleanFn := func() error {
		if err := ctx.Run().RemoveAll(webDir); err != nil {
			return err
		}
		return nil
	}
	result, err := runJSTest(ctx, testName, testDir, target, cleanFn, nil)
	if err != nil {
		return nil, err
	}
	// Move generated js documentation to the web server directory.
	if err := ctx.Run().Rename(jsDocDir, webDir); err != nil {
		return nil, err
	}
	return result, nil
}

// vanadiumJSBrowserIntegration runs the vanadium javascript integration test in a browser environment using nacl plugin.
func vanadiumJSBrowserIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-integration-browser"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["BROWSER_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSNodeIntegration runs the vanadium javascript integration test in NodeJS environment using wspr.
func vanadiumJSNodeIntegration(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-integration-node"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSUnit runs the vanadium javascript unit test.
func vanadiumJSUnit(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-unit"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSVdl runs the vanadium javascript vdl test.
func vanadiumJSVdl(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "core")
	target := "test-vdl"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

// vanadiumJSVom runs the vanadium javascript vom test.
func vanadiumJSVom(ctx *util.Context, testName string) (*TestResult, error) {
	root, err := util.VanadiumRoot()
	if err != nil {
		return nil, err
	}
	testDir := filepath.Join(root, "release", "javascript", "vom")
	target := "test"
	env := map[string]string{}
	setCommonJSEnv(env)
	env["NODE_OUTPUT"] = XUnitReportPath(testName)
	return runJSTest(ctx, testName, testDir, target, nil, env)
}

func setCommonJSEnv(env map[string]string) {
	env["XUNIT"] = "true"
	env["NOVDLGEN"] = "true"
}
<|endoftext|>


<commit_before>package message

import (
	"errors"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/response"
	"github.com/koding/bongo"
)

func Create(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	channelId, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	// override message type
	// all of the messages coming from client-side
	// should be marked as POST
	req.TypeConstant = models.ChannelMessage_TYPE_POST

	// set initial channel id
	req.InitialChannelId = channelId

	if err := req.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	cml := models.NewChannelMessageList()
	// override channel id
	cml.ChannelId = channelId
	cml.MessageId = req.Id
	if err := cml.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		req.BuildEmptyMessageContainer(),
	)
}

func Delete(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	err = deleteSingleMessage(req, true)
	if err != nil {
		return response.NewBadRequest(err)
	}

	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func deleteSingleMessage(cm *models.ChannelMessage, deleteReplies bool) error {
	// first delete from all channels
	selector := map[string]interface{}{
		"message_id": cm.Id,
	}

	cml := models.NewChannelMessageList()
	if err := cml.DeleteMessagesBySelector(selector); err != nil {
		return err
	}

	// fetch interactions
	i := models.NewInteraction()
	i.MessageId = cm.Id
	interactions, err := i.FetchAll("like")
	if err != nil {
		return err
	}

	// delete interactions
	for _, interaction := range interactions {
		err := interaction.Delete()
		if err != nil {
			return err
		}
	}

	if deleteReplies {
		mr := models.NewMessageReply()
		mr.MessageId = cm.Id

		// list returns ChannelMessage
		messageReplies, err := mr.ListAll()
		if err != nil {
			return err
		}

		// delete message replies
		for _, replyMessage := range messageReplies {
			err := deleteSingleMessage(&replyMessage, false)
			if err != nil {
				return err
			}
		}
	}

	err = models.NewMessageReply().DeleteByOrQuery(cm.Id)
	if err != nil {
		return err
	}

	// delete replyMessage itself
	err = cm.Delete()
	if err != nil {
		return err
	}
	return nil
}

func Update(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	body := req.Body
	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	req.Body = body
	if err := req.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		req.BuildEmptyMessageContainer(),
	)
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}
	cm := models.NewChannelMessage()
	if err := cm.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		cm.BuildEmptyMessageContainer(),
	)
}

func GetWithRelated(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	cm := models.NewChannelMessage()
	if err := cm.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	cmc, err := cm.BuildMessage(request.GetQuery(u))
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cmc)
}

func GetBySlug(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Slug == "" {
		return response.NewBadRequest(errors.New("slug is not set"))
	}

	cm := models.NewChannelMessage()
	if err := cm.BySlug(q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc, err := cm.BuildMessage(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cmc)
}
<commit_msg>Social: add exempt query params into message handlers<commit_after>package message

import (
	"errors"
	"net/http"
	"net/url"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/response"

	"github.com/koding/bongo"
)

func Create(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	channelId, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	// override message type
	// all of the messages coming from client-side
	// should be marked as POST
	req.TypeConstant = models.ChannelMessage_TYPE_POST

	// set initial channel id
	req.InitialChannelId = channelId

	if err := req.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	cml := models.NewChannelMessageList()
	// override channel id
	cml.ChannelId = channelId
	cml.MessageId = req.Id
	if err := cml.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		req.BuildEmptyMessageContainer(),
	)
}

func Delete(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	err = deleteSingleMessage(req, true)
	if err != nil {
		return response.NewBadRequest(err)
	}

	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func deleteSingleMessage(cm *models.ChannelMessage, deleteReplies bool) error {
	// first delete from all channels
	selector := map[string]interface{}{
		"message_id": cm.Id,
	}

	cml := models.NewChannelMessageList()
	if err := cml.DeleteMessagesBySelector(selector); err != nil {
		return err
	}

	// fetch interactions
	i := models.NewInteraction()
	i.MessageId = cm.Id
	interactions, err := i.FetchAll("like")
	if err != nil {
		return err
	}

	// delete interactions
	for _, interaction := range interactions {
		err := interaction.Delete()
		if err != nil {
			return err
		}
	}

	if deleteReplies {
		mr := models.NewMessageReply()
		mr.MessageId = cm.Id

		// list returns ChannelMessage
		messageReplies, err := mr.ListAll()
		if err != nil {
			return err
		}

		// delete message replies
		for _, replyMessage := range messageReplies {
			err := deleteSingleMessage(&replyMessage, false)
			if err != nil {
				return err
			}
		}
	}

	err = models.NewMessageReply().DeleteByOrQuery(cm.Id)
	if err != nil {
		return err
	}

	// delete replyMessage itself
	err = cm.Delete()
	if err != nil {
		return err
	}
	return nil
}

func Update(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	body := req.Body
	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	req.Body = body
	if err := req.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	return response.HandleResultAndError(
		req.BuildEmptyMessageContainer(),
	)
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	return response.HandleResultAndError(
		cm.BuildEmptyMessageContainer(),
	)
}

func getMessageByUrl(u *url.URL) (*models.ChannelMessage, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return nil, err
	}

	// get url query params
	q := request.GetQuery(u)

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"id": id,
		},
		Pagination: *bongo.NewPagination(1, 0),
	}

	cm := models.NewChannelMessage()
	// add exempt info
	query.AddScope(models.RemoveTrollContent(cm, q.ShowExempt))

	if err := cm.One(query); err != nil {
		return nil, err
	}

	return cm, nil
}

func GetWithRelated(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	cmc, err := cm.BuildMessage(request.GetQuery(u))
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cmc)
}

func GetBySlug(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Slug == "" {
		return response.NewBadRequest(errors.New("slug is not set"))
	}

	cm := models.NewChannelMessage()
	if err := cm.BySlug(q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc, err := cm.BuildMessage(q)
	if err != nil {
		return response.NewBadRequest(err)
	}

	return response.NewOK(cmc)
}
<|endoftext|>


<commit_before>//  Copyright (c) 2014 Couchbase, 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.

package bleve

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

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/search"
)

type numericRange struct {
	Name string   `json:"name,omitempty"`
	Min  *float64 `json:"min,omitempty"`
	Max  *float64 `json:"max,omitempty"`
}

type dateTimeRange struct {
	Name        string    `json:"name,omitempty"`
	Start       time.Time `json:"start,omitempty"`
	End         time.Time `json:"end,omitempty"`
	startString *string
	endString   *string
}

func (dr *dateTimeRange) ParseDates(dateTimeParser analysis.DateTimeParser) {
	if dr.Start.IsZero() && dr.startString != nil {
		start, err := dateTimeParser.ParseDateTime(*dr.startString)
		if err == nil {
			dr.Start = start
		}
	}
	if dr.End.IsZero() && dr.endString != nil {
		end, err := dateTimeParser.ParseDateTime(*dr.endString)
		if err == nil {
			dr.End = end
		}
	}
}

func (dr *dateTimeRange) UnmarshalJSON(input []byte) error {
	var temp struct {
		Name  string  `json:"name,omitempty"`
		Start *string `json:"start,omitempty"`
		End   *string `json:"end,omitempty"`
	}

	err := json.Unmarshal(input, &temp)
	if err != nil {
		return err
	}

	dr.Name = temp.Name
	if temp.Start != nil {
		dr.startString = temp.Start
	}
	if temp.End != nil {
		dr.endString = temp.End
	}

	return nil
}

// A FacetRequest describes a facet or aggregation
// of the result document set you would like to be
// built.
type FacetRequest struct {
	Size           int              `json:"size"`
	Field          string           `json:"field"`
	NumericRanges  []*numericRange  `json:"numeric_ranges,omitempty"`
	DateTimeRanges []*dateTimeRange `json:"date_ranges,omitempty"`
}

func (fr *FacetRequest) Validate() error {
	if len(fr.NumericRanges) > 0 && len(fr.DateTimeRanges) > 0 {
		return fmt.Errorf("facet can only conain numeric ranges or date ranges, not both")
	}

	nrNames := map[string]interface{}{}
	for _, nr := range fr.NumericRanges {
		if _, ok := nrNames[nr.Name]; ok {
			return fmt.Errorf("numeric ranges contains duplicate name '%s'", nr.Name)
		}
		nrNames[nr.Name] = struct{}{}
	}

	drNames := map[string]interface{}{}
	for _, dr := range fr.DateTimeRanges {
		if _, ok := drNames[dr.Name]; ok {
			return fmt.Errorf("date ranges contains duplicate name '%s'", dr.Name)
		}
		drNames[dr.Name] = struct{}{}
	}

	return nil
}

// NewFacetRequest creates a facet on the specified
// field that limits the number of entries to the
// specified size.
func NewFacetRequest(field string, size int) *FacetRequest {
	return &FacetRequest{
		Field: field,
		Size:  size,
	}
}

// AddDateTimeRange adds a bucket to a field
// containing date values.  Documents with a
// date value falling into this range are tabulated
// as part of this bucket/range.
func (fr *FacetRequest) AddDateTimeRange(name string, start, end time.Time) {
	if fr.DateTimeRanges == nil {
		fr.DateTimeRanges = make([]*dateTimeRange, 0, 1)
	}
	fr.DateTimeRanges = append(fr.DateTimeRanges, &dateTimeRange{Name: name, Start: start, End: end})
}

// AddNumericRange adds a bucket to a field
// containing numeric values.  Documents with a
// numeric value falling into this range are
// tabulated as part of this bucket/range.
func (fr *FacetRequest) AddNumericRange(name string, min, max *float64) {
	if fr.NumericRanges == nil {
		fr.NumericRanges = make([]*numericRange, 0, 1)
	}
	fr.NumericRanges = append(fr.NumericRanges, &numericRange{Name: name, Min: min, Max: max})
}

// FacetsRequest groups together all the
// FacetRequest objects for a single query.
type FacetsRequest map[string]*FacetRequest

func (fr FacetsRequest) Validate() error {
	for _, v := range fr {
		err := v.Validate()
		if err != nil {
			return err
		}
	}
	return nil
}

// HighlightRequest describes how field matches
// should be highlighted.
type HighlightRequest struct {
	Style  *string  `json:"style"`
	Fields []string `json:"fields"`
}

// NewHighlight creates a default
// HighlightRequest.
func NewHighlight() *HighlightRequest {
	return &HighlightRequest{}
}

// NewHighlightWithStyle creates a HighlightRequest
// with an alternate style.
func NewHighlightWithStyle(style string) *HighlightRequest {
	return &HighlightRequest{
		Style: &style,
	}
}

func (h *HighlightRequest) AddField(field string) {
	if h.Fields == nil {
		h.Fields = make([]string, 0, 1)
	}
	h.Fields = append(h.Fields, field)
}

// A SearchRequest describes all the parameters
// needed to search the index.
// Query is required.
// Size/From describe how much and which part of the
// result set to return.
// Highlight describes optional search result
// highlighting.
// Fields describes a list of field values which
// should be retrieved for result documents, provided they
// were stored while indexing.
// Facets describe the set of facets to be computed.
// Explain triggers inclusion of additional search
// result score explanations.
// Sort describes the desired order for the results to be returned.
//
// A special field named "*" can be used to return all fields.
type SearchRequest struct {
	Query     Query             `json:"query"`
	Size      int               `json:"size"`
	From      int               `json:"from"`
	Highlight *HighlightRequest `json:"highlight"`
	Fields    []string          `json:"fields"`
	Facets    FacetsRequest     `json:"facets"`
	Explain   bool              `json:"explain"`
	Sort      search.SortOrder  `json:"sort"`
}

func (sr *SearchRequest) Validate() error {
	err := sr.Query.Validate()
	if err != nil {
		return err
	}

	return sr.Facets.Validate()
}

// AddFacet adds a FacetRequest to this SearchRequest
func (r *SearchRequest) AddFacet(facetName string, f *FacetRequest) {
	if r.Facets == nil {
		r.Facets = make(FacetsRequest, 1)
	}
	r.Facets[facetName] = f
}

// SortBy changes the request to use the requested sort order
func (r *SearchRequest) SortBy(order search.SortOrder) {
	r.Sort = order
}

// UnmarshalJSON deserializes a JSON representation of
// a SearchRequest
func (r *SearchRequest) UnmarshalJSON(input []byte) error {
	var temp struct {
		Q         json.RawMessage   `json:"query"`
		Size      *int              `json:"size"`
		From      int               `json:"from"`
		Highlight *HighlightRequest `json:"highlight"`
		Fields    []string          `json:"fields"`
		Facets    FacetsRequest     `json:"facets"`
		Explain   bool              `json:"explain"`
	}

	err := json.Unmarshal(input, &temp)
	if err != nil {
		return err
	}

	if temp.Size == nil {
		r.Size = 10
	} else {
		r.Size = *temp.Size
	}
	r.From = temp.From
	r.Explain = temp.Explain
	r.Highlight = temp.Highlight
	r.Fields = temp.Fields
	r.Facets = temp.Facets
	r.Query, err = ParseQuery(temp.Q)
	if err != nil {
		return err
	}

	if r.Size < 0 {
		r.Size = 10
	}
	if r.From < 0 {
		r.From = 0
	}

	return nil

}

// NewSearchRequest creates a new SearchRequest
// for the Query, using default values for all
// other search parameters.
func NewSearchRequest(q Query) *SearchRequest {
	return NewSearchRequestOptions(q, 10, 0, false)
}

// NewSearchRequestOptions creates a new SearchRequest
// for the Query, with the requested size, from
// and explanation search parameters.
// By default results are ordered by score, descending.
func NewSearchRequestOptions(q Query, size, from int, explain bool) *SearchRequest {
	return &SearchRequest{
		Query:   q,
		Size:    size,
		From:    from,
		Explain: explain,
		Sort:    search.SortOrder{&search.SortScore{Descending: true}},
	}
}

// IndexErrMap tracks errors with the name of the index where it occurred
type IndexErrMap map[string]error

// MarshalJSON seralizes the error into a string for JSON consumption
func (iem IndexErrMap) MarshalJSON() ([]byte, error) {
	tmp := make(map[string]string, len(iem))
	for k, v := range iem {
		tmp[k] = v.Error()
	}
	return json.Marshal(tmp)
}

func (iem IndexErrMap) UnmarshalJSON(data []byte) error {
	var tmp map[string]string
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		return err
	}
	for k, v := range tmp {
		iem[k] = fmt.Errorf("%s", v)
	}
	return nil
}

// SearchStatus is a secion in the SearchResult reporting how many
// underlying indexes were queried, how many were successful/failed
// and a map of any errors that were encountered
type SearchStatus struct {
	Total      int         `json:"total"`
	Failed     int         `json:"failed"`
	Successful int         `json:"successful"`
	Errors     IndexErrMap `json:"errors,omitempty"`
}

// Merge will merge together multiple SearchStatuses during a MultiSearch
func (ss *SearchStatus) Merge(other *SearchStatus) {
	ss.Total += other.Total
	ss.Failed += other.Failed
	ss.Successful += other.Successful
	if len(other.Errors) > 0 {
		if ss.Errors == nil {
			ss.Errors = make(map[string]error)
		}
		for otherIndex, otherError := range other.Errors {
			ss.Errors[otherIndex] = otherError
		}
	}
}

// A SearchResult describes the results of executing
// a SearchRequest.
type SearchResult struct {
	Status   *SearchStatus                  `json:"status"`
	Request  *SearchRequest                 `json:"request"`
	Hits     search.DocumentMatchCollection `json:"hits"`
	Total    uint64                         `json:"total_hits"`
	MaxScore float64                        `json:"max_score"`
	Took     time.Duration                  `json:"took"`
	Facets   search.FacetResults            `json:"facets"`
}

func (sr *SearchResult) String() string {
	rv := ""
	if sr.Total > 0 {
		if sr.Request.Size > 0 {
			rv = fmt.Sprintf("%d matches, showing %d through %d, took %s\n", sr.Total, sr.Request.From+1, sr.Request.From+len(sr.Hits), sr.Took)
			for i, hit := range sr.Hits {
				rv += fmt.Sprintf("%5d. %s (%f)\n", i+sr.Request.From+1, hit.ID, hit.Score)
				for fragmentField, fragments := range hit.Fragments {
					rv += fmt.Sprintf("\t%s\n", fragmentField)
					for _, fragment := range fragments {
						rv += fmt.Sprintf("\t\t%s\n", fragment)
					}
				}
				for otherFieldName, otherFieldValue := range hit.Fields {
					if _, ok := hit.Fragments[otherFieldName]; !ok {
						rv += fmt.Sprintf("\t%s\n", otherFieldName)
						rv += fmt.Sprintf("\t\t%v\n", otherFieldValue)
					}
				}
			}
		} else {
			rv = fmt.Sprintf("%d matches, took %s\n", sr.Total, sr.Took)
		}
	} else {
		rv = "No matches"
	}
	if len(sr.Facets) > 0 {
		rv += fmt.Sprintf("Facets:\n")
		for fn, f := range sr.Facets {
			rv += fmt.Sprintf("%s(%d)\n", fn, f.Total)
			for _, t := range f.Terms {
				rv += fmt.Sprintf("\t%s(%d)\n", t.Term, t.Count)
			}
			if f.Other != 0 {
				rv += fmt.Sprintf("\tOther(%d)\n", f.Other)
			}
		}
	}
	return rv
}

// Merge will merge together multiple SearchResults during a MultiSearch
func (sr *SearchResult) Merge(other *SearchResult) {
	sr.Status.Merge(other.Status)
	sr.Hits = append(sr.Hits, other.Hits...)
	sr.Total += other.Total
	if other.MaxScore > sr.MaxScore {
		sr.MaxScore = other.MaxScore
	}
	sr.Facets.Merge(other.Facets)
}
<commit_msg>fix SearchRequest parsing to default to proper default sort order<commit_after>//  Copyright (c) 2014 Couchbase, 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.

package bleve

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

	"github.com/blevesearch/bleve/analysis"
	"github.com/blevesearch/bleve/search"
)

type numericRange struct {
	Name string   `json:"name,omitempty"`
	Min  *float64 `json:"min,omitempty"`
	Max  *float64 `json:"max,omitempty"`
}

type dateTimeRange struct {
	Name        string    `json:"name,omitempty"`
	Start       time.Time `json:"start,omitempty"`
	End         time.Time `json:"end,omitempty"`
	startString *string
	endString   *string
}

func (dr *dateTimeRange) ParseDates(dateTimeParser analysis.DateTimeParser) {
	if dr.Start.IsZero() && dr.startString != nil {
		start, err := dateTimeParser.ParseDateTime(*dr.startString)
		if err == nil {
			dr.Start = start
		}
	}
	if dr.End.IsZero() && dr.endString != nil {
		end, err := dateTimeParser.ParseDateTime(*dr.endString)
		if err == nil {
			dr.End = end
		}
	}
}

func (dr *dateTimeRange) UnmarshalJSON(input []byte) error {
	var temp struct {
		Name  string  `json:"name,omitempty"`
		Start *string `json:"start,omitempty"`
		End   *string `json:"end,omitempty"`
	}

	err := json.Unmarshal(input, &temp)
	if err != nil {
		return err
	}

	dr.Name = temp.Name
	if temp.Start != nil {
		dr.startString = temp.Start
	}
	if temp.End != nil {
		dr.endString = temp.End
	}

	return nil
}

// A FacetRequest describes a facet or aggregation
// of the result document set you would like to be
// built.
type FacetRequest struct {
	Size           int              `json:"size"`
	Field          string           `json:"field"`
	NumericRanges  []*numericRange  `json:"numeric_ranges,omitempty"`
	DateTimeRanges []*dateTimeRange `json:"date_ranges,omitempty"`
}

func (fr *FacetRequest) Validate() error {
	if len(fr.NumericRanges) > 0 && len(fr.DateTimeRanges) > 0 {
		return fmt.Errorf("facet can only conain numeric ranges or date ranges, not both")
	}

	nrNames := map[string]interface{}{}
	for _, nr := range fr.NumericRanges {
		if _, ok := nrNames[nr.Name]; ok {
			return fmt.Errorf("numeric ranges contains duplicate name '%s'", nr.Name)
		}
		nrNames[nr.Name] = struct{}{}
	}

	drNames := map[string]interface{}{}
	for _, dr := range fr.DateTimeRanges {
		if _, ok := drNames[dr.Name]; ok {
			return fmt.Errorf("date ranges contains duplicate name '%s'", dr.Name)
		}
		drNames[dr.Name] = struct{}{}
	}

	return nil
}

// NewFacetRequest creates a facet on the specified
// field that limits the number of entries to the
// specified size.
func NewFacetRequest(field string, size int) *FacetRequest {
	return &FacetRequest{
		Field: field,
		Size:  size,
	}
}

// AddDateTimeRange adds a bucket to a field
// containing date values.  Documents with a
// date value falling into this range are tabulated
// as part of this bucket/range.
func (fr *FacetRequest) AddDateTimeRange(name string, start, end time.Time) {
	if fr.DateTimeRanges == nil {
		fr.DateTimeRanges = make([]*dateTimeRange, 0, 1)
	}
	fr.DateTimeRanges = append(fr.DateTimeRanges, &dateTimeRange{Name: name, Start: start, End: end})
}

// AddNumericRange adds a bucket to a field
// containing numeric values.  Documents with a
// numeric value falling into this range are
// tabulated as part of this bucket/range.
func (fr *FacetRequest) AddNumericRange(name string, min, max *float64) {
	if fr.NumericRanges == nil {
		fr.NumericRanges = make([]*numericRange, 0, 1)
	}
	fr.NumericRanges = append(fr.NumericRanges, &numericRange{Name: name, Min: min, Max: max})
}

// FacetsRequest groups together all the
// FacetRequest objects for a single query.
type FacetsRequest map[string]*FacetRequest

func (fr FacetsRequest) Validate() error {
	for _, v := range fr {
		err := v.Validate()
		if err != nil {
			return err
		}
	}
	return nil
}

// HighlightRequest describes how field matches
// should be highlighted.
type HighlightRequest struct {
	Style  *string  `json:"style"`
	Fields []string `json:"fields"`
}

// NewHighlight creates a default
// HighlightRequest.
func NewHighlight() *HighlightRequest {
	return &HighlightRequest{}
}

// NewHighlightWithStyle creates a HighlightRequest
// with an alternate style.
func NewHighlightWithStyle(style string) *HighlightRequest {
	return &HighlightRequest{
		Style: &style,
	}
}

func (h *HighlightRequest) AddField(field string) {
	if h.Fields == nil {
		h.Fields = make([]string, 0, 1)
	}
	h.Fields = append(h.Fields, field)
}

// A SearchRequest describes all the parameters
// needed to search the index.
// Query is required.
// Size/From describe how much and which part of the
// result set to return.
// Highlight describes optional search result
// highlighting.
// Fields describes a list of field values which
// should be retrieved for result documents, provided they
// were stored while indexing.
// Facets describe the set of facets to be computed.
// Explain triggers inclusion of additional search
// result score explanations.
// Sort describes the desired order for the results to be returned.
//
// A special field named "*" can be used to return all fields.
type SearchRequest struct {
	Query     Query             `json:"query"`
	Size      int               `json:"size"`
	From      int               `json:"from"`
	Highlight *HighlightRequest `json:"highlight"`
	Fields    []string          `json:"fields"`
	Facets    FacetsRequest     `json:"facets"`
	Explain   bool              `json:"explain"`
	Sort      search.SortOrder  `json:"sort"`
}

func (sr *SearchRequest) Validate() error {
	err := sr.Query.Validate()
	if err != nil {
		return err
	}

	return sr.Facets.Validate()
}

// AddFacet adds a FacetRequest to this SearchRequest
func (r *SearchRequest) AddFacet(facetName string, f *FacetRequest) {
	if r.Facets == nil {
		r.Facets = make(FacetsRequest, 1)
	}
	r.Facets[facetName] = f
}

// SortBy changes the request to use the requested sort order
func (r *SearchRequest) SortBy(order search.SortOrder) {
	r.Sort = order
}

// UnmarshalJSON deserializes a JSON representation of
// a SearchRequest
func (r *SearchRequest) UnmarshalJSON(input []byte) error {
	var temp struct {
		Q         json.RawMessage   `json:"query"`
		Size      *int              `json:"size"`
		From      int               `json:"from"`
		Highlight *HighlightRequest `json:"highlight"`
		Fields    []string          `json:"fields"`
		Facets    FacetsRequest     `json:"facets"`
		Explain   bool              `json:"explain"`
		Sort      search.SortOrder  `json:"sort"`
	}

	err := json.Unmarshal(input, &temp)
	if err != nil {
		return err
	}

	if temp.Size == nil {
		r.Size = 10
	} else {
		r.Size = *temp.Size
	}
	if temp.Sort == nil {
		r.Sort = search.SortOrder{&search.SortScore{Descending: true}}
	}
	r.From = temp.From
	r.Explain = temp.Explain
	r.Highlight = temp.Highlight
	r.Fields = temp.Fields
	r.Facets = temp.Facets
	r.Query, err = ParseQuery(temp.Q)
	if err != nil {
		return err
	}

	if r.Size < 0 {
		r.Size = 10
	}
	if r.From < 0 {
		r.From = 0
	}

	return nil

}

// NewSearchRequest creates a new SearchRequest
// for the Query, using default values for all
// other search parameters.
func NewSearchRequest(q Query) *SearchRequest {
	return NewSearchRequestOptions(q, 10, 0, false)
}

// NewSearchRequestOptions creates a new SearchRequest
// for the Query, with the requested size, from
// and explanation search parameters.
// By default results are ordered by score, descending.
func NewSearchRequestOptions(q Query, size, from int, explain bool) *SearchRequest {
	return &SearchRequest{
		Query:   q,
		Size:    size,
		From:    from,
		Explain: explain,
		Sort:    search.SortOrder{&search.SortScore{Descending: true}},
	}
}

// IndexErrMap tracks errors with the name of the index where it occurred
type IndexErrMap map[string]error

// MarshalJSON seralizes the error into a string for JSON consumption
func (iem IndexErrMap) MarshalJSON() ([]byte, error) {
	tmp := make(map[string]string, len(iem))
	for k, v := range iem {
		tmp[k] = v.Error()
	}
	return json.Marshal(tmp)
}

func (iem IndexErrMap) UnmarshalJSON(data []byte) error {
	var tmp map[string]string
	err := json.Unmarshal(data, &tmp)
	if err != nil {
		return err
	}
	for k, v := range tmp {
		iem[k] = fmt.Errorf("%s", v)
	}
	return nil
}

// SearchStatus is a secion in the SearchResult reporting how many
// underlying indexes were queried, how many were successful/failed
// and a map of any errors that were encountered
type SearchStatus struct {
	Total      int         `json:"total"`
	Failed     int         `json:"failed"`
	Successful int         `json:"successful"`
	Errors     IndexErrMap `json:"errors,omitempty"`
}

// Merge will merge together multiple SearchStatuses during a MultiSearch
func (ss *SearchStatus) Merge(other *SearchStatus) {
	ss.Total += other.Total
	ss.Failed += other.Failed
	ss.Successful += other.Successful
	if len(other.Errors) > 0 {
		if ss.Errors == nil {
			ss.Errors = make(map[string]error)
		}
		for otherIndex, otherError := range other.Errors {
			ss.Errors[otherIndex] = otherError
		}
	}
}

// A SearchResult describes the results of executing
// a SearchRequest.
type SearchResult struct {
	Status   *SearchStatus                  `json:"status"`
	Request  *SearchRequest                 `json:"request"`
	Hits     search.DocumentMatchCollection `json:"hits"`
	Total    uint64                         `json:"total_hits"`
	MaxScore float64                        `json:"max_score"`
	Took     time.Duration                  `json:"took"`
	Facets   search.FacetResults            `json:"facets"`
}

func (sr *SearchResult) String() string {
	rv := ""
	if sr.Total > 0 {
		if sr.Request.Size > 0 {
			rv = fmt.Sprintf("%d matches, showing %d through %d, took %s\n", sr.Total, sr.Request.From+1, sr.Request.From+len(sr.Hits), sr.Took)
			for i, hit := range sr.Hits {
				rv += fmt.Sprintf("%5d. %s (%f)\n", i+sr.Request.From+1, hit.ID, hit.Score)
				for fragmentField, fragments := range hit.Fragments {
					rv += fmt.Sprintf("\t%s\n", fragmentField)
					for _, fragment := range fragments {
						rv += fmt.Sprintf("\t\t%s\n", fragment)
					}
				}
				for otherFieldName, otherFieldValue := range hit.Fields {
					if _, ok := hit.Fragments[otherFieldName]; !ok {
						rv += fmt.Sprintf("\t%s\n", otherFieldName)
						rv += fmt.Sprintf("\t\t%v\n", otherFieldValue)
					}
				}
			}
		} else {
			rv = fmt.Sprintf("%d matches, took %s\n", sr.Total, sr.Took)
		}
	} else {
		rv = "No matches"
	}
	if len(sr.Facets) > 0 {
		rv += fmt.Sprintf("Facets:\n")
		for fn, f := range sr.Facets {
			rv += fmt.Sprintf("%s(%d)\n", fn, f.Total)
			for _, t := range f.Terms {
				rv += fmt.Sprintf("\t%s(%d)\n", t.Term, t.Count)
			}
			if f.Other != 0 {
				rv += fmt.Sprintf("\tOther(%d)\n", f.Other)
			}
		}
	}
	return rv
}

// Merge will merge together multiple SearchResults during a MultiSearch
func (sr *SearchResult) Merge(other *SearchResult) {
	sr.Status.Merge(other.Status)
	sr.Hits = append(sr.Hits, other.Hits...)
	sr.Total += other.Total
	if other.MaxScore > sr.MaxScore {
		sr.MaxScore = other.MaxScore
	}
	sr.Facets.Merge(other.Facets)
}
<|endoftext|>


<commit_before><commit_msg>Split out subject fields<commit_after><|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/howeyc/gopass"
	"github.com/koding/kite"
	"io/ioutil"
	"log"
	"os"
)

const Name = "secret"
const Version = "0.1.0"

// create gpg keys with
// $ gpg --gen-key
// ensure you correct paths and passphrase

const mysecretstring = "this is so very secret!"
const prefix = "/Users/peter/"
const secretKeyring = prefix + ".gnupg/secring.gpg"
const publicKeyring = prefix + ".gnupg/pubring.gpg"

// Command-line commands
var commands = []cli.Command{
	{
		Name:   "send",
		Usage:  "Sends a secret",
		Action: send,
	},
	{
		Name:   "encrypt",
		Usage:  "Encrypts a secret",
		Action: encrypt,
	},
	{
		Name:   "server",
		Usage:  "Starts the server",
		Action: server,
	},
	{
		Name:   "client",
		Usage:  "Connects to the server",
		Action: client,
	},
}

// Send subcommand

func send(c *cli.Context) {
	_, err := fmt.Println("Sending.")
	if err != nil {
		exit(err)
	}
}

// Encrypt subcommand

func encrypt(c *cli.Context) {
	err := encryptMessage()
	if err != nil {
		exit(err)
	}
}

// Server subcommand

func server(c *cli.Context) {
	// Create the kite
	k := kite.New(Name, Version)
	k.HandleFunc("hello", hello).DisableAuthentication()

	// Run the kite
	k.Config.Port = 3636
	k.Run()
}

func hello(r *kite.Request) (interface{}, error) {
	return "hey", nil
}

// Client subcommand

func client(c *cli.Context) {
	// Create the kite
	k := kite.New(Name, Version)

	// Connect to the server kite
	client := k.NewClient("http://localhost:3636/kite")
	client.Dial()

	// Say hello
	response, _ := client.Tell("hello")
	fmt.Println(response.MustString())
}

func exit(err error) {
	log.Fatal(err)
}

func encryptMessage() error {
	fmt.Println("Secret:", mysecretstring)

	// Read in public key
	keyringFileBuffer, _ := os.Open(publicKeyring)
	defer keyringFileBuffer.Close()
	entitylist, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		return err
	}

	// Encrypt string
	buf := new(bytes.Buffer)
	w, err := openpgp.Encrypt(buf, entitylist, nil, nil, nil)
	if err != nil {
		return err
	}
	_, err = w.Write([]byte(mysecretstring))
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	// Encode to base64
	bytesp, err := ioutil.ReadAll(buf)
	if err != nil {
		return err
	}
	encstr := base64.StdEncoding.EncodeToString(bytesp)

	// Output encrypted/encoded string
	//if options.Verbose {
	//fmt.Println("Encrypted Secret:", encstr)
	//}

	// Here is where I would transfer the encrypted string to someone else
	// but we'll just decrypt it in the same code

	// Init some vars
	var entity2 *openpgp.Entity
	var entitylist2 openpgp.EntityList

	// Open the private key file
	keyringFileBuffer2, err := os.Open(secretKeyring)
	if err != nil {
		return err
	}
	defer keyringFileBuffer2.Close()
	entitylist2, err = openpgp.ReadKeyRing(keyringFileBuffer2)
	if err != nil {
		return err
	}
	entity2 = entitylist2[0]

	// Get the passphrase and read the private key.
	// Have not touched the encrypted string yet
	//if options.Verbose {
	//fmt.Println("Decrypting private key using passphrase")
	//}
	if !decryptKey(entity2) {
		fmt.Println("Incorrect password. Exiting.")
		return nil
	}
	//for !decryptKey(entity2) {
	//}
	//if options.Verbose {
	//fmt.Println("Finished decrypting private key using passphrase")
	//}

	// Decode the base64 string
	dec, err := base64.StdEncoding.DecodeString(encstr)
	if err != nil {
		return err
	}

	// Decrypt it with the contents of the private key
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entitylist2, nil, nil)
	if err != nil {
		return err
	}
	bytess, err := ioutil.ReadAll(md.UnverifiedBody)
	if err != nil {
		return err
	}
	decstr := string(bytess)

	// Should be done
	fmt.Println("Decrypted Secret:", decstr)

	return nil
}

func decryptKey(entity *openpgp.Entity) bool {
	// Get the password
	fmt.Printf("Password: ")
	passphrase := gopass.GetPasswd()
	passphrasebyte := []byte(passphrase)

	// Decrypt the key and subkeys
	err := entity.PrivateKey.Decrypt(passphrasebyte)
	if err != nil {
		return false
	}
	for _, subkey := range entity.Subkeys {
		err := subkey.PrivateKey.Decrypt(passphrasebyte)
		if err != nil {
			return false
		}
	}

	return true
}

func main() {
	// Setup the app
	app := cli.NewApp()
	app.Name = Name
	app.Author = "Peter Valdez"
	app.Email = "peter@nycmesh.net"
	app.Usage = "Send secrets with ease."
	app.Version = Version
	app.Commands = commands

	// Run the app
	app.Run(os.Args)
}
<commit_msg>Get basic send and receive working with encryption.<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/howeyc/gopass"
	"github.com/koding/kite"
	"io/ioutil"
	"log"
	"os"
)

// General values

const Author = "Peter Valdez"
const Email = "peter@nycmesh.net"
const Name = "secret"
const Usage = "Send secrets with ease."
const Version = "0.1.0"

const mysecretstring = "this is so very secret!"
const prefix = "/Users/peter/"
const secretKeyring = prefix + ".gnupg/secring.gpg"
const publicKeyring = prefix + ".gnupg/pubring.gpg"

var context *cli.Context

// Flags

var Flags = []cli.Flag{
	cli.BoolFlag{
		Name:  "verbose",
		Usage: "talks more",
	},
}

// Subcommands

var Commands = []cli.Command{
	{
		Name:   "send",
		Usage:  "Sends a secret",
		Action: handle(send),
	},
	{
		Name:   "receive",
		Usage:  "Waits for secrets",
		Action: handle(receive),
		Flags:  Flags,
	},
}

func handle(f func() error) func(*cli.Context) {
	return func(c *cli.Context) {
		// Store the context globally
		context = c

		// Run the function
		err := f()
		if err != nil {
			log.Fatal(err)
		}
	}
}

// Receive subcommand

func receive() error {
	// Create and configure the kite
	k := kite.New(Name, Version)
	k.Config.Port = 4321
	k.SetLogLevel(kite.ERROR)
	k.HandleFunc("secret", secret).DisableAuthentication()

	// Run the kite
	fmt.Println("Waiting for secrets...")
	k.Run()

	return nil
}

func secret(r *kite.Request) (interface{}, error) {
	// Retrieve the encrypted secret
	encrypted := r.Args.One().MustString()

	// Decrypt and print the secret
	decrypted, err := decryptMessage(encrypted)
	if err != nil {
		return nil, err
	}
	fmt.Println(decrypted)

	// Return an acknowledgment
	return "Received.", nil
}

// Send subcommand

func send() error {
	// Create the kite
	k := kite.New(Name, Version)

	// Connect to the server kite
	client := k.NewClient("http://localhost:4321/kite")
	client.Dial()

	// Send them a secret
	fmt.Println("Sending secret...")
	secret, err := encryptMessage(mysecretstring)
	if err != nil {
		return err
	}

	response, _ := client.Tell("secret", secret)
	fmt.Println(response.MustString())

	return nil
}

func encryptMessage(str string) (string, error) {
	// Read in public key
	keyringFileBuffer, _ := os.Open(publicKeyring)
	defer keyringFileBuffer.Close()
	entitylist, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		return "", err
	}

	// Encrypt string
	buf := new(bytes.Buffer)
	w, err := openpgp.Encrypt(buf, entitylist, nil, nil, nil)
	if err != nil {
		return "", err
	}
	_, err = w.Write([]byte(str))
	if err != nil {
		return "", err
	}
	err = w.Close()
	if err != nil {
		return "", err
	}

	// Encode to base64
	bytesp, err := ioutil.ReadAll(buf)
	if err != nil {
		return "", err
	}
	encstr := base64.StdEncoding.EncodeToString(bytesp)

	return encstr, nil
}

func decryptMessage(encstr string) (string, error) {
	var entity2 *openpgp.Entity
	var entitylist2 openpgp.EntityList

	// Output encrypted/encoded string
	if context.Bool("verbose") {
		fmt.Println("Encrypted Secret:", encstr)
	}

	// Open the private key file
	keyringFileBuffer2, err := os.Open(secretKeyring)
	if err != nil {
		return "", err
	}
	defer keyringFileBuffer2.Close()
	entitylist2, err = openpgp.ReadKeyRing(keyringFileBuffer2)
	if err != nil {
		return "", err
	}
	entity2 = entitylist2[0]

	// Get the passphrase and read the private key.
	// Have not touched the encrypted string yet
	if context.Bool("verbose") {
		fmt.Println("Decrypting private key using passphrase")
	}
	if !decryptKey(entity2) {
		fmt.Println("Incorrect password. Exiting.")
		return "", nil
	}
	if context.Bool("verbose") {
		fmt.Println("Finished decrypting private key using passphrase")
	}

	// Decode the base64 string
	dec, err := base64.StdEncoding.DecodeString(encstr)
	if err != nil {
		return "", err
	}

	// Decrypt it with the contents of the private key
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entitylist2, nil, nil)
	if err != nil {
		return "", err
	}
	bytess, err := ioutil.ReadAll(md.UnverifiedBody)
	if err != nil {
		return "", err
	}
	decstr := string(bytess)

	return decstr, nil
}

func decryptKey(entity *openpgp.Entity) bool {
	// Get the password
	fmt.Printf("Password: ")
	passphrase := gopass.GetPasswd()
	passphrasebyte := []byte(passphrase)

	// Decrypt the key and subkeys
	err := entity.PrivateKey.Decrypt(passphrasebyte)
	if err != nil {
		return false
	}
	for _, subkey := range entity.Subkeys {
		err := subkey.PrivateKey.Decrypt(passphrasebyte)
		if err != nil {
			return false
		}
	}

	return true
}

func main() {
	// Setup the app
	app := cli.NewApp()
	app.Name = Name
	app.Author = Author
	app.Email = Email
	app.Usage = Usage
	app.Version = Version
	app.Commands = Commands

	// Run the app
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"fmt"
	"github.com/howeyc/gopass"
	"github.com/jessevdk/go-flags"
	"io/ioutil"
	"log"
	"os"
)

// Commmand line arguments
var options struct {
	Verbose bool `short:"v" long:"verbose" description:"make the output verbose"`
}

// create gpg keys with
// $ gpg --gen-key
// ensure you correct paths and passphrase

const mysecretstring = "this is so very secret!"
const prefix = "/Users/peter/"
const secretKeyring = prefix + ".gnupg/secring.gpg"
const publicKeyring = prefix + ".gnupg/pubring.gpg"

func encryptMessage() error {
	fmt.Println("Secret:", mysecretstring)

	// Read in public key
	keyringFileBuffer, _ := os.Open(publicKeyring)
	defer keyringFileBuffer.Close()
	entitylist, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		return err
	}

	// Encrypt string
	buf := new(bytes.Buffer)
	w, err := openpgp.Encrypt(buf, entitylist, nil, nil, nil)
	if err != nil {
		return err
	}
	_, err = w.Write([]byte(mysecretstring))
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	// Encode to base64
	bytesp, err := ioutil.ReadAll(buf)
	if err != nil {
		return err
	}
	encstr := base64.StdEncoding.EncodeToString(bytesp)

	// Output encrypted/encoded string
	if options.Verbose {
		fmt.Println("Encrypted Secret:", encstr)
	}

	// Here is where I would transfer the encrypted string to someone else
	// but we'll just decrypt it in the same code

	// Init some vars
	var entity2 *openpgp.Entity
	var entitylist2 openpgp.EntityList

	// Open the private key file
	keyringFileBuffer2, err := os.Open(secretKeyring)
	if err != nil {
		return err
	}
	defer keyringFileBuffer2.Close()
	entitylist2, err = openpgp.ReadKeyRing(keyringFileBuffer2)
	if err != nil {
		return err
	}
	entity2 = entitylist2[0]

	// Get the passphrase and read the private key.
	// Have not touched the encrypted string yet
	if options.Verbose {
		fmt.Println("Decrypting private key using passphrase")
	}
	if !decryptKey(entity2) {
		fmt.Println("Incorrect password. Exiting.")
		return nil
	}
	//for !decryptKey(entity2) {
	//}
	if options.Verbose {
		fmt.Println("Finished decrypting private key using passphrase")
	}

	// Decode the base64 string
	dec, err := base64.StdEncoding.DecodeString(encstr)
	if err != nil {
		return err
	}

	// Decrypt it with the contents of the private key
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entitylist2, nil, nil)
	if err != nil {
		return err
	}
	bytess, err := ioutil.ReadAll(md.UnverifiedBody)
	if err != nil {
		return err
	}
	decstr := string(bytess)

	// Should be done
	fmt.Println("Decrypted Secret:", decstr)

	return nil
}

func decryptKey(entity *openpgp.Entity) bool {
	// Get the password
	fmt.Printf("Password: ")
	passphrase := gopass.GetPasswd()
	passphrasebyte := []byte(passphrase)

	// Decrypt the key and subkeys
	err := entity.PrivateKey.Decrypt(passphrasebyte)
	if err != nil {
		return false
	}
	for _, subkey := range entity.Subkeys {
		err := subkey.PrivateKey.Decrypt(passphrasebyte)
		if err != nil {
			return false
		}
	}

	return true
}

func main() {
	// Parse command line arguments
	_, err := flags.Parse(&options)
	if err != nil {
		return
	}

	err = encryptMessage()
	if err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Use codegangsta/cli to easily create the command.<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/howeyc/gopass"
	"io/ioutil"
	"log"
	"os"
)

// Commmand line arguments

// create gpg keys with
// $ gpg --gen-key
// ensure you correct paths and passphrase

const mysecretstring = "this is so very secret!"
const prefix = "/Users/peter/"
const secretKeyring = prefix + ".gnupg/secring.gpg"
const publicKeyring = prefix + ".gnupg/pubring.gpg"

// Command-line commands
var commands = []cli.Command{
	{
		Name:   "send",
		Usage:  "Sends a secret",
		Action: send,
	},
	{
		Name:   "encrypt",
		Usage:  "Encrypts a secret",
		Action: encrypt,
	},
}

func send(c *cli.Context) {
	_, err := fmt.Println("Sending.")
	if err != nil {
		exit(err)
	}
}

func encrypt(c *cli.Context) {
	err := encryptMessage()
	if err != nil {
		exit(err)
	}
}

func exit(err error) {
	log.Fatal(err)
}

func encryptMessage() error {
	fmt.Println("Secret:", mysecretstring)

	// Read in public key
	keyringFileBuffer, _ := os.Open(publicKeyring)
	defer keyringFileBuffer.Close()
	entitylist, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		return err
	}

	// Encrypt string
	buf := new(bytes.Buffer)
	w, err := openpgp.Encrypt(buf, entitylist, nil, nil, nil)
	if err != nil {
		return err
	}
	_, err = w.Write([]byte(mysecretstring))
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	// Encode to base64
	bytesp, err := ioutil.ReadAll(buf)
	if err != nil {
		return err
	}
	encstr := base64.StdEncoding.EncodeToString(bytesp)

	// Output encrypted/encoded string
	//if options.Verbose {
	//fmt.Println("Encrypted Secret:", encstr)
	//}

	// Here is where I would transfer the encrypted string to someone else
	// but we'll just decrypt it in the same code

	// Init some vars
	var entity2 *openpgp.Entity
	var entitylist2 openpgp.EntityList

	// Open the private key file
	keyringFileBuffer2, err := os.Open(secretKeyring)
	if err != nil {
		return err
	}
	defer keyringFileBuffer2.Close()
	entitylist2, err = openpgp.ReadKeyRing(keyringFileBuffer2)
	if err != nil {
		return err
	}
	entity2 = entitylist2[0]

	// Get the passphrase and read the private key.
	// Have not touched the encrypted string yet
	//if options.Verbose {
	//fmt.Println("Decrypting private key using passphrase")
	//}
	if !decryptKey(entity2) {
		fmt.Println("Incorrect password. Exiting.")
		return nil
	}
	//for !decryptKey(entity2) {
	//}
	//if options.Verbose {
	//fmt.Println("Finished decrypting private key using passphrase")
	//}

	// Decode the base64 string
	dec, err := base64.StdEncoding.DecodeString(encstr)
	if err != nil {
		return err
	}

	// Decrypt it with the contents of the private key
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), entitylist2, nil, nil)
	if err != nil {
		return err
	}
	bytess, err := ioutil.ReadAll(md.UnverifiedBody)
	if err != nil {
		return err
	}
	decstr := string(bytess)

	// Should be done
	fmt.Println("Decrypted Secret:", decstr)

	return nil
}

func decryptKey(entity *openpgp.Entity) bool {
	// Get the password
	fmt.Printf("Password: ")
	passphrase := gopass.GetPasswd()
	passphrasebyte := []byte(passphrase)

	// Decrypt the key and subkeys
	err := entity.PrivateKey.Decrypt(passphrasebyte)
	if err != nil {
		return false
	}
	for _, subkey := range entity.Subkeys {
		err := subkey.PrivateKey.Decrypt(passphrasebyte)
		if err != nil {
			return false
		}
	}

	return true
}

func main() {
	// Setup the app
	app := cli.NewApp()
	app.Name = "secret"
	app.Author = "Peter Valdez"
	app.Email = "peter@nycmesh.net"
	app.Usage = "Send secrets with ease."
	app.Version = "0.1.0"
	app.Commands = commands

	// Run the app
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package chem

import (
	"database/sql"
	"fmt"
	"reflect"
	"strings"
)

type SelectStmt struct {
	columns []Column
	filters []Filter
}

func Select(columnThings ...Columnser) SelectStmt {
	columns := make([]Column, 0, len(columnThings))
	for _, c := range columnThings {
		columns = append(columns, c.Columns()...)
	}

	return SelectStmt{columns: columns}
}

func (s SelectStmt) Where(filters ...Filter) SelectStmt {
	s.filters = filters
	return s
}

func toTableNames(columns []Column) []string {
	names := make(map[string]bool)
	for _, c := range columns {
		names[c.Table().Name()] = true
	}
	nameList := make([]string, 0, len(names))
	for name := range names {
		nameList = append(nameList, name)
	}
	return nameList
}

func toColumnExpressions(columns []Column, withTableName bool) (out []string) {
	for _, c := range columns {
		out = append(out, c.toColumnExpression(withTableName))
	}
	return
}

func flattenValues(values []interface{}) []interface{} {
	out := make([]interface{}, 0, len(values))
	flattened := false

	for _, value := range values {
		reflection := reflect.ValueOf(value).Elem()
		reflectType := reflection.Type()
		switch reflectType.Kind() {
		case reflect.Struct:
			for i := 0; i < reflection.NumField(); i++ {
				// flatten this struct into pointers to each field of it
				out = append(out, reflection.Field(i).Addr().Interface())
			}
			flattened = true
		default:
			out = append(out, value)
		}
	}
	// if we ever flattened any structs, there could be nested structs, so let's recurse
	if flattened {
		return flattenValues(out)
	}
	// otherwise we're done
	return out
}

func makeWhereClause(f Filter, withTableNames bool) string {
	expression := f.toBooleanExpression(withTableNames)
	if expression == "" {
		return ""
	}
	return fmt.Sprintf("WHERE %v", expression)
}

func (s SelectStmt) constructSQL() string {
	tableNames := toTableNames(s.columns)
	fullyQualifyColumns := (len(tableNames) > 1)
	return strings.Join(
		filterStringSlice(
			fmt.Sprintf(
				"SELECT %v FROM %v",
				strings.Join(toColumnExpressions(s.columns, fullyQualifyColumns), ", "),
				strings.Join(tableNames, ", "),
			),
			makeWhereClause(AND(s.filters...), fullyQualifyColumns),
		),
		" ",
	)
}

func (s SelectStmt) First(tx *sql.Tx, values ...interface{}) error {
	return tx.QueryRow(
		s.constructSQL(),
		AND(s.filters...).binds()...,
	).Scan(flattenValues(values)...)
}

func (s SelectStmt) All(tx *sql.Tx, values ...interface{}) error {
	reflections := make([]reflect.Value, len(values))
	for i, value := range values {
		reflection := reflect.ValueOf(value).Elem()
		reflectType := reflection.Type()
		if reflectType.Kind() != reflect.Slice {
			return NonSliceError{
				Type: reflectType,
			}
		}
		reflections[i] = reflection
	}

	rows, err := tx.Query(
		s.constructSQL(),
		AND(s.filters...).binds()...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		// create new slice element value(s) to scan the database row into
		rowValues := make([]interface{}, len(reflections))
		for i, reflection := range reflections {
			rowValues[i] = reflect.New(reflection.Type().Elem()).Interface()
		}

		err = rows.Scan(flattenValues(rowValues)...)
		if err != nil {
			return err
		}

		for i, rowValue := range rowValues {
			reflections[i].Set(reflect.Append(reflections[i], reflect.ValueOf(rowValue).Elem()))
		}
	}

	return rows.Err()
}
<commit_msg>make SelectStmt.Where more generalized and fix style<commit_after>package chem

import (
	"database/sql"
	"fmt"
	"reflect"
	"strings"
)

type SelectStmt struct {
	columns []Column
	filters []Filter
}

func Select(columnThings ...Columnser) SelectStmt {
	columns := make([]Column, 0, len(columnThings))
	for _, c := range columnThings {
		columns = append(columns, c.Columns()...)
	}

	return SelectStmt{columns: columns}
}

func (stmt SelectStmt) Where(filters ...Filter) SelectStmt {
	stmt.filters = append(stmt.filters, filters...)
	return stmt
}

func toTableNames(columns []Column) []string {
	names := make(map[string]bool)
	for _, c := range columns {
		names[c.Table().Name()] = true
	}
	nameList := make([]string, 0, len(names))
	for name := range names {
		nameList = append(nameList, name)
	}
	return nameList
}

func toColumnExpressions(columns []Column, withTableName bool) (out []string) {
	for _, c := range columns {
		out = append(out, c.toColumnExpression(withTableName))
	}
	return
}

func flattenValues(values []interface{}) []interface{} {
	out := make([]interface{}, 0, len(values))
	flattened := false

	for _, value := range values {
		reflection := reflect.ValueOf(value).Elem()
		reflectType := reflection.Type()
		switch reflectType.Kind() {
		case reflect.Struct:
			for i := 0; i < reflection.NumField(); i++ {
				// flatten this struct into pointers to each field of it
				out = append(out, reflection.Field(i).Addr().Interface())
			}
			flattened = true
		default:
			out = append(out, value)
		}
	}
	// if we ever flattened any structs, there could be nested structs, so let's recurse
	if flattened {
		return flattenValues(out)
	}
	// otherwise we're done
	return out
}

func makeWhereClause(f Filter, withTableNames bool) string {
	expression := f.toBooleanExpression(withTableNames)
	if expression == "" {
		return ""
	}
	return fmt.Sprintf("WHERE %v", expression)
}

func (s SelectStmt) constructSQL() string {
	tableNames := toTableNames(s.columns)
	fullyQualifyColumns := (len(tableNames) > 1)
	return strings.Join(
		filterStringSlice(
			fmt.Sprintf(
				"SELECT %v FROM %v",
				strings.Join(toColumnExpressions(s.columns, fullyQualifyColumns), ", "),
				strings.Join(tableNames, ", "),
			),
			makeWhereClause(AND(s.filters...), fullyQualifyColumns),
		),
		" ",
	)
}

func (s SelectStmt) First(tx *sql.Tx, values ...interface{}) error {
	return tx.QueryRow(
		s.constructSQL(),
		AND(s.filters...).binds()...,
	).Scan(flattenValues(values)...)
}

func (s SelectStmt) All(tx *sql.Tx, values ...interface{}) error {
	reflections := make([]reflect.Value, len(values))
	for i, value := range values {
		reflection := reflect.ValueOf(value).Elem()
		reflectType := reflection.Type()
		if reflectType.Kind() != reflect.Slice {
			return NonSliceError{
				Type: reflectType,
			}
		}
		reflections[i] = reflection
	}

	rows, err := tx.Query(
		s.constructSQL(),
		AND(s.filters...).binds()...,
	)
	if err != nil {
		return err
	}
	defer rows.Close()

	for rows.Next() {
		// create new slice element value(s) to scan the database row into
		rowValues := make([]interface{}, len(reflections))
		for i, reflection := range reflections {
			rowValues[i] = reflect.New(reflection.Type().Elem()).Interface()
		}

		err = rows.Scan(flattenValues(rowValues)...)
		if err != nil {
			return err
		}

		for i, rowValue := range rowValues {
			reflections[i].Set(reflect.Append(reflections[i], reflect.ValueOf(rowValue).Elem()))
		}
	}

	return rows.Err()
}
<|endoftext|>