<commit_before>package annotations

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

	"regexp"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

var uuidExtractRegex = regexp.MustCompile(".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$")

// Service interface. Compatible with the baserwftapp service EXCEPT for
// 1) the Write function, which has signature Write(thing interface{}) error...
// 2) the DecodeJson function, which has signature DecodeJSON(*json.Decoder) (thing interface{}, identity string, err error)
// The problem is that we have a list of things, and the uuid is for a related OTHER thing
// TODO - move to implement a shared defined Service interface?
type Service interface {
	Write(contentUUID string, thing interface{}) (err error)
	Read(contentUUID string) (thing interface{}, found bool, err error)
	Delete(contentUUID string) (found bool, err error)
	Check() (err error)
	DecodeJSON(*json.Decoder) (thing interface{}, err error)
	Count() (int, error)
	Initialise() error
}

//holds the Neo4j-specific information
type service struct {
	conn            neoutils.NeoConnection
	platformVersion string
}

const (
	v1PlatformVersion         = "v1"
	v2PlatformVersion         = "v2"
	brightcovePlatformVersion = "brightcove"
)

//NewCypherAnnotationsService instantiate driver
func NewCypherAnnotationsService(cypherRunner neoutils.NeoConnection, platformVersion string) service {
	if platformVersion == "" {
		log.Fatalf("PlatformVersion was not specified!")
	}
	return service{cypherRunner, platformVersion}
}

// DecodeJSON decodes to a list of annotations, for ease of use this is a struct itself
func (s service) DecodeJSON(dec *json.Decoder) (interface{}, error) {
	a := annotations{}
	err := dec.Decode(&a)
	return a, err
}

func (s service) Read(contentUUID string) (thing interface{}, found bool, err error) {
	results := []annotation{}

	//TODO shouldn't return Provenances if none of the scores, agentRole or atTime are set
	statementTemplate := `
					MATCH (c:Thing{uuid:{contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing)
					WITH c, cc, rel, {id:cc.uuid,prefLabel:cc.prefLabel,types:labels(cc),predicate:type(rel)} as thing,
					collect(
						{scores:[
							{scoringSystem:'%s', value:rel.relevanceScore},
							{scoringSystem:'%s', value:rel.confidenceScore}],
						agentRole:rel.annotatedBy,
						atTime:rel.annotatedDate}) as provenances
					RETURN thing, provenances ORDER BY thing.id
									`
	statement := fmt.Sprintf(statementTemplate, relevanceScoringSystem, confidenceScoringSystem)

	query := &neoism.CypherQuery{
		Statement:  statement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		Result:     &results,
	}
	err = s.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v", contentUUID, query.Statement, err)
		return annotations{}, false, fmt.Errorf("Error accessing Annotations datastore for uuid: %s", contentUUID)
	}
	log.Debugf("CypherResult Read Annotations for uuid: %s was: %+v", contentUUID, results)
	if (len(results)) == 0 {
		return annotations{}, false, nil
	}

	for idx := range results {
		mapToResponseFormat(&results[idx])
	}

	return annotations(results), true, nil
}

//Delete removes all the annotations for this content. Ignore the nodes on either end -
//may leave nodes that are only 'things' inserted by this writer: clean up
//as a result of this will need to happen externally if required
func (s service) Delete(contentUUID string) (bool, error) {

	var deleteStatement string

	if s.platformVersion == v2PlatformVersion {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel:MENTIONS{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	} else if s.platformVersion == brightcovePlatformVersion {
		// TODO this clause should be refactored when all videos in Neo4j have brightcove only as lifecycle and no v1 reference
		deleteStatement = `	OPTIONAL MATCH (c:Thing{uuid: {contentUUID}})-[r]->(cc:Thing)
					WHERE r.lifecycle={lifecycle} OR r.lifecycle={v1Lifecycle}
					DELETE r`
	} else {
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	}

	query := &neoism.CypherQuery{
		Statement: deleteStatement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion,
			"lifecycle": lifecycle(s.platformVersion), "v1Lifecycle": lifecycle(v1PlatformVersion)},
		IncludeStats: true,
	}

	err := s.conn.CypherBatch([]*neoism.CypherQuery{query})

	stats, err := query.Stats()
	if err != nil {
		return false, err
	}

	return stats.ContainsUpdates, err
}

//Write a set of annotations associated with a piece of content. Any annotations
//already there will be removed
func (s service) Write(contentUUID string, thing interface{}) (err error) {
	annotationsToWrite := thing.(annotations)

	if contentUUID == "" {
		return errors.New("Content uuid is required")
	}
	if err := validateAnnotations(&annotationsToWrite); err != nil {
		log.Warnf("Validation of supplied annotations failed")
		return err
	}

	if len(annotationsToWrite) == 0 {
		log.Warnf("No new annotations supplied for content uuid: %s", contentUUID)
	}

	queries := append([]*neoism.CypherQuery{}, dropAllAnnotationsQuery(contentUUID, s.platformVersion))

	var statements = []string{}
	for _, annotationToWrite := range annotationsToWrite {
		query, err := createAnnotationQuery(contentUUID, annotationToWrite, s.platformVersion)
		if err != nil {
			return err
		}
		statements = append(statements, query.Statement)
		queries = append(queries, query)
	}
	log.Infof("Updated Annotations for content uuid: %s", contentUUID)
	log.Debugf("For update, ran statements: %+v", statements)

	return s.conn.CypherBatch(queries)
}

// Check tests neo4j by running a simple cypher query
func (s service) Check() error {
	return neoutils.Check(s.conn)
}

func (s service) Count() (int, error) {
	results := []struct {
		Count int `json:"c"`
	}{}

	query := &neoism.CypherQuery{
		Statement: `MATCH ()-[r{platformVersion:{platformVersion}}]->()
                WHERE r.lifecycle = {lifecycle}
                OR r.lifecycle IS NULL
                RETURN count(r) as c`,
		Parameters: neoism.Props{"platformVersion": s.platformVersion, "lifecycle": lifecycle(s.platformVersion)},
		Result:     &results,
	}

	err := s.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}

func (s service) Initialise() error {
	return nil // No constraints need to be set up
}

func createAnnotationRelationship(relation string) (statement string) {
	stmt := `
                MERGE (content:Thing{uuid:{contentID}})
                MERGE (upp:Identifier:UPPIdentifier{value:{conceptID}})
                MERGE (upp)-[:IDENTIFIES]->(concept:Thing) ON CREATE SET concept.uuid = {conceptID}
                MERGE (content)-[pred:%s {platformVersion:{platformVersion}}]->(concept)
                SET pred={annProps}
          `
	statement = fmt.Sprintf(stmt, relation)
	return statement
}

func getRelationshipFromPredicate(predicate string) (relation string) {
	if predicate != "" {
		relation = relations[predicate]
	} else {
		relation = relations["mentions"]
	}
	return relation
}

func createAnnotationQuery(contentUUID string, ann annotation, platformVersion string) (*neoism.CypherQuery, error) {
	query := neoism.CypherQuery{}
	thingID, err := extractUUIDFromURI(ann.Thing.ID)
	if err != nil {
		return nil, err
	}

	//todo temporary change to deal with multiple provenances
	/*if len(ann.Provenances) > 1 {
		return nil, errors.New("Cannot insert a MENTIONS annotation with multiple provenances")
	}*/

	var prov provenance
	params := map[string]interface{}{}
	params["platformVersion"] = platformVersion
	params["lifecycle"] = lifecycle(platformVersion)

	if len(ann.Provenances) >= 1 {
		prov = ann.Provenances[0]
		annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, supplied, err := extractDataFromProvenance(&prov)

		if err != nil {
			log.Infof("ERROR=%s", err)
			return nil, err
		}

		if supplied == true {
			if annotatedBy != "" {
				params["annotatedBy"] = annotatedBy
			}
			if prov.AtTime != "" {
				params["annotatedDateEpoch"] = annotatedDateEpoch
				params["annotatedDate"] = prov.AtTime
			}
			params["relevanceScore"] = relevanceScore
			params["confidenceScore"] = confidenceScore
		}
	}

	relation := getRelationshipFromPredicate(ann.Thing.Predicate)
	query.Statement = createAnnotationRelationship(relation)
	query.Parameters = map[string]interface{}{
		"contentID":       contentUUID,
		"conceptID":       thingID,
		"platformVersion": platformVersion,
		"annProps":        params,
	}
	return &query, nil
}

func extractDataFromProvenance(prov *provenance) (string, int64, float64, float64, bool, error) {
	if len(prov.Scores) == 0 {
		return "", -1, -1, -1, false, nil
	}
	var annotatedBy string
	var annotatedDateEpoch int64
	var confidenceScore, relevanceScore float64
	var err error
	if prov.AgentRole != "" {
		annotatedBy, err = extractUUIDFromURI(prov.AgentRole)
	}
	if prov.AtTime != "" {
		annotatedDateEpoch, err = convertAnnotatedDateToEpoch(prov.AtTime)
	}
	relevanceScore, confidenceScore, err = extractScores(prov.Scores)

	if err != nil {
		return "", -1, -1, -1, true, err
	}
	return annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, true, nil
}

func extractUUIDFromURI(uri string) (string, error) {
	result := uuidExtractRegex.FindStringSubmatch(uri)
	if len(result) == 2 {
		return result[1], nil
	}
	return "", fmt.Errorf("Couldn't extract uuid from uri %s", uri)
}

func convertAnnotatedDateToEpoch(annotatedDateString string) (int64, error) {
	datetimeEpoch, err := time.Parse(time.RFC3339, annotatedDateString)

	if err != nil {
		return 0, err
	}

	return datetimeEpoch.Unix(), nil
}

func extractScores(scores []score) (float64, float64, error) {
	var relevanceScore, confidenceScore float64
	for _, score := range scores {
		scoringSystem := score.ScoringSystem
		value := score.Value
		switch scoringSystem {
		case relevanceScoringSystem:
			relevanceScore = value
		case confidenceScoringSystem:
			confidenceScore = value
		}
	}
	return relevanceScore, confidenceScore, nil
}

func dropAllAnnotationsQuery(contentUUID string, platformVersion string) *neoism.CypherQuery {

	var matchStmtTemplate string

	//TODO hard-coded verification:
	//WE STILL NEED THIS UNTIL EVERYTHNG HAS A LIFECYCLE PROPERTY!
	// -> necessary for brands - which got written by content-api with isClassifiedBy relationship, and should not be deleted by annotations-rw
	// -> so far brands are the only v2 concepts which have isClassifiedBy relationship; as soon as this changes: implementation needs to be updated
	if platformVersion == v2PlatformVersion {
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r:MENTIONS{platformVersion:{platformVersion}}]->(t:Thing)
                         		DELETE r`
	} else if platformVersion == brightcovePlatformVersion {
		// TODO this clause should be refactored when all videos in Neo4j have brightcove only as lifecycle and no v1 reference
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
					WHERE r.lifecycle={lifecycle} OR r.lifecycle={v1Lifecycle}
					DELETE r`
	} else {
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
					WHERE r.platformVersion={platformVersion}
                         		DELETE r`
	}

	query := neoism.CypherQuery{}
	query.Statement = matchStmtTemplate
	query.Parameters = neoism.Props{"contentID": contentUUID, "platformVersion": platformVersion,
		"lifecycle": lifecycle(platformVersion), "v1Lifecycle": lifecycle(v1PlatformVersion)}
	return &query
}

func validateAnnotations(annotations *annotations) error {
	//TODO - for consistency, we should probably just not create the annotation?
	for _, annotation := range *annotations {
		if annotation.Thing.ID == "" {
			return ValidationError{fmt.Sprintf("Concept uuid missing for annotation %+v", annotation)}
		}
	}
	return nil
}

//ValidationError is thrown when the annotations are not valid because mandatory information is missing
type ValidationError struct {
	Msg string
}

func (v ValidationError) Error() string {
	return v.Msg
}

func mapToResponseFormat(ann *annotation) {
	ann.Thing.ID = mapper.IDURL(ann.Thing.ID)
	// We expect only ONE provenance - provenance value is considered valid even if the AgentRole is not specified. See: v1 - isClassifiedBy
	for idx := range ann.Provenances {
		if ann.Provenances[idx].AgentRole != "" {
			ann.Provenances[idx].AgentRole = mapper.IDURL(ann.Provenances[idx].AgentRole)
		}
	}
}

func lifecycle(platformVersion string) string {
	return "annotations-" + platformVersion
}
<commit_msg>Small refactoring- change multi branches if with switch.<commit_after>package annotations

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

	"regexp"

	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/neo-utils-go/neoutils"
	log "github.com/Sirupsen/logrus"
	"github.com/jmcvetta/neoism"
)

var uuidExtractRegex = regexp.MustCompile(".*/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$")

// Service interface. Compatible with the baserwftapp service EXCEPT for
// 1) the Write function, which has signature Write(thing interface{}) error...
// 2) the DecodeJson function, which has signature DecodeJSON(*json.Decoder) (thing interface{}, identity string, err error)
// The problem is that we have a list of things, and the uuid is for a related OTHER thing
// TODO - move to implement a shared defined Service interface?
type Service interface {
	Write(contentUUID string, thing interface{}) (err error)
	Read(contentUUID string) (thing interface{}, found bool, err error)
	Delete(contentUUID string) (found bool, err error)
	Check() (err error)
	DecodeJSON(*json.Decoder) (thing interface{}, err error)
	Count() (int, error)
	Initialise() error
}

//holds the Neo4j-specific information
type service struct {
	conn            neoutils.NeoConnection
	platformVersion string
}

const (
	v1PlatformVersion         = "v1"
	v2PlatformVersion         = "v2"
	brightcovePlatformVersion = "brightcove"
)

//NewCypherAnnotationsService instantiate driver
func NewCypherAnnotationsService(cypherRunner neoutils.NeoConnection, platformVersion string) service {
	if platformVersion == "" {
		log.Fatalf("PlatformVersion was not specified!")
	}
	return service{cypherRunner, platformVersion}
}

// DecodeJSON decodes to a list of annotations, for ease of use this is a struct itself
func (s service) DecodeJSON(dec *json.Decoder) (interface{}, error) {
	a := annotations{}
	err := dec.Decode(&a)
	return a, err
}

func (s service) Read(contentUUID string) (thing interface{}, found bool, err error) {
	results := []annotation{}

	//TODO shouldn't return Provenances if none of the scores, agentRole or atTime are set
	statementTemplate := `
					MATCH (c:Thing{uuid:{contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing)
					WITH c, cc, rel, {id:cc.uuid,prefLabel:cc.prefLabel,types:labels(cc),predicate:type(rel)} as thing,
					collect(
						{scores:[
							{scoringSystem:'%s', value:rel.relevanceScore},
							{scoringSystem:'%s', value:rel.confidenceScore}],
						agentRole:rel.annotatedBy,
						atTime:rel.annotatedDate}) as provenances
					RETURN thing, provenances ORDER BY thing.id
									`
	statement := fmt.Sprintf(statementTemplate, relevanceScoringSystem, confidenceScoringSystem)

	query := &neoism.CypherQuery{
		Statement:  statement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion},
		Result:     &results,
	}
	err = s.conn.CypherBatch([]*neoism.CypherQuery{query})
	if err != nil {
		log.Errorf("Error looking up uuid %s with query %s from neoism: %+v", contentUUID, query.Statement, err)
		return annotations{}, false, fmt.Errorf("Error accessing Annotations datastore for uuid: %s", contentUUID)
	}
	log.Debugf("CypherResult Read Annotations for uuid: %s was: %+v", contentUUID, results)
	if (len(results)) == 0 {
		return annotations{}, false, nil
	}

	for idx := range results {
		mapToResponseFormat(&results[idx])
	}

	return annotations(results), true, nil
}

//Delete removes all the annotations for this content. Ignore the nodes on either end -
//may leave nodes that are only 'things' inserted by this writer: clean up
//as a result of this will need to happen externally if required
func (s service) Delete(contentUUID string) (bool, error) {

	var deleteStatement string

	switch {
	case s.platformVersion == v2PlatformVersion:
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel:MENTIONS{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	case s.platformVersion == brightcovePlatformVersion:
		// TODO this clause should be refactored when all videos in Neo4j have brightcove only as lifecycle and no v1 reference
		deleteStatement = `	OPTIONAL MATCH (c:Thing{uuid: {contentUUID}})-[r]->(cc:Thing)
					WHERE r.lifecycle={lifecycle} OR r.lifecycle={v1Lifecycle}
					DELETE r`
	default:
		deleteStatement = `MATCH (c:Thing{uuid: {contentUUID}})-[rel{platformVersion:{platformVersion}}]->(cc:Thing) DELETE rel`
	}

	query := &neoism.CypherQuery{
		Statement: deleteStatement,
		Parameters: neoism.Props{"contentUUID": contentUUID, "platformVersion": s.platformVersion,
			"lifecycle": lifecycle(s.platformVersion), "v1Lifecycle": lifecycle(v1PlatformVersion)},
		IncludeStats: true,
	}

	err := s.conn.CypherBatch([]*neoism.CypherQuery{query})

	stats, err := query.Stats()
	if err != nil {
		return false, err
	}

	return stats.ContainsUpdates, err
}

//Write a set of annotations associated with a piece of content. Any annotations
//already there will be removed
func (s service) Write(contentUUID string, thing interface{}) (err error) {
	annotationsToWrite := thing.(annotations)

	if contentUUID == "" {
		return errors.New("Content uuid is required")
	}
	if err := validateAnnotations(&annotationsToWrite); err != nil {
		log.Warnf("Validation of supplied annotations failed")
		return err
	}

	if len(annotationsToWrite) == 0 {
		log.Warnf("No new annotations supplied for content uuid: %s", contentUUID)
	}

	queries := append([]*neoism.CypherQuery{}, dropAllAnnotationsQuery(contentUUID, s.platformVersion))

	var statements = []string{}
	for _, annotationToWrite := range annotationsToWrite {
		query, err := createAnnotationQuery(contentUUID, annotationToWrite, s.platformVersion)
		if err != nil {
			return err
		}
		statements = append(statements, query.Statement)
		queries = append(queries, query)
	}
	log.Infof("Updated Annotations for content uuid: %s", contentUUID)
	log.Debugf("For update, ran statements: %+v", statements)

	return s.conn.CypherBatch(queries)
}

// Check tests neo4j by running a simple cypher query
func (s service) Check() error {
	return neoutils.Check(s.conn)
}

func (s service) Count() (int, error) {
	results := []struct {
		Count int `json:"c"`
	}{}

	query := &neoism.CypherQuery{
		Statement: `MATCH ()-[r{platformVersion:{platformVersion}}]->()
                WHERE r.lifecycle = {lifecycle}
                OR r.lifecycle IS NULL
                RETURN count(r) as c`,
		Parameters: neoism.Props{"platformVersion": s.platformVersion, "lifecycle": lifecycle(s.platformVersion)},
		Result:     &results,
	}

	err := s.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}

func (s service) Initialise() error {
	return nil // No constraints need to be set up
}

func createAnnotationRelationship(relation string) (statement string) {
	stmt := `
                MERGE (content:Thing{uuid:{contentID}})
                MERGE (upp:Identifier:UPPIdentifier{value:{conceptID}})
                MERGE (upp)-[:IDENTIFIES]->(concept:Thing) ON CREATE SET concept.uuid = {conceptID}
                MERGE (content)-[pred:%s {platformVersion:{platformVersion}}]->(concept)
                SET pred={annProps}
          `
	statement = fmt.Sprintf(stmt, relation)
	return statement
}

func getRelationshipFromPredicate(predicate string) (relation string) {
	if predicate != "" {
		relation = relations[predicate]
	} else {
		relation = relations["mentions"]
	}
	return relation
}

func createAnnotationQuery(contentUUID string, ann annotation, platformVersion string) (*neoism.CypherQuery, error) {
	query := neoism.CypherQuery{}
	thingID, err := extractUUIDFromURI(ann.Thing.ID)
	if err != nil {
		return nil, err
	}

	//todo temporary change to deal with multiple provenances
	/*if len(ann.Provenances) > 1 {
		return nil, errors.New("Cannot insert a MENTIONS annotation with multiple provenances")
	}*/

	var prov provenance
	params := map[string]interface{}{}
	params["platformVersion"] = platformVersion
	params["lifecycle"] = lifecycle(platformVersion)

	if len(ann.Provenances) >= 1 {
		prov = ann.Provenances[0]
		annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, supplied, err := extractDataFromProvenance(&prov)

		if err != nil {
			log.Infof("ERROR=%s", err)
			return nil, err
		}

		if supplied == true {
			if annotatedBy != "" {
				params["annotatedBy"] = annotatedBy
			}
			if prov.AtTime != "" {
				params["annotatedDateEpoch"] = annotatedDateEpoch
				params["annotatedDate"] = prov.AtTime
			}
			params["relevanceScore"] = relevanceScore
			params["confidenceScore"] = confidenceScore
		}
	}

	relation := getRelationshipFromPredicate(ann.Thing.Predicate)
	query.Statement = createAnnotationRelationship(relation)
	query.Parameters = map[string]interface{}{
		"contentID":       contentUUID,
		"conceptID":       thingID,
		"platformVersion": platformVersion,
		"annProps":        params,
	}
	return &query, nil
}

func extractDataFromProvenance(prov *provenance) (string, int64, float64, float64, bool, error) {
	if len(prov.Scores) == 0 {
		return "", -1, -1, -1, false, nil
	}
	var annotatedBy string
	var annotatedDateEpoch int64
	var confidenceScore, relevanceScore float64
	var err error
	if prov.AgentRole != "" {
		annotatedBy, err = extractUUIDFromURI(prov.AgentRole)
	}
	if prov.AtTime != "" {
		annotatedDateEpoch, err = convertAnnotatedDateToEpoch(prov.AtTime)
	}
	relevanceScore, confidenceScore, err = extractScores(prov.Scores)

	if err != nil {
		return "", -1, -1, -1, true, err
	}
	return annotatedBy, annotatedDateEpoch, relevanceScore, confidenceScore, true, nil
}

func extractUUIDFromURI(uri string) (string, error) {
	result := uuidExtractRegex.FindStringSubmatch(uri)
	if len(result) == 2 {
		return result[1], nil
	}
	return "", fmt.Errorf("Couldn't extract uuid from uri %s", uri)
}

func convertAnnotatedDateToEpoch(annotatedDateString string) (int64, error) {
	datetimeEpoch, err := time.Parse(time.RFC3339, annotatedDateString)

	if err != nil {
		return 0, err
	}

	return datetimeEpoch.Unix(), nil
}

func extractScores(scores []score) (float64, float64, error) {
	var relevanceScore, confidenceScore float64
	for _, score := range scores {
		scoringSystem := score.ScoringSystem
		value := score.Value
		switch scoringSystem {
		case relevanceScoringSystem:
			relevanceScore = value
		case confidenceScoringSystem:
			confidenceScore = value
		}
	}
	return relevanceScore, confidenceScore, nil
}

func dropAllAnnotationsQuery(contentUUID string, platformVersion string) *neoism.CypherQuery {

	var matchStmtTemplate string

	//TODO hard-coded verification:
	//WE STILL NEED THIS UNTIL EVERYTHNG HAS A LIFECYCLE PROPERTY!
	// -> necessary for brands - which got written by content-api with isClassifiedBy relationship, and should not be deleted by annotations-rw
	// -> so far brands are the only v2 concepts which have isClassifiedBy relationship; as soon as this changes: implementation needs to be updated
	switch {
	case platformVersion == v2PlatformVersion:
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r:MENTIONS{platformVersion:{platformVersion}}]->(t:Thing)
                         		DELETE r`
	case platformVersion == brightcovePlatformVersion:
		// TODO this clause should be refactored when all videos in Neo4j have brightcove only as lifecycle and no v1 reference
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
					WHERE r.lifecycle={lifecycle} OR r.lifecycle={v1Lifecycle}
					DELETE r`
	default:
		matchStmtTemplate = `	OPTIONAL MATCH (:Thing{uuid:{contentID}})-[r]->(t:Thing)
					WHERE r.platformVersion={platformVersion}
                         		DELETE r`
	}

	query := neoism.CypherQuery{}
	query.Statement = matchStmtTemplate
	query.Parameters = neoism.Props{"contentID": contentUUID, "platformVersion": platformVersion,
		"lifecycle": lifecycle(platformVersion), "v1Lifecycle": lifecycle(v1PlatformVersion)}
	return &query
}

func validateAnnotations(annotations *annotations) error {
	//TODO - for consistency, we should probably just not create the annotation?
	for _, annotation := range *annotations {
		if annotation.Thing.ID == "" {
			return ValidationError{fmt.Sprintf("Concept uuid missing for annotation %+v", annotation)}
		}
	}
	return nil
}

//ValidationError is thrown when the annotations are not valid because mandatory information is missing
type ValidationError struct {
	Msg string
}

func (v ValidationError) Error() string {
	return v.Msg
}

func mapToResponseFormat(ann *annotation) {
	ann.Thing.ID = mapper.IDURL(ann.Thing.ID)
	// We expect only ONE provenance - provenance value is considered valid even if the AgentRole is not specified. See: v1 - isClassifiedBy
	for idx := range ann.Provenances {
		if ann.Provenances[idx].AgentRole != "" {
			ann.Provenances[idx].AgentRole = mapper.IDURL(ann.Provenances[idx].AgentRole)
		}
	}
}

func lifecycle(platformVersion string) string {
	return "annotations-" + platformVersion
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

func floyd(start int) (int, int) {
	tortoise := f[start]
	hare := f[f[start]]

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[f[hare]]
	}

	mu := 0
	tortoise = start

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[hare]
		mu += 1
	}

	lam := 1
	hare = f[tortoise]

	for tortoise != hare {
		hare = f[hare]
		lam += 1
	}

	return mu, lam
}

func brent(start int) (int, int) {
	power := 1
	lam := 1

	tortoise := start
	hare := f[start]

	for tortoise != hare {
		if power == lam {
			tortoise = hare
			power *= 2
			lam = 0
		}

		hare = f[hare]
		lam += 1
	}

	mu := 0
	tortoise = start
	hare = start

	for i := 0; i < lam; i++ {
		hare = f[hare]
	}

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[hare]
		mu += 1
	}

	return mu, lam
}

// var f = [...]int{6, 6, 0, 1, 4, 3, 3, 4, 0}
var f = [...]int{1, 5, 5, 2, 5, 9, 16, 16, 11, 6, 6, 19, 1, 12, 6, 0, 13, 8, 7, 16}

func main() {
	for i := 0; i < len(f); i++ {
		position, length := floyd(i)
		fmt.Println("floyd", "search start", i, "cycle start position", position, "cycle length", length)
		position, length = brent(i)
		fmt.Println("brent", "search start", i, "cycle start position", position, "cycle length", length)
	}
}
<commit_msg>cls goodness<commit_after>package main

import (
	"log"
	"os"
	"strconv"
	"sir"
)

func floyd(start int) (int, int) {
	tortoise := f[start]
	hare := f[f[start]]

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[f[hare]]
	}

	mu := 0
	tortoise = start

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[hare]
		mu += 1
	}

	lam := 1
	hare = f[tortoise]

	for tortoise != hare {
		hare = f[hare]
		lam += 1
	}

	return mu, lam
}

func brent(start int) (int, int) {
	power := 1
	lam := 1

	tortoise := start
	hare := f[start]

	for tortoise != hare {
		if power == lam {
			tortoise = hare
			power *= 2
			lam = 0
		}

		hare = f[hare]
		lam += 1
	}

	mu := 0
	tortoise = start
	hare = start

	for i := 0; i < lam; i++ {
		hare = f[hare]
	}

	for tortoise != hare {
		tortoise = f[tortoise]
		hare = f[hare]
		mu += 1
	}

	return mu, lam
}

var f []int

func main() {
	if len(os.Args) < 1 {
		log.Fatal("not enough args")
	}

	max := 0

	for i := 1; i < len(os.Args); i++ {
		arg, err := strconv.Atoi(os.Args[i])

		sir.CheckError(err)

		if arg > max {
			max = arg
		}
	}

	if len(os.Args[1:]) < max {
		log.Fatal(max, " points to a location not in array")
	}

	log.Println(max)

	f = make([]int, 0)

	for i := 1; i < len(os.Args); i++ {
		arg, err := strconv.Atoi(os.Args[i])

		sir.CheckError(err)

		f = append(f, arg)
	}

	log.Println("\t", "[start pos]", "[cycle start pos]", "[cycle len]")

	for i := 0; i < len(f); i++ {
		fpos, flen := floyd(i)
		log.Println("floyd", i, "\t", fpos, "\t", flen)

		bpos, blen := brent(i)
		log.Println("brent", i, "\t", bpos, "\t", blen)

		if fpos != bpos || flen != blen {
			log.Fatal("[error]", "floyd and brent do not agree!")
		}
	}

	log.Println(f)
}
<|endoftext|>


<commit_before>package quiclatest

import (
	"bytes"
	"net"

	"github.com/ami-GS/gQUIC/latest/qtype"
)

type Client struct {
	*BasePacketHandler

	remoteAddr        net.Addr
	session           *Session
	versionOffer      qtype.Version
	versionNegotiated bool
}

func (c *Client) Ping() {
	c.session.ping()
}

func (s *Client) Send(data []byte) (int, error) {
	return s.session.Write(data)
}

func (s *Client) Close() {
	s.close(nil)
}

func (s *Client) close(f *ConnectionCloseFrame) {
	if f == nil {
		f = NewConnectionCloseFrame(0, qtype.NoError, "Close request from client")
	}
	s.session.Close(f)
}

func DialAddr(addr string) (*Client, error) {
	remoteAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
	udpConn, err := net.ListenUDP("udp", srcAddr)

	srcConnID, err := qtype.NewConnectionID(nil)
	if err != nil {
		return nil, err
	}
	// have same ID until getting Retry Packet
	destConnID := srcConnID

	cli := &Client{
		remoteAddr:        remoteAddr,
		session:           NewSession(&Connection{conn: udpConn, remoteAddr: remoteAddr}, srcConnID, destConnID, true),
		versionOffer:      qtype.VersionUnsupportedTest,
		versionNegotiated: false,
	}
	cli.session.packetHandler = cli
	go cli.run()
	cli.Connect()

	return cli, nil
}

func (c *Client) readTokenInfo() (*qtype.TokenInfo, bool) {
	return nil, false
}

func (c *Client) ReadUsableToken() []byte {
	// TODO: not fully implemented
	// 1. whether client has token? true -> 2.
	if tknInfo, exists := c.readTokenInfo(); exists {
		// 2. check local IP and network interface, is it different from that of used last time? true -> return last token.
		localAddr := c.session.conn.conn.LocalAddr().(*net.UDPAddr)
		if tknInfo.Addr != localAddr.String() {
			return nil
		}

		ifaces, err := net.Interfaces()
		if err != nil {
			panic(err)
		}
		for _, iface := range ifaces {
			addrs, err := iface.Addrs()
			if err != nil {
				panic(err)
			}

			for _, addr := range addrs {
				switch v := addr.(type) {
				case *net.IPNet:
					continue
				case *net.IPAddr:
					if localAddr.String() == v.IP.String() {
						if tknInfo.Iface == iface.Name {
							return tknInfo.Raw
						}
					}
				}
			}
		}

	}
	// For now, return nil
	return nil
}

func (c *Client) Connect() {
	// version negotiation
	// wait response

	token := c.ReadUsableToken()

	// first initial packet
	destID, _ := qtype.NewConnectionID(nil)
	c.session.sendPacketChan <- //NewCoalescingPacket([]Packet{
	NewInitialPacket(c.versionOffer, destID, destID, token, c.session.LastPacketNumber,
		[]Frame{NewCryptoFrame(0, []byte("first cryptographic handshake message (ClientHello)"))})
	//NewProtectedPacket0RTT(c.versionOffer, destID, destID, c.session.LastPacketNumber, []Frame{NewStreamFrame(0, 0, true, true, false, []byte("0-RTT[0]: STREAM[0, ...]"))}),
	//})
	c.session.DestConnID = destID
	c.session.SrcConnID = destID

	//
}

func (c *Client) run() {
	go c.session.Run()
	buffer := make([]byte, qtype.MTUIPv4)
	for {
		length, _, err := c.session.conn.Read(buffer)
		if err != nil {
			panic(err)
		}
		packets, _, err := ParsePackets(buffer[:length])
		if err != nil {
			panic(err)
		}

		for _, p := range packets {
			err = c.handlePacket(p)
			if err != nil {
				panic(err)
			}
		}

	}
}

func (c *Client) handlePacket(p Packet) error {
	if _, ok := p.(*VersionNegotiationPacket); !ok && c.versionNegotiated == false {
		c.versionNegotiated = true
		c.session.versionDecided = c.versionOffer
	}

	return c.session.HandlePacket(p)
}

func (c *Client) handleVersionNegotiationPacket(packet *VersionNegotiationPacket) error {
	// TODO: shoulsd be written in session?
	if c.versionNegotiated {
		// Once a client receives a packet from the server which is not a Version Negotiation
		// packet, it MUST discard other Version Negotiation packets on the same connection.
		return nil
	}
	if !c.session.SrcConnID.Equal(packet.DestConnID) || !c.session.DestConnID.Equal(packet.SrcConnID) {
		// If this check fails, the packet MUST be discarded.
		return nil
	}

	// TODO: priority queue?
	found := false
	versionTBD := qtype.Version(0)
	for _, version := range packet.SupportedVersions {
		if version == c.versionOffer {
			// MUST ignore a Version Negotiation packet that lists the client's chosen version.
			return nil
		}

		if !found {
			for _, supportedVersion := range qtype.SupportedVersions {
				if version == supportedVersion {
					found = true
					versionTBD = version
				}
			}
		}
	}
	c.versionOffer = versionTBD
	// WIP

	c.session.sendPacketChan <- NewInitialPacket(c.versionOffer, c.session.DestConnID, c.session.SrcConnID, nil,
		c.session.LastPacketNumber,
		[]Frame{NewCryptoFrame(0, []byte("second cryptographic handshake message for answering VersionNegotiation Packet"))})
	return nil
}

func (c *Client) handleRetryPacket(packet *RetryPacket) error {
	// send second initial packet

	// The client retains the state of its cryptographic handshake, but discards all transport state.
	//c.session.DestConnID, _ = packet.GetHeader().GetConnectionIDPair()
	_, destConnID := packet.GetHeader().GetConnectionIDPair()
	if !bytes.Equal(destConnID, c.session.DestConnID) {
		/*
		   Clients MUST discard Retry packets that contain an Original
		   Destination Connection ID field that does not match the Destination
		   Connection ID from its Initial packet.  This prevents an off-path
		   attacker from injecting a Retry packet.
		*/
		// any error?
		panic("destConnID mismatch")
		return nil
	}
	c.session.SrcConnID, _ = qtype.NewConnectionID(nil)
	c.session.RetryTokenReceived = packet.RetryToken

	// try again with new transport, but MUST remember the results of any version negotiation that occurred
	pn := packet.GetPacketNumber()
	c.session.sendPacketChan <- NewInitialPacket(c.session.versionDecided, c.session.DestConnID, c.session.SrcConnID,
		packet.RetryToken, pn.Increase(),
		[]Frame{NewCryptoFrame(qtype.QuicInt(len("first cryptographic handshake message (ClientHello)")), []byte("second cryptographic handshake message"))})
	return nil
}

func (c *Client) handleHandshakePacket(packet *HandshakePacket) error {
	return nil
}

func (c *Client) handleInitialPacket(packet *InitialPacket) error {
	pn := packet.GetPacketNumber()
	if packet.TokenLen != 0 {
		c.session.sendPacketChan <- NewProtectedPacket0RTT(c.session.versionDecided, c.session.DestConnID, c.session.SrcConnID, pn.Increase(), []Frame{NewConnectionCloseFrame(0, qtype.ProtocolViolation, "client receives initial packet with Non-zero token length")})
	}
	return nil
}
<commit_msg>fix client side connection ID (in RetryPacket) management<commit_after>package quiclatest

import (
	"bytes"
	"net"

	"github.com/ami-GS/gQUIC/latest/qtype"
)

type Client struct {
	*BasePacketHandler

	remoteAddr        net.Addr
	session           *Session
	versionOffer      qtype.Version
	versionNegotiated bool
}

func (c *Client) Ping() {
	c.session.ping()
}

func (s *Client) Send(data []byte) (int, error) {
	return s.session.Write(data)
}

func (s *Client) Close() {
	s.close(nil)
}

func (s *Client) close(f *ConnectionCloseFrame) {
	if f == nil {
		f = NewConnectionCloseFrame(0, qtype.NoError, "Close request from client")
	}
	s.session.Close(f)
}

func DialAddr(addr string) (*Client, error) {
	remoteAddr, err := net.ResolveUDPAddr("udp", addr)
	if err != nil {
		return nil, err
	}
	srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0}
	udpConn, err := net.ListenUDP("udp", srcAddr)

	srcConnID, err := qtype.NewConnectionID(nil)
	if err != nil {
		return nil, err
	}
	// have same ID until getting Retry Packet
	destConnID := srcConnID

	cli := &Client{
		remoteAddr:        remoteAddr,
		session:           NewSession(&Connection{conn: udpConn, remoteAddr: remoteAddr}, srcConnID, destConnID, true),
		versionOffer:      qtype.VersionUnsupportedTest,
		versionNegotiated: false,
	}
	cli.session.packetHandler = cli
	go cli.run()
	cli.Connect()

	return cli, nil
}

func (c *Client) readTokenInfo() (*qtype.TokenInfo, bool) {
	return nil, false
}

func (c *Client) ReadUsableToken() []byte {
	// TODO: not fully implemented
	// 1. whether client has token? true -> 2.
	if tknInfo, exists := c.readTokenInfo(); exists {
		// 2. check local IP and network interface, is it different from that of used last time? true -> return last token.
		localAddr := c.session.conn.conn.LocalAddr().(*net.UDPAddr)
		if tknInfo.Addr != localAddr.String() {
			return nil
		}

		ifaces, err := net.Interfaces()
		if err != nil {
			panic(err)
		}
		for _, iface := range ifaces {
			addrs, err := iface.Addrs()
			if err != nil {
				panic(err)
			}

			for _, addr := range addrs {
				switch v := addr.(type) {
				case *net.IPNet:
					continue
				case *net.IPAddr:
					if localAddr.String() == v.IP.String() {
						if tknInfo.Iface == iface.Name {
							return tknInfo.Raw
						}
					}
				}
			}
		}

	}
	// For now, return nil
	return nil
}

func (c *Client) Connect() {
	// version negotiation
	// wait response

	token := c.ReadUsableToken()

	// first initial packet
	destID, _ := qtype.NewConnectionID(nil)
	c.session.sendPacketChan <- //NewCoalescingPacket([]Packet{
	NewInitialPacket(c.versionOffer, destID, destID, token, c.session.LastPacketNumber,
		[]Frame{NewCryptoFrame(0, []byte("first cryptographic handshake message (ClientHello)"))})
	//NewProtectedPacket0RTT(c.versionOffer, destID, destID, c.session.LastPacketNumber, []Frame{NewStreamFrame(0, 0, true, true, false, []byte("0-RTT[0]: STREAM[0, ...]"))}),
	//})
	c.session.DestConnID = destID
	c.session.SrcConnID = destID

	//
}

func (c *Client) run() {
	go c.session.Run()
	buffer := make([]byte, qtype.MTUIPv4)
	for {
		length, _, err := c.session.conn.Read(buffer)
		if err != nil {
			panic(err)
		}
		packets, _, err := ParsePackets(buffer[:length])
		if err != nil {
			panic(err)
		}

		for _, p := range packets {
			err = c.handlePacket(p)
			if err != nil {
				panic(err)
			}
		}

	}
}

func (c *Client) handlePacket(p Packet) error {
	if _, ok := p.(*VersionNegotiationPacket); !ok && c.versionNegotiated == false {
		c.versionNegotiated = true
		c.session.versionDecided = c.versionOffer
	}

	return c.session.HandlePacket(p)
}

func (c *Client) handleVersionNegotiationPacket(packet *VersionNegotiationPacket) error {
	// TODO: shoulsd be written in session?
	if c.versionNegotiated {
		// Once a client receives a packet from the server which is not a Version Negotiation
		// packet, it MUST discard other Version Negotiation packets on the same connection.
		return nil
	}
	if !c.session.SrcConnID.Equal(packet.DestConnID) || !c.session.DestConnID.Equal(packet.SrcConnID) {
		// If this check fails, the packet MUST be discarded.
		return nil
	}

	// TODO: priority queue?
	found := false
	versionTBD := qtype.Version(0)
	for _, version := range packet.SupportedVersions {
		if version == c.versionOffer {
			// MUST ignore a Version Negotiation packet that lists the client's chosen version.
			return nil
		}

		if !found {
			for _, supportedVersion := range qtype.SupportedVersions {
				if version == supportedVersion {
					found = true
					versionTBD = version
				}
			}
		}
	}
	c.versionOffer = versionTBD
	// WIP

	c.session.sendPacketChan <- NewInitialPacket(c.versionOffer, c.session.DestConnID, c.session.SrcConnID, nil,
		c.session.LastPacketNumber,
		[]Frame{NewCryptoFrame(0, []byte("second cryptographic handshake message for answering VersionNegotiation Packet"))})
	return nil
}

func (c *Client) handleRetryPacket(packet *RetryPacket) error {
	// send second initial packet

	// The client retains the state of its cryptographic handshake, but discards all transport state.
	//c.session.DestConnID, _ = packet.GetHeader().GetConnectionIDPair()
	rcvSrcConnID, _ := packet.GetHeader().GetConnectionIDPair()

	if !bytes.Equal(packet.OriginalDestConnID, c.session.DestConnID) {
		/*
			Clients MUST discard Retry packets that contain an Original
			Destination Connection ID field that does not match the Destination
			Connection ID from its Initial packet.
		*/
		panic("original DestinationID mismatch")
		return nil
	}

	c.session.DestConnID = rcvSrcConnID
	c.session.RetryTokenReceived = packet.RetryToken

	// try again with new transport, but MUST remember the results of any version negotiation that occurred
	pn := packet.GetPacketNumber()
	c.session.sendPacketChan <- NewInitialPacket(c.session.versionDecided, rcvSrcConnID, c.session.SrcConnID,
		packet.RetryToken, pn.Increase(),
		[]Frame{NewCryptoFrame(qtype.QuicInt(len("first cryptographic handshake message (ClientHello)")), []byte("second cryptographic handshake message"))})
	return nil
}

func (c *Client) handleHandshakePacket(packet *HandshakePacket) error {
	return nil
}

func (c *Client) handleInitialPacket(packet *InitialPacket) error {
	pn := packet.GetPacketNumber()
	if packet.TokenLen != 0 {
		c.session.sendPacketChan <- NewProtectedPacket0RTT(c.session.versionDecided, c.session.DestConnID, c.session.SrcConnID, pn.Increase(), []Frame{NewConnectionCloseFrame(0, qtype.ProtocolViolation, "client receives initial packet with Non-zero token length")})
	}
	return nil
}
<|endoftext|>


<commit_before>package bitset

import (
	"fmt"
	"github.com/hydroo/gomochex/basics/set"
)

type BitPosition int

func (b BitPosition) IsEqual(e set.Element) bool {
	if f, ok := e.(BitPosition); ok == true && b == f {
		return true
	} //else {
	return false
	//}
}

type BitSet []uint64

func NewBitSet() *BitSet {
	return &BitSet{}
}

func (S *BitSet) Add(elements ...BitPosition) {

	for _, e := range elements {
		b := int(e)
		if b >= len(*S)*64 {
			S.resize(BitPosition(b))
		}

		(*S)[b/64] = (*S)[b/64] | (1 << uint(b%64))
	}
}

func (S BitSet) At(index int) (BitPosition, bool) {

	for i, v := range S {
		for j, b := 0, uint64(1<<0); j < 64; j, b = j+1, b<<1 {
			if v&b != 0 {
				if index == 0 {
					return BitPosition(i*64 + j), true
				}
				index -= 1
			}
		}
	}

	return BitPosition(-1), false
}

func (S BitSet) Copy() set.Copier {
	cp := NewBitSet()
	cp.resize(BitPosition((len(S) * 64) - 1))
	copy(*cp, S)
	return cp
}

func (S BitSet) IsEqual(e set.Element) bool {
	T, ok := e.(BitSet)

	if ok == false {
		return false
	}

	if len(S) != len(T) {
		return false
	}

	for i := 0; i < len(S); i += 1 {
		if S[i] != T[i] {
			return false
		}
	}

	return true
}

func (S BitSet) New() set.Newer {
	return NewBitSet()
}

func (S BitSet) Probe(b BitPosition) bool {
	if int(b) >= len(S)*64 {
		return false
	}
	return (S[b/64] & (1 << uint(b%64))) != 0
}

func (S *BitSet) Remove(elements ...BitPosition) {

	for _, e := range elements {
		b := int(e)

		if b >= len(*S)*64 {
			continue
		}

		(*S)[b/64] = (*S)[b/64] & ^(1 << uint(b%64))
	}

	for i := len(*S) - 1; i >= 0; i -= 1 {
		if (*S)[i] != 0x0 {
			if i < len(*S)-1 {
				S.resize(BitPosition(i * 64))
			}
			return
		}
	}

	// the bitset is completely empty
	*S = *NewBitSet()
}

func (S *BitSet) resize(b BitPosition) {
	T := make(BitSet, int(b/64)+1)
	for k, v := range *S {
		if k >= len(T) {
			break
		}
		T[k] = v
	}
	*S = T
}

func (S BitSet) Size() int {

	size := 0

	for _, v := range S {
		for b := uint64(0x1); b != 0x0; b = b << 1 {
			if v&b != 0 {
				size += 1
			}
		}
	}

	return size
}

func (S BitSet) String() string {
	return fmt.Sprintf("%b", []uint64(S))
}
<commit_msg>make an own function for shrinking the array if possible<commit_after>package bitset

import (
	"fmt"
	"github.com/hydroo/gomochex/basics/set"
)

type BitPosition int

func (b BitPosition) IsEqual(e set.Element) bool {
	if f, ok := e.(BitPosition); ok == true && b == f {
		return true
	} //else {
	return false
	//}
}

type BitSet []uint64

func NewBitSet() *BitSet {
	return &BitSet{}
}

func (S *BitSet) Add(elements ...BitPosition) {

	for _, e := range elements {
		b := int(e)
		if b >= len(*S)*64 {
			S.resize(BitPosition(b))
		}

		(*S)[b/64] = (*S)[b/64] | (1 << uint(b%64))
	}
}

func (S BitSet) At(index int) (BitPosition, bool) {

	for i, v := range S {
		for j, b := 0, uint64(1<<0); j < 64; j, b = j+1, b<<1 {
			if v&b != 0 {
				if index == 0 {
					return BitPosition(i*64 + j), true
				}
				index -= 1
			}
		}
	}

	return BitPosition(-1), false
}

func (S BitSet) Copy() set.Copier {
	cp := NewBitSet()
	cp.resize(BitPosition((len(S) * 64) - 1))
	copy(*cp, S)
	return cp
}

func (S BitSet) IsEqual(e set.Element) bool {
	T, ok := e.(BitSet)

	if ok == false {
		return false
	}

	if len(S) != len(T) {
		return false
	}

	for i := 0; i < len(S); i += 1 {
		if S[i] != T[i] {
			return false
		}
	}

	return true
}

func (S BitSet) New() set.Newer {
	return NewBitSet()
}

func (S BitSet) Probe(b BitPosition) bool {
	if int(b) >= len(S)*64 {
		return false
	}
	return (S[b/64] & (1 << uint(b%64))) != 0
}

func (S *BitSet) Remove(elements ...BitPosition) {

	for _, e := range elements {
		b := int(e)

		if b >= len(*S)*64 {
			continue
		}

		(*S)[b/64] = (*S)[b/64] & ^(1 << uint(b%64))
	}

	for i := len(*S) - 1; i >= 0; i -= 1 {
		if (*S)[i] != 0x0 {
			if i < len(*S)-1 {
				S.resize(BitPosition(i * 64))
			}
			return
		}
	}

	S.resizeIfPossible()

	// the bitset is completely empty
	*S = *NewBitSet()
}

func (S *BitSet) resize(b BitPosition) {
	T := make(BitSet, int(b/64)+1)
	for k, v := range *S {
		if k >= len(T) {
			break
		}
		T[k] = v
	}
	*S = T
}

func (S *BitSet) resizeIfPossible() {
	for i := len(*S) - 1; i >= 0; i -= 1 {
		if (*S)[i] != 0x0 {
			if i < len(*S)-1 {
				S.resize(BitPosition(i * 64))
			}
			return
		}
	}
}

func (S BitSet) Size() int {

	size := 0

	for _, v := range S {
		for b := uint64(0x1); b != 0x0; b = b << 1 {
			if v&b != 0 {
				size += 1
			}
		}
	}

	return size
}

func (S BitSet) String() string {
	return fmt.Sprintf("%b", []uint64(S))
}
<|endoftext|>


<commit_before>package logging

import (
	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-smoke-tests/smoke"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("Loggregator:", func() {
	var testConfig = smoke.GetConfig()
	var useExistingApp = (testConfig.LoggingApp != "")
	var appName string

	Describe("cf logs", func() {
		AfterEach(func() {
			smoke.AppReport(appName, CF_TIMEOUT_IN_SECONDS)
			if testConfig.Cleanup && !useExistingApp {
				Expect(cf.Cf("delete", appName, "-f", "-r").Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
			}
		})

		Context("linux", func() {
			BeforeEach(func() {
				if !useExistingApp {
					appName = generator.PrefixedRandomName("SMOKES", "APP")
					Expect(cf.Cf("push", appName, "-b", "ruby_buildpack", "-p", SIMPLE_RUBY_APP_BITS_PATH, "-d", testConfig.AppsDomain, "--no-start").Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
					smoke.SetBackend(appName)
					Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
				}
			})

			It("can see app messages in the logs", func() {
				Eventually(func() *Session {
					appLogsSession := cf.Cf("logs", "--recent", appName)
					Expect(appLogsSession.Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
					return appLogsSession
				}, CF_TIMEOUT_IN_SECONDS*5).Should(Say(`\[(App|APP)/0\]`))
			})
		})

		Context("windows", func() {
			BeforeEach(func() {
				smoke.SkipIfWindows(testConfig)

				appName = generator.PrefixedRandomName("SMOKES", "APP")
				Expect(cf.Cf("push", appName, "-p", SIMPLE_DOTNET_APP_BITS_PATH, "-d", testConfig.AppsDomain, "-s", "windows2012R2", "-b", "binary_buildpack", "--no-start").Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
				smoke.EnableDiego(appName)
				Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
			})

			It("can see app messages in the logs", func() {
				Eventually(func() *Session {
					appLogsSession := cf.Cf("logs", "--recent", appName)
					Expect(appLogsSession.Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
					return appLogsSession
				}, CF_TIMEOUT_IN_SECONDS*5).Should(Say(`\[(App|APP)/0\]`))
			})
		})
	})
})
<commit_msg>Fix regex that no longer matches loggregator output<commit_after>package logging

import (
	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/generator"
	"github.com/cloudfoundry/cf-smoke-tests/smoke"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("Loggregator:", func() {
	var testConfig = smoke.GetConfig()
	var useExistingApp = (testConfig.LoggingApp != "")
	var appName string

	Describe("cf logs", func() {
		AfterEach(func() {
			smoke.AppReport(appName, CF_TIMEOUT_IN_SECONDS)
			if testConfig.Cleanup && !useExistingApp {
				Expect(cf.Cf("delete", appName, "-f", "-r").Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
			}
		})

		Context("linux", func() {
			BeforeEach(func() {
				if !useExistingApp {
					appName = generator.PrefixedRandomName("SMOKES", "APP")
					Expect(cf.Cf("push", appName, "-b", "ruby_buildpack", "-p", SIMPLE_RUBY_APP_BITS_PATH, "-d", testConfig.AppsDomain, "--no-start").Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
					smoke.SetBackend(appName)
					Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
				}
			})

			It("can see app messages in the logs", func() {
				Eventually(func() *Session {
					appLogsSession := cf.Cf("logs", "--recent", appName)
					Expect(appLogsSession.Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
					return appLogsSession
				}, CF_TIMEOUT_IN_SECONDS*5).Should(Say(`\[(App|APP).*/0\]`))
			})
		})

		Context("windows", func() {
			BeforeEach(func() {
				smoke.SkipIfWindows(testConfig)

				appName = generator.PrefixedRandomName("SMOKES", "APP")
				Expect(cf.Cf("push", appName, "-p", SIMPLE_DOTNET_APP_BITS_PATH, "-d", testConfig.AppsDomain, "-s", "windows2012R2", "-b", "binary_buildpack", "--no-start").Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
				smoke.EnableDiego(appName)
				Expect(cf.Cf("start", appName).Wait(CF_PUSH_TIMEOUT_IN_SECONDS)).To(Exit(0))
			})

			It("can see app messages in the logs", func() {
				Eventually(func() *Session {
					appLogsSession := cf.Cf("logs", "--recent", appName)
					Expect(appLogsSession.Wait(CF_TIMEOUT_IN_SECONDS)).To(Exit(0))
					return appLogsSession
				}, CF_TIMEOUT_IN_SECONDS*5).Should(Say(`\[(App|APP).*/0\]`))
			})
		})
	})
})
<|endoftext|>


<commit_before>package cachebench

import (
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"github.com/djinn/mace"
	"github.com/rif/cache2go"
	"gopkg.in/redis.v3"
)

var (
	logger = log.New(os.Stdout, "Mace:", log.LstdFlags)
)

func BenchmarkRedis(b *testing.B) {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	_, err := client.Ping().Result()
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		err := client.Set(key, key, 0).Err()
		if err != nil {
			panic(err)
		}
		err = client.Del(key).Err()
		if err != nil {
			panic(err)
		}
	}

}

func BenchmarkRedisWithExpiry(b *testing.B) {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	_, err := client.Ping().Result()
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		err := client.Set(key, key, 300*time.Millisecond).Err()
		if err != nil {
			panic(err)
		}
	}

}

func BenchmarkCache2Go(b *testing.B) {
	cache := cache2go.Cache("bench")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Cache(key, 0*time.Second, &key)
		cache.Delete(key)
	}
}

func BenchmarkCache2GoWithExpiry(b *testing.B) {
	cache := cache2go.Cache("benchExpiry")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Cache(key, 300*time.Second, &key)
	}
}

func BenchmarkMace(b *testing.B) {
	cache := mace.Mace("bench")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Set(key, &key, 0*time.Millisecond)
		cache.Delete(key)
	}
}

func BenchmarkMaceWithExpiry(b *testing.B) {
	cache := mace.Mace("benchExpiry")
	//cache.SetLogger(logger)
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Set(key, &key, 1*time.Millisecond)
	}
}
<commit_msg>fix caching flag in benchmark<commit_after>package cachebench

import (
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"github.com/djinn/mace"
	"github.com/rif/cache2go"
	"gopkg.in/redis.v3"
)

var (
	logger = log.New(os.Stdout, "Mace:", log.LstdFlags)
)

func BenchmarkRedis(b *testing.B) {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	_, err := client.Ping().Result()
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		err := client.Set(key, key, 0).Err()
		if err != nil {
			panic(err)
		}
		err = client.Del(key).Err()
		if err != nil {
			panic(err)
		}
	}

}

func BenchmarkRedisWithExpiry(b *testing.B) {
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	_, err := client.Ping().Result()
	if err != nil {
		panic(err)
	}
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		err := client.Set(key, key, 300*time.Millisecond).Err()
		if err != nil {
			panic(err)
		}
	}

}

func BenchmarkCache2Go(b *testing.B) {
	cache := cache2go.Cache("bench")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Cache(key, 0*time.Second, &key)
		cache.Delete(key)
	}
}

func BenchmarkCache2GoWithExpiry(b *testing.B) {
	cache := cache2go.Cache("benchExpiry")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Cache(key, 300*time.Second, &key)
	}
}

func BenchmarkMace(b *testing.B) {
	cache := mace.Mace("bench")
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Set(key, &key, 0*time.Millisecond)
		cache.Delete(key)
	}
}

func BenchmarkMaceWithExpiry(b *testing.B) {
	cache := mace.Mace("benchExpiry")
	//cache.SetLogger(logger)
	for i := 0; i < b.N; i++ {
		key := fmt.Sprintf("k%d", i)
		cache.Set(key, &key, 300*time.Millisecond)
	}
}
<|endoftext|>


<commit_before>package gocli

import (
	"fmt"
	"regexp"
	"strings"
)

func NewTable() *Table {
	return &Table{
		Columns:   [][]string{},
		Lengths:   map[int]int{},
		Separator: "\t",
	}
}

type Table struct {
	Columns   [][]string
	Lengths   map[int]int
	Separator string
}

func (t *Table) String() string {
	return strings.Join(t.Lines(), "\n")
}

var uncolorRegexp = regexp.MustCompile("\033\\[38;5;\\d+m([^\033]+)\033\\[0m")

func stringLength(s string) int {
	return len(uncolorRegexp.ReplaceAllString(s, "$1"))
}

func (t *Table) Lines() (lines []string) {
	for _, col := range t.Columns {
		cl := []string{}
		for i, v := range col {
			cl = append(cl, fmt.Sprintf("%-*s", t.Lengths[i], v))
		}
		lines = append(lines, strings.Join(cl, t.Separator))
	}
	return
}

func (t *Table) AddStrings(s []string) {
	var ret = make([]interface{}, len(s))

	for i, v := range s {
		ret[i] = v
	}
	t.Add(ret...)
}

// Add adds a column to the table
func (t *Table) Add(cols ...interface{}) {
	converted := make([]string, len(cols))
	for i, v := range cols {
		s := fmt.Sprint(v)
		converted[i] = s
		if t.Lengths[i] < stringLength(s) {
			t.Lengths[i] = len(s)
		}
	}
	t.Columns = append(t.Columns, converted)
}
<commit_msg>Use AddStrings to partially implement Add<commit_after>package gocli

import (
	"fmt"
	"regexp"
	"strings"
)

func NewTable() *Table {
	return &Table{
		Columns:   [][]string{},
		Lengths:   map[int]int{},
		Separator: "\t",
	}
}

type Table struct {
	Columns   [][]string
	Lengths   map[int]int
	Separator string
}

func (t *Table) String() string {
	return strings.Join(t.Lines(), "\n")
}

var uncolorRegexp = regexp.MustCompile("\033\\[38;5;\\d+m([^\033]+)\033\\[0m")

func stringLength(s string) int {
	return len(uncolorRegexp.ReplaceAllString(s, "$1"))
}

func (t *Table) Lines() (lines []string) {
	for _, col := range t.Columns {
		cl := []string{}
		for i, v := range col {
			cl = append(cl, fmt.Sprintf("%-*s", t.Lengths[i], v))
		}
		lines = append(lines, strings.Join(cl, t.Separator))
	}
	return
}

func (t *Table) AddStrings(list []string) {
	for i, s := range list {
		length := stringLength(s)
		if width := t.Lengths[i]; width < length {
			t.Lengths[i] = length
		}
	}
	t.Columns = append(t.Columns, list)
}

// Add adds a column to the table
func (t *Table) Add(cols ...interface{}) {
	converted := make([]string, 0, len(cols))
	for _, v := range cols {
		converted = append(converted, fmt.Sprint(v))
	}
	t.AddStrings(converted)
}
<|endoftext|>


<commit_before>// Package uitable provides a decorator for formating data as a table
package uitable

import (
	"fmt"
	"strings"
	"sync"

	"github.com/gosuri/uitable/util/strutil"
	"github.com/gosuri/uitable/util/wordwrap"
	"github.com/mattn/go-runewidth"
)

// Separator is the default column seperator
var Separator = "\t"

// Table represents a decorator that renders the data in formatted in a table
type Table struct {
	// Rows is the collection of rows in the table
	Rows []*Row

	// MaxColWidth is the maximum allowed width for cells in the table
	MaxColWidth uint

	// Wrap when set to true wraps the contents of the columns when the length exceeds the MaxColWidth
	Wrap bool

	// Separator is the seperator for columns in the table. Default is "\t"
	Separator string

	mtx *sync.RWMutex
}

// New returns a new Table with default values
func New() *Table {
	return &Table{
		Separator: Separator,
		mtx:       new(sync.RWMutex),
	}
}

// AddRow adds a new row to the table
func (t *Table) AddRow(data ...interface{}) *Table {
	t.mtx.Lock()
	defer t.mtx.Unlock()
	r := NewRow(data...)
	t.Rows = append(t.Rows, r)
	return t
}

// String returns the string value of table
func (t *Table) String() string {
	t.mtx.RLock()
	defer t.mtx.RUnlock()

	if len(t.Rows) == 0 {
		return ""
	}

	// determine the width for each column (cell in a row)
	var colwidths []uint
	for _, row := range t.Rows {
		for i, cell := range row.Cells {
			// resize colwidth array
			if i+1 > len(colwidths) {
				colwidths = append(colwidths, 0)
			}
			cellwidth := cell.LineWidth()
			if t.MaxColWidth != 0 && cellwidth > t.MaxColWidth {
				cellwidth = t.MaxColWidth
			}

			if cellwidth > colwidths[i] {
				colwidths[i] = cellwidth
			}
		}
	}

	var lines []string
	for _, row := range t.Rows {
		row.Separator = t.Separator
		for i, cell := range row.Cells {
			cell.Width = colwidths[i]
			cell.Wrap = t.Wrap
		}
		lines = append(lines, row.String())
	}
	return strutil.Join(lines, "\n")
}

// Row represents a row in a table
type Row struct {
	// Cells is the group of cell for the row
	Cells []*Cell

	// Separator for tabular columns
	Separator string
}

// NewRow returns a new Row and adds the data to the row
func NewRow(data ...interface{}) *Row {
	r := &Row{Cells: make([]*Cell, len(data))}
	for i, d := range data {
		r.Cells[i] = &Cell{Data: d}
	}
	return r
}

// String returns the string representation of the row
func (r *Row) String() string {
	// get the max number of lines for each cell
	var lc int // line count
	for _, cell := range r.Cells {
		if clc := len(strings.Split(cell.String(), "\n")); clc > lc {
			lc = clc
		}
	}

	// allocate a two-dimentional array of cells for each line and add size them
	cells := make([][]*Cell, lc)
	for x := 0; x < lc; x++ {
		cells[x] = make([]*Cell, len(r.Cells))
		for y := 0; y < len(r.Cells); y++ {
			cells[x][y] = &Cell{Width: r.Cells[y].Width}
		}
	}

	// insert each line in a cell as new cell in the cells array
	for y, cell := range r.Cells {
		lines := strings.Split(cell.String(), "\n")
		for x, line := range lines {
			cells[x][y].Data = line
		}
	}

	// format each line
	lines := make([]string, lc)
	for x := range lines {
		line := make([]string, len(cells[x]))
		for y := range cells[x] {
			line[y] = cells[x][y].String()
		}
		lines[x] = strutil.Join(line, r.Separator)
	}
	return strutil.Join(lines, "\n")
}

// Cell represents a column in a row
type Cell struct {
	// Width is the width of the cell
	Width uint

	// Wrap when true wraps the contents of the cell when the lenght exceeds the width
	Wrap bool

	// Data is the cell data
	Data interface{}
}

// LineWidth returns the max width of all the lines in a cell
func (c *Cell) LineWidth() uint {
	width := 0
	for _, s := range strings.Split(c.String(), "\n") {
		w := runewidth.StringWidth(s)
		if w > width {
			width = w
		}
	}
	return uint(width)
}

// String returns the string formated representation of the cell
func (c *Cell) String() string {
	if c.Data == nil {
		return strutil.PadLeft(" ", int(c.Width), ' ')
	}
	s := fmt.Sprintf("%v", c.Data)
	switch {
	case c.Width > 0 && c.Wrap:
		return wordwrap.WrapString(s, c.Width)
	case c.Width > 0:
		return strutil.Resize(s, c.Width)
	}
	return s
}
<commit_msg>uitable: add Table.Bytes to return bytes<commit_after>// Package uitable provides a decorator for formating data as a table
package uitable

import (
	"fmt"
	"strings"
	"sync"

	"github.com/gosuri/uitable/util/strutil"
	"github.com/gosuri/uitable/util/wordwrap"
	"github.com/mattn/go-runewidth"
)

// Separator is the default column seperator
var Separator = "\t"

// Table represents a decorator that renders the data in formatted in a table
type Table struct {
	// Rows is the collection of rows in the table
	Rows []*Row

	// MaxColWidth is the maximum allowed width for cells in the table
	MaxColWidth uint

	// Wrap when set to true wraps the contents of the columns when the length exceeds the MaxColWidth
	Wrap bool

	// Separator is the seperator for columns in the table. Default is "\t"
	Separator string

	mtx *sync.RWMutex
}

// New returns a new Table with default values
func New() *Table {
	return &Table{
		Separator: Separator,
		mtx:       new(sync.RWMutex),
	}
}

// AddRow adds a new row to the table
func (t *Table) AddRow(data ...interface{}) *Table {
	t.mtx.Lock()
	defer t.mtx.Unlock()
	r := NewRow(data...)
	t.Rows = append(t.Rows, r)
	return t
}

// Bytes returns the []byte value of table
func (t *Table) Bytes() []byte {
	return []byte(t.String())
}

// String returns the string value of table
func (t *Table) String() string {
	t.mtx.RLock()
	defer t.mtx.RUnlock()

	if len(t.Rows) == 0 {
		return ""
	}

	// determine the width for each column (cell in a row)
	var colwidths []uint
	for _, row := range t.Rows {
		for i, cell := range row.Cells {
			// resize colwidth array
			if i+1 > len(colwidths) {
				colwidths = append(colwidths, 0)
			}
			cellwidth := cell.LineWidth()
			if t.MaxColWidth != 0 && cellwidth > t.MaxColWidth {
				cellwidth = t.MaxColWidth
			}

			if cellwidth > colwidths[i] {
				colwidths[i] = cellwidth
			}
		}
	}

	var lines []string
	for _, row := range t.Rows {
		row.Separator = t.Separator
		for i, cell := range row.Cells {
			cell.Width = colwidths[i]
			cell.Wrap = t.Wrap
		}
		lines = append(lines, row.String())
	}
	return strutil.Join(lines, "\n")
}

// Row represents a row in a table
type Row struct {
	// Cells is the group of cell for the row
	Cells []*Cell

	// Separator for tabular columns
	Separator string
}

// NewRow returns a new Row and adds the data to the row
func NewRow(data ...interface{}) *Row {
	r := &Row{Cells: make([]*Cell, len(data))}
	for i, d := range data {
		r.Cells[i] = &Cell{Data: d}
	}
	return r
}

// String returns the string representation of the row
func (r *Row) String() string {
	// get the max number of lines for each cell
	var lc int // line count
	for _, cell := range r.Cells {
		if clc := len(strings.Split(cell.String(), "\n")); clc > lc {
			lc = clc
		}
	}

	// allocate a two-dimentional array of cells for each line and add size them
	cells := make([][]*Cell, lc)
	for x := 0; x < lc; x++ {
		cells[x] = make([]*Cell, len(r.Cells))
		for y := 0; y < len(r.Cells); y++ {
			cells[x][y] = &Cell{Width: r.Cells[y].Width}
		}
	}

	// insert each line in a cell as new cell in the cells array
	for y, cell := range r.Cells {
		lines := strings.Split(cell.String(), "\n")
		for x, line := range lines {
			cells[x][y].Data = line
		}
	}

	// format each line
	lines := make([]string, lc)
	for x := range lines {
		line := make([]string, len(cells[x]))
		for y := range cells[x] {
			line[y] = cells[x][y].String()
		}
		lines[x] = strutil.Join(line, r.Separator)
	}
	return strutil.Join(lines, "\n")
}

// Cell represents a column in a row
type Cell struct {
	// Width is the width of the cell
	Width uint

	// Wrap when true wraps the contents of the cell when the lenght exceeds the width
	Wrap bool

	// Data is the cell data
	Data interface{}
}

// LineWidth returns the max width of all the lines in a cell
func (c *Cell) LineWidth() uint {
	width := 0
	for _, s := range strings.Split(c.String(), "\n") {
		w := runewidth.StringWidth(s)
		if w > width {
			width = w
		}
	}
	return uint(width)
}

// String returns the string formated representation of the cell
func (c *Cell) String() string {
	if c.Data == nil {
		return strutil.PadLeft(" ", int(c.Width), ' ')
	}
	s := fmt.Sprintf("%v", c.Data)
	switch {
	case c.Width > 0 && c.Wrap:
		return wordwrap.WrapString(s, c.Width)
	case c.Width > 0:
		return strutil.Resize(s, c.Width)
	}
	return s
}
<|endoftext|>


<commit_before>package gocli

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

func NewTable() *Table {
	return &Table{
		Columns:   [][]string{},
		Lengths:   map[int]int{},
		Separator: "\t",
	}
}

type Table struct {
	Columns   [][]string
	Lengths   map[int]int
	Separator string

	SortBy int
}

func (t *Table) Select(message string) int {
	for {
		fmt.Fprintf(os.Stdout, t.StringWithIndex()+"\n"+message+": ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		i, e := strconv.Atoi(scanner.Text())
		if e == nil {
			if i > 0 && i <= len(t.Columns) {
				return i - 1
			}
		}
	}
}

func (t *Table) Len() int { return len(t.Columns) }

func (t *Table) Swap(a, b int) { t.Columns[a], t.Columns[b] = t.Columns[b], t.Columns[a] }

func (t *Table) Less(a, b int) bool {
	if len(t.Columns[a]) <= t.SortBy {
		return false
	} else if len(t.Columns[b]) <= t.SortBy {
		return true
	} else {
		return fmt.Sprint(t.Columns[a][t.SortBy]) <= fmt.Sprintf(t.Columns[b][t.SortBy])
	}
	return true
}

func (t *Table) String() string {
	return strings.Join(t.Lines(false), "\n")
}

func (t *Table) StringWithIndex() string {
	return strings.Join(t.Lines(true), "\n")
}

var uncolorRegexp = regexp.MustCompile("\033\\[38;5;\\d+m([^\033]+)\033\\[0m")

func stringLength(s string) int {
	return utf8.RuneCountInString((uncolorRegexp.ReplaceAllString(s, "$1")))
}

func (t *Table) Lines(printIndex bool) (lines []string) {
	for row, col := range t.Columns {
		cl := []string{}
		if printIndex {
			col = append([]string{strconv.Itoa(row + 1)}, col...)
		}
		for i, v := range col {
			theLen := t.Lengths[i]
			if printIndex {
				if i == 0 {
					theLen = intLength(len(t.Columns))
				} else {
					theLen = t.Lengths[i-1]
				}
			}
			pad := theLen - stringLength(v)
			cl = append(cl, v+strings.Repeat(" ", pad))
		}
		lines = append(lines, strings.Join(cl, t.Separator))
	}
	return
}

func intLength(i int) int {
	if i == 0 {
		return 1
	} else if i < 0 {
		return intLength(int(math.Abs(float64(i)))) + 1
	}
	return int(math.Ceil(math.Log10(float64(i + 1))))
}

func (t *Table) AddStrings(list []string) {
	for i, s := range list {
		length := stringLength(s)
		if width := t.Lengths[i]; width < length {
			t.Lengths[i] = length
		}
	}
	t.Columns = append(t.Columns, list)
}

// Add adds a column to the table
func (t *Table) Add(cols ...interface{}) {
	converted := make([]string, 0, len(cols))
	for _, v := range cols {
		converted = append(converted, fmt.Sprint(v))
	}
	t.AddStrings(converted)
}
<commit_msg>adds AddP to dereference pointer values as struct fields<commit_after>package gocli

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"unicode/utf8"
)

func NewTable() *Table {
	return &Table{
		Columns:   [][]string{},
		Lengths:   map[int]int{},
		Separator: "\t",
	}
}

type Table struct {
	Columns   [][]string
	Lengths   map[int]int
	Separator string

	SortBy int
}

func (t *Table) Select(message string) int {
	for {
		fmt.Fprintf(os.Stdout, t.StringWithIndex()+"\n"+message+": ")
		scanner := bufio.NewScanner(os.Stdin)
		scanner.Scan()
		i, e := strconv.Atoi(scanner.Text())
		if e == nil {
			if i > 0 && i <= len(t.Columns) {
				return i - 1
			}
		}
	}
}

func (t *Table) Len() int { return len(t.Columns) }

func (t *Table) Swap(a, b int) { t.Columns[a], t.Columns[b] = t.Columns[b], t.Columns[a] }

func (t *Table) Less(a, b int) bool {
	if len(t.Columns[a]) <= t.SortBy {
		return false
	} else if len(t.Columns[b]) <= t.SortBy {
		return true
	} else {
		return fmt.Sprint(t.Columns[a][t.SortBy]) <= fmt.Sprintf(t.Columns[b][t.SortBy])
	}
	return true
}

func (t *Table) String() string {
	return strings.Join(t.Lines(false), "\n")
}

func (t *Table) StringWithIndex() string {
	return strings.Join(t.Lines(true), "\n")
}

var uncolorRegexp = regexp.MustCompile("\033\\[38;5;\\d+m([^\033]+)\033\\[0m")

func stringLength(s string) int {
	return utf8.RuneCountInString((uncolorRegexp.ReplaceAllString(s, "$1")))
}

func (t *Table) Lines(printIndex bool) (lines []string) {
	for row, col := range t.Columns {
		cl := []string{}
		if printIndex {
			col = append([]string{strconv.Itoa(row + 1)}, col...)
		}
		for i, v := range col {
			theLen := t.Lengths[i]
			if printIndex {
				if i == 0 {
					theLen = intLength(len(t.Columns))
				} else {
					theLen = t.Lengths[i-1]
				}
			}
			pad := theLen - stringLength(v)
			cl = append(cl, v+strings.Repeat(" ", pad))
		}
		lines = append(lines, strings.Join(cl, t.Separator))
	}
	return
}

func intLength(i int) int {
	if i == 0 {
		return 1
	} else if i < 0 {
		return intLength(int(math.Abs(float64(i)))) + 1
	}
	return int(math.Ceil(math.Log10(float64(i + 1))))
}

func (t *Table) AddStrings(list []string) {
	for i, s := range list {
		length := stringLength(s)
		if width := t.Lengths[i]; width < length {
			t.Lengths[i] = length
		}
	}
	t.Columns = append(t.Columns, list)
}

// Add adds a column to the table
func (t *Table) Add(cols ...interface{}) {
	converted := make([]string, 0, len(cols))
	for _, v := range cols {
		converted = append(converted, fmt.Sprint(v))
	}
	t.AddStrings(converted)
}

// Dereferencing pointers if not nil
// TODO: Please someone tell me, how to do this right!
func (t *Table) AddP(cols ...interface{}) {
	converted := make([]string, 0, len(cols))
	var str string
	for _, v := range cols {
		if value := reflect.ValueOf(v); value.Kind() == reflect.Ptr {
			indirect := reflect.Indirect(value)
			switch {
			case indirect != reflect.Zero(value.Type()) && indirect.IsValid() == true:
				switch {
				case indirect.Kind() == reflect.String:
					str = fmt.Sprint(indirect.String())
				case indirect.Kind() == reflect.Int:
					str = fmt.Sprint(indirect.Int())
				case indirect.Kind() == reflect.Float32:
					str = fmt.Sprint(indirect.Float())
				case indirect.Kind() == reflect.Bool:
					str = fmt.Sprint(indirect.Bool())
				}
			default:
				str = ""
			}
		} else {
			str = fmt.Sprint(v)
		}
		converted = append(converted, str)
	}
	t.AddStrings(converted)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"fmt"
	"time"
	"strconv"
	"database/sql"
)

type Task struct {
	/*CREATE TABLE tasks (
	id serial PRIMARY KEY,
	description text NOT NULL,
	duration integer NOT NULL,
	user_id integer NOT NULL);*/
	ID string
	//Project string
	Description string
	Duration time.Duration
	//Date time.Time
	//Creation time.Time
}
func (t *Task) Save(db *sql.DB) error {
	_, err := db.Exec("INSERT INTO tasks (description, duration, user_id) VALUES ($1, $2, 1)",
							t.Description, t.Duration)
	if err != nil {
		log.Fatal(err)
	}
	return nil
}
func (t *Task) Update() error {
	return nil
}
func (t *Task) Remove() error {
	return nil
}
func LoadTask(db *sql.DB, task_id string) (*Task, error) {
	var id, dur int
	var desc string
	row := db.QueryRow("SELECT id, description, duration FROM tasks WHERE id=$1", task_id)
	err := row.Scan(&id, &desc, &dur)
	if err != nil {
		log.Fatal(err)
	}
	task := &Task{ID: strconv.Itoa(id), Description: desc, Duration: time.Duration(dur)}

	return task, nil
}

func LoadAccountTasks(db *sql.DB, account_id string) ([]*Task, error) {

	// SELECT * FROM tasks WHERE account=$1 LIMIT 50 ORDER_BT date ASC
	rows, err := db.Query("SELECT id, description, duration FROM tasks")
	if err != nil {
		log.Fatal(err)
	}

	var tasks []*Task
	for rows.Next() {
		var id, dur int
		var desc string

		if err := rows.Scan(&id, &desc, &dur); err != nil {
			fmt.Println(err)
		}

	    //dur, _ := time.ParseDuration("10s")
		task := &Task{ID: strconv.Itoa(id), Description: desc, Duration: time.Duration(dur)}
		tasks = append(tasks, task)
	}
	return tasks, nil
}
<commit_msg>store seconds in postgres instead of ns<commit_after>package main

import (
	"log"
	//"fmt"
	"time"
	"strconv"
	"database/sql"
)

type Task struct {
	/*CREATE TABLE tasks (
	id serial PRIMARY KEY,
	description text NOT NULL,
	duration integer NOT NULL,
	user_id integer NOT NULL);*/
	ID string
	//Project string
	Description string
	Duration time.Duration
	//Date time.Time
	//Creation time.Time
}
func (t *Task) Save(db *sql.DB) error {
	_, err := db.Exec("INSERT INTO tasks (description, duration, user_id) VALUES ($1, $2, 1)",
							t.Description, DurationToDB(t.Duration))
	if err != nil {
		log.Fatal(err)
	}
	return nil
}
func (t *Task) Update() error {
	return nil
}
func (t *Task) Remove() error {
	return nil
}
func LoadTask(db *sql.DB, task_id string) (*Task, error) {
	var id, dur int
	var desc string
	row := db.QueryRow("SELECT id, description, duration FROM tasks WHERE id=$1", task_id)
	err := row.Scan(&id, &desc, &dur)
	if err != nil {
		log.Fatal(err)
	}
	task := &Task{ID: strconv.Itoa(id), Description: desc, Duration: DurationFromDB(dur)}

	return task, nil
}

func LoadAccountTasks(db *sql.DB, account_id string) ([]*Task, error) {

	// SELECT * FROM tasks WHERE account=$1 LIMIT 50 ORDER_BT date ASC
	rows, err := db.Query("SELECT id, description, duration FROM tasks")
	if err != nil {
		log.Fatal(err)
	}

	var tasks []*Task
	for rows.Next() {
		var id, dur int
		var desc string

		if err := rows.Scan(&id, &desc, &dur); err != nil {
			log.Fatal(err)
		}

	    //dur, _ := time.ParseDuration("10s")
		task := &Task{ID: strconv.Itoa(id), Description: desc, Duration: DurationFromDB(dur)}
		tasks = append(tasks, task)
	}
	return tasks, nil
}

func DurationFromDB(s int) time.Duration {
	return time.Duration(int64(s) * int64(time.Second))
}

func DurationToDB(d time.Duration) int {
	return int(d / time.Second)
}
<|endoftext|>


<commit_before>package box

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

type TaskCollection struct {
	TotalCount int     `json:"total_count"`
	Entries    []*Task `json:"entries"`
}

// Documentation: https://developers.box.com/docs/#tasks-task-object
// TODO(ttacon): add missing fields
type Task struct {
	Type                     string                    `json:"type"`
	Id                       string                    `json:"id"`
	Item                     *Item                     `json:"item"`
	DueAt                    *string                   `json:"due_at"`     // TODO(ttacon): time.Time
	CreatedAt                *string                   `json:"created_at"` // TODO(ttacon): time.Time
	CreatedBy                *Item                     `json:"created_by"` // TODO(ttacon): change to user
	Action                   *string                   `json:"action"`     //TODO(ttacon): validation as this must be 'review'?
	Message                  *string                   `json:"message"`
	IsCompleted              *bool                     `json:"is_completed"`
	TaskAssignmentCollection *TaskAssignmentCollection `json:"task_assignment_collection"`
}

// Documentation: https://developers.box.com/docs/#tasks-create-a-task
func (c *Client) CreateTask(itemId, itemType, action, message, due_at string) (*http.Response, *Task, error) {
	var dataMap = map[string]interface{}{
		"item": map[string]string{
			"id":   itemId,
			"type": itemType,
		},
	}
	if len(action) > 0 {
		// TODO(ttacon): make sure this is "review"
		dataMap["action"] = action
	}
	if len(message) > 0 {
		dataMap["message"] = message
	}
	if len(due_at) > 0 {
		dataMap["due_at"] = due_at
	}

	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("%s/tasks", BASE_URL),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-get-a-task
func (c *Client) GetTask(taskId string) (*http.Response, *Task, error) {
	req, err := http.NewRequest(
		"GET",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-update-a-task
func (c *Client) UpdateTask(taskId, action, message, due_at string) (*http.Response, *Task, error) {
	var dataMap = make(map[string]interface{})
	if len(action) > 0 {
		dataMap["action"] = action
	}
	if len(message) > 0 {
		dataMap["message"] = message
	}
	if len(due_at) > 0 {
		dataMap["due_at"] = due_at
	}

	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"PUT",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-delete-a-task
func (c *Client) DeleteTask(taskId string) (*http.Response, error) {
	req, err := http.NewRequest(
		"DELETE",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	return c.Trans.Client().Do(req)
}
<commit_msg>Add TaskAssignment and corresponding Collection<commit_after>package box

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

type TaskCollection struct {
	TotalCount int     `json:"total_count"`
	Entries    []*Task `json:"entries"`
}

// Documentation: https://developers.box.com/docs/#tasks-task-object
// TODO(ttacon): add missing fields
type Task struct {
	Type                     string                    `json:"type"`
	Id                       string                    `json:"id"`
	Item                     *Item                     `json:"item"`
	DueAt                    *string                   `json:"due_at"`     // TODO(ttacon): time.Time
	CreatedAt                *string                   `json:"created_at"` // TODO(ttacon): time.Time
	CreatedBy                *Item                     `json:"created_by"` // TODO(ttacon): change to user
	Action                   *string                   `json:"action"`     //TODO(ttacon): validation as this must be 'review'?
	Message                  *string                   `json:"message"`
	IsCompleted              *bool                     `json:"is_completed"`
	TaskAssignmentCollection *TaskAssignmentCollection `json:"task_assignment_collection"`
}

type TaskAssignmentCollection struct {
	TotalCount int               `json:"total_count"`
	Entries    []*TaskAssignment `json:"entries"`
}

// TODO(ttacon): find out where the deuce this is defined in their documentation?!?!?!
type TaskAssignment struct {
	Type       *string `json:"type"`
	Id         string  `json:"id"`
	Item       *Item   `json:"item"`
	AssignedTo *Item   `json:"assigned_to"` // TODO(ttacon): change to mini-user
}

// Documentation: https://developers.box.com/docs/#tasks-create-a-task
func (c *Client) CreateTask(itemId, itemType, action, message, due_at string) (*http.Response, *Task, error) {
	var dataMap = map[string]interface{}{
		"item": map[string]string{
			"id":   itemId,
			"type": itemType,
		},
	}
	if len(action) > 0 {
		// TODO(ttacon): make sure this is "review"
		dataMap["action"] = action
	}
	if len(message) > 0 {
		dataMap["message"] = message
	}
	if len(due_at) > 0 {
		dataMap["due_at"] = due_at
	}

	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"POST",
		fmt.Sprintf("%s/tasks", BASE_URL),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-get-a-task
func (c *Client) GetTask(taskId string) (*http.Response, *Task, error) {
	req, err := http.NewRequest(
		"GET",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-update-a-task
func (c *Client) UpdateTask(taskId, action, message, due_at string) (*http.Response, *Task, error) {
	var dataMap = make(map[string]interface{})
	if len(action) > 0 {
		dataMap["action"] = action
	}
	if len(message) > 0 {
		dataMap["message"] = message
	}
	if len(due_at) > 0 {
		dataMap["due_at"] = due_at
	}

	dataBytes, err := json.Marshal(dataMap)
	if err != nil {
		return nil, nil, err
	}

	req, err := http.NewRequest(
		"PUT",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		bytes.NewReader(dataBytes),
	)
	if err != nil {
		return nil, nil, err
	}

	resp, err := c.Trans.Client().Do(req)
	if err != nil {
		return resp, nil, err
	}

	var data Task
	err = json.NewDecoder(resp.Body).Decode(&data)
	return resp, &data, err
}

// Documentation: https://developers.box.com/docs/#tasks-delete-a-task
func (c *Client) DeleteTask(taskId string) (*http.Response, error) {
	req, err := http.NewRequest(
		"DELETE",
		fmt.Sprintf("%s/tasks/%s", BASE_URL, taskId),
		nil,
	)
	if err != nil {
		return nil, nil, err
	}

	return c.Trans.Client().Do(req)
}
<|endoftext|>


<commit_before>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package controllers

import (
	"math"
	"net/http"
	"strconv"

	"github.com/adampresley/GoHttpService"
	"github.com/adampresley/logging"
	"github.com/mailslurper/libmailslurper/model/mailitem"
	"github.com/mailslurper/libmailslurper/model/requests"
	"github.com/mailslurper/libmailslurper/model/response"
	"github.com/mailslurper/libmailslurper/model/search"
	"github.com/mailslurper/libmailslurper/storage"

	"github.com/gorilla/context"
	"github.com/gorilla/mux"
)

/*
DeleteMail is a request to delete mail items. This expects a body containing
a DeleteMailRequest object.

	DELETE: /mail
*/
func DeleteMail(writer http.ResponseWriter, request *http.Request) {
	var err error
	deleteRequest := &requests.DeleteMailRequest{}

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	if err = GoHttpService.ParseJsonBody(request, deleteRequest); err != nil {
		log.Errorf("Invalid delete mail request - %s", err.Error())
		GoHttpService.BadRequest(writer, "Invalid delete mail request")
		return
	}

	if !deleteRequest.PruneCode.IsValid() {
		log.Errorf("Attempt to use invalid prune code - %s", deleteRequest.PruneCode)
		GoHttpService.BadRequest(writer, "Invalid prune type")
		return
	}

	startDate := deleteRequest.PruneCode.ConvertToDate()

	if err = database.DeleteMailsAfterDate(startDate); err != nil {
		log.Errorf("Problem deleting mails - %s", err.Error())
		GoHttpService.Error(writer, "There was a problem deleting mails")
		return
	}

	log.Infof("Deleting mails, code %s - Start - %s", deleteRequest.PruneCode.String(), startDate)
	GoHttpService.Success(writer, "OK")
}

/*
GetMail returns a single mail item by ID.

	GET: /mail/{id}
*/
func GetMail(writer http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	var mailID string
	var mailItem mailitem.MailItem
	var err error
	var ok bool

	/*
	 * Validate incoming arguments
	 */
	if mailID, ok = vars["mailId"]; !ok {
		log.Error("Invalid mail ID passed to GetMail")
		GoHttpService.BadRequest(writer, "A valid mail ID is required")
		return
	}

	/*
	 * Retrieve the mail item
	 */
	if mailItem, err = database.GetMailByID(mailID); err != nil {
		log.Errorf("Problem getting mail item in GetMail - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail item")
		return
	}

	log.Infof("Mail item %s retrieved", mailID)

	result := &response.MailItemResponse{
		MailItem: mailItem,
	}

	GoHttpService.WriteJson(writer, result, 200)
}

/*
GetMailCollection returns a collection of mail items. This is constrianed
by a page number. A page of data contains 50 items.

	GET: /mails?pageNumber={pageNumber}
*/
func GetMailCollection(writer http.ResponseWriter, request *http.Request) {
	var err error
	var pageNumberString string
	var pageNumber int
	var mailCollection []mailitem.MailItem
	var totalRecordCount int

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	/*
	 * Validate incoming arguments. A page is currently 50 items, hard coded
	 */
	pageNumberString = request.URL.Query().Get("pageNumber")
	if pageNumberString == "" {
		pageNumber = 1
	} else {
		if pageNumber, err = strconv.Atoi(pageNumberString); err != nil {
			log.Error("Invalid page number passed to GetMailCollection")
			GoHttpService.BadRequest(writer, "A valid page number is required")
			return
		}
	}

	length := 50
	offset := (pageNumber - 1) * length

	/*
	 * Retrieve mail items
	 */
	mailSearch := &search.MailSearch{
		Message: request.URL.Query().Get("message"),
		Start:   request.URL.Query().Get("start"),
		End:     request.URL.Query().Get("end"),
		From:    request.URL.Query().Get("from"),
		To:      request.URL.Query().Get("to"),
	}

	if mailCollection, err = database.GetMailCollection(offset, length, mailSearch); err != nil {
		log.Errorf("Problem getting mail collection - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail collection")
		return
	}

	if totalRecordCount, err = database.GetMailCount(mailSearch); err != nil {
		log.Errorf("Problem getting record count in GetMailCollection - %s", err.Error())
		GoHttpService.Error(writer, "Error getting record count")
		return
	}

	totalPages := int(math.Ceil(float64(totalRecordCount / length)))
	if totalPages*length < totalRecordCount {
		totalPages++
	}

	log.Infof("Mail collection page %d retrieved", pageNumber)

	result := &response.MailCollectionResponse{
		MailItems:    mailCollection,
		TotalPages:   totalPages,
		TotalRecords: totalRecordCount,
	}

	GoHttpService.WriteJson(writer, result, 200)
}

/*
GetMailCount returns the number of mail items in storage.

	GET: /mailcount
*/
func GetMailCount(writer http.ResponseWriter, request *http.Request) {
	var err error
	var mailItemCount int

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	/*
	 * Get the count
	 */
	if mailItemCount, err = database.GetMailCount(&search.MailSearch{}); err != nil {
		log.Errorf("Problem getting mail item count in GetMailCount - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail count")
		return
	}

	log.Infof("Mail item count - %d", mailItemCount)

	result := response.MailCountResponse{
		MailCount: mailItemCount,
	}

	GoHttpService.WriteJson(writer, result, 200)
}
<commit_msg>Capital D<commit_after>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package controllers

import (
	"math"
	"net/http"
	"strconv"

	"github.com/adampresley/GoHttpService"
	"github.com/adampresley/logging"
	"github.com/mailslurper/libmailslurper/model/mailitem"
	"github.com/mailslurper/libmailslurper/model/requests"
	"github.com/mailslurper/libmailslurper/model/response"
	"github.com/mailslurper/libmailslurper/model/search"
	"github.com/mailslurper/libmailslurper/storage"

	"github.com/gorilla/context"
	"github.com/gorilla/mux"
)

/*
DeleteMail is a request to delete mail items. This expects a body containing
a DeleteMailRequest object.

	DELETE: /mail
*/
func DeleteMail(writer http.ResponseWriter, request *http.Request) {
	var err error
	deleteRequest := &requests.DeleteMailRequest{}

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	if err = GoHttpService.ParseJsonBody(request, deleteRequest); err != nil {
		log.Errorf("Invalid delete mail request - %s", err.Error())
		GoHttpService.BadRequest(writer, "Invalid delete mail request")
		return
	}

	if !deleteRequest.PruneCode.IsValid() {
		log.Errorf("Attempt to use invalid prune code - %s", deleteRequest.PruneCode)
		GoHttpService.BadRequest(writer, "Invalid prune type")
		return
	}

	startDate := deleteRequest.PruneCode.ConvertToDate()

	if err = database.DeleteMailsAfterDate(startDate); err != nil {
		log.Errorf("Problem deleting mails - %s", err.Error())
		GoHttpService.Error(writer, "There was a problem deleting mails")
		return
	}

	log.Infof("Deleting mails, code %s - Start - %s", deleteRequest.PruneCode.String(), startDate)
	GoHttpService.Success(writer, "OK")
}

/*
GetMail returns a single mail item by ID.

	GET: /mail/{id}
*/
func GetMail(writer http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	var mailID string
	var mailItem mailitem.MailItem
	var err error
	var ok bool

	/*
	 * Validate incoming arguments
	 */
	if mailID, ok = vars["mailID"]; !ok {
		log.Error("Invalid mail ID passed to GetMail")
		GoHttpService.BadRequest(writer, "A valid mail ID is required")
		return
	}

	/*
	 * Retrieve the mail item
	 */
	if mailItem, err = database.GetMailByID(mailID); err != nil {
		log.Errorf("Problem getting mail item in GetMail - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail item")
		return
	}

	log.Infof("Mail item %s retrieved", mailID)

	result := &response.MailItemResponse{
		MailItem: mailItem,
	}

	GoHttpService.WriteJson(writer, result, 200)
}

/*
GetMailCollection returns a collection of mail items. This is constrianed
by a page number. A page of data contains 50 items.

	GET: /mails?pageNumber={pageNumber}
*/
func GetMailCollection(writer http.ResponseWriter, request *http.Request) {
	var err error
	var pageNumberString string
	var pageNumber int
	var mailCollection []mailitem.MailItem
	var totalRecordCount int

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	/*
	 * Validate incoming arguments. A page is currently 50 items, hard coded
	 */
	pageNumberString = request.URL.Query().Get("pageNumber")
	if pageNumberString == "" {
		pageNumber = 1
	} else {
		if pageNumber, err = strconv.Atoi(pageNumberString); err != nil {
			log.Error("Invalid page number passed to GetMailCollection")
			GoHttpService.BadRequest(writer, "A valid page number is required")
			return
		}
	}

	length := 50
	offset := (pageNumber - 1) * length

	/*
	 * Retrieve mail items
	 */
	mailSearch := &search.MailSearch{
		Message: request.URL.Query().Get("message"),
		Start:   request.URL.Query().Get("start"),
		End:     request.URL.Query().Get("end"),
		From:    request.URL.Query().Get("from"),
		To:      request.URL.Query().Get("to"),
	}

	if mailCollection, err = database.GetMailCollection(offset, length, mailSearch); err != nil {
		log.Errorf("Problem getting mail collection - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail collection")
		return
	}

	if totalRecordCount, err = database.GetMailCount(mailSearch); err != nil {
		log.Errorf("Problem getting record count in GetMailCollection - %s", err.Error())
		GoHttpService.Error(writer, "Error getting record count")
		return
	}

	totalPages := int(math.Ceil(float64(totalRecordCount / length)))
	if totalPages*length < totalRecordCount {
		totalPages++
	}

	log.Infof("Mail collection page %d retrieved", pageNumber)

	result := &response.MailCollectionResponse{
		MailItems:    mailCollection,
		TotalPages:   totalPages,
		TotalRecords: totalRecordCount,
	}

	GoHttpService.WriteJson(writer, result, 200)
}

/*
GetMailCount returns the number of mail items in storage.

	GET: /mailcount
*/
func GetMailCount(writer http.ResponseWriter, request *http.Request) {
	var err error
	var mailItemCount int

	log := context.Get(request, "log").(*logging.Logger)
	database := context.Get(request, "database").(storage.IStorage)

	/*
	 * Get the count
	 */
	if mailItemCount, err = database.GetMailCount(&search.MailSearch{}); err != nil {
		log.Errorf("Problem getting mail item count in GetMailCount - %s", err.Error())
		GoHttpService.Error(writer, "Problem getting mail count")
		return
	}

	log.Infof("Mail item count - %d", mailItemCount)

	result := response.MailCountResponse{
		MailCount: mailItemCount,
	}

	GoHttpService.WriteJson(writer, result, 200)
}
<|endoftext|>