<commit_before>/*
Copyright 2019 The Jetstack cert-manager contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package images

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path"

	flag "github.com/spf13/pflag"

	"github.com/jetstack/cert-manager/hack/release/pkg/bazel"
	"github.com/jetstack/cert-manager/hack/release/pkg/flags"
	logf "github.com/jetstack/cert-manager/hack/release/pkg/log"
	"github.com/jetstack/cert-manager/hack/release/pkg/util"
)

var (
	Default = &Plugin{}

	supportedComponents = []string{"acmesolver", "controller", "webhook", "cainjector"}
	log                 = logf.Log.WithName("images")
)

type Plugin struct {
	// The list of images to build (e.g. acmesolver, controller, webhook)
	Components []string

	// If true, the built images will be exported to the configured docker
	// daemon when the Build() method is called.
	ExportToDocker bool

	// List of architectures to build images for
	GoArch []string

	// DockerConfig is a path to a directory containing a config.json file that
	// is used for Docker authentication
	DockerConfig string

	// TODO: add GOOS support once the build system supports more than linux

	// built is set to true if Build() has completed successfully
	built bool
	// configFileName is computed based on the DockerConfig field
	configFileName string
}

func (g *Plugin) AddFlags(fs *flag.FlagSet) {
	fs.BoolVar(&g.ExportToDocker, "images.export", false, "if true, images will be exported to the currently configured docker daemon")
	fs.StringSliceVar(&g.Components, "images.components", []string{"acmesolver", "controller", "webhook", "cainjector"}, "the list of components to build images for")
	fs.StringSliceVar(&g.GoArch, "images.goarch", []string{"amd64", "arm64", "arm"}, "list of architectures to build images for")
	fs.StringVar(&g.DockerConfig, "images.docker-config", "", "path to a directory containing a docker config.json file used when pushing images")
}

func (g *Plugin) Validate() []error {
	var errs []error

	// validate components flag
	for _, a := range g.Components {
		valid := false
		for _, sa := range supportedComponents {
			if a == sa {
				valid = true
				break
			}
		}
		if !valid {
			errs = append(errs, fmt.Errorf("invalid component name %q", a))
		}
	}

	return errs
}

func (g *Plugin) InitPublish() []error {
	var errs []error

	if g.DockerConfig != "" {
		configFileName := path.Join(g.DockerConfig, "config.json")
		f, err := os.Stat(configFileName)
		if err != nil {
			return []error{fmt.Errorf("error checking config file: %v", err)}
		}
		if f.IsDir() {
			return []error{fmt.Errorf("docker config.json is not a file")}
		}
		g.configFileName = g.DockerConfig
	}

	return errs
}

func (g *Plugin) Build(ctx context.Context) error {
	_, err := g.build(ctx)
	if err != nil {
		return err
	}

	if g.ExportToDocker {
		log.Info("Exporting docker images to local docker daemon")
		if err := g.exportToDocker(ctx); err != nil {
			return err
		}
	} else {
		log.Info("skipping exporting docker images to docker daemon")
	}

	return nil
}

func (g *Plugin) Publish(ctx context.Context) error {
	log.Info("running publish for image plugin")
	// this case should never be reached, but we check it to be safe
	if !g.built {
		if _, err := g.build(ctx); err != nil {
			return err
		}
	}

	log.Info("pushing images")
	targets := g.generateTargets()
	err := g.pushImages(ctx, targets)
	if err != nil {
		return err
	}

	log.Info("published all docker images")

	return nil
}

func (g *Plugin) Complete() error {
	log = log.WithName("default-flags")

	if g.DockerConfig == "" {
		g.DockerConfig = os.Getenv("DOCKER_CONFIG")
		if g.DockerConfig != "" {
			log.Info("set default value", "flag", "images.docker-config", "value", g.DockerConfig)
		}
	}
	return nil
}

func (g *Plugin) build(ctx context.Context) (imageTargets, error) {
	targets := g.generateTargets()

	// only support building docker images for linux for now
	os := "linux"
	for _, arch := range g.GoArch {
		filteredTargets := targets.withOSArch(os, arch)
		bazelTargets := filteredTargets.bazelTargets()
		log := log.WithValues("images", bazelTargets)
		log.Info("building bazel image targets")

		err := bazel.Default.BuildPlatformE(ctx, log, os, arch, bazelTargets...)
		if err != nil {
			return nil, fmt.Errorf("error building docker images (%v): %v", targets, err)
		}
	}

	g.built = true
	return targets, nil
}

func (g *Plugin) exportToDocker(ctx context.Context) error {
	targets := g.generateTargets()
	log.WithValues("images", targets.bazelExportTargets()).Info("exporting images to docker daemon")
	for _, target := range targets {
		log := log.WithValues("target", target.name, "os", target.os, "arch", target.arch)
		log.Info("exporting image to docker daemon")
		exportTarget := target.bazelExportTarget()
		err := bazel.Default.RunPlatformE(ctx, log, target.os, target.arch, exportTarget)
		if err != nil {
			return fmt.Errorf("error exporting image %q to docker daemon: %v", target, err)
		}

		for _, taggedImage := range target.taggedImageNames() {
			log.Info("tagging image", "tag", taggedImage)
			cmd := exec.CommandContext(ctx, "docker", "tag", target.exportedImageName(), taggedImage)
			err := util.RunE(log, cmd)
			if err != nil {
				return err
			}
		}
	}

	log.WithValues("images", targets.taggedImageNames()).Info("exported all docker images")

	return nil
}

// generateTargets generates a list of Bazel target names that must be
// built for this invocation of the image builder
func (g *Plugin) generateTargets() imageTargets {
	var targets []imageTarget
	for _, c := range g.Components {
		for _, a := range g.GoArch {
			targets = append(targets, imageTarget{c, "linux", a})
		}
	}
	return targets
}

// pushImages will push the images built for this release to the registry
// TODO: add support for calling container_push targets instead of just 'docker push'
func (p *Plugin) pushImages(ctx context.Context, targets imageTargets) error {
	err := p.exportToDocker(ctx)
	if err != nil {
		return err
	}

	images := targets.taggedImageNames()

	log.WithValues("images", images).Info("pushing docker images")
	for _, img := range images {
		log := log.WithValues("image", img)
		log.Info("pushing docker image")
		args := []string{}
		if p.configFileName != "" {
			args = append(args, "--config", p.configFileName)
		}
		args = append(args, "push", img)
		cmd := exec.CommandContext(ctx, "docker", args...)
		err := util.RunE(log, cmd)
		if err != nil {
			return err
		}
	}

	return nil
}

type imageTargets []imageTarget

func (i imageTargets) bazelTargets() []string {
	out := make([]string, len(i))
	for idx, target := range i {
		out[idx] = target.bazelTarget()
	}
	return out
}

func (i imageTargets) bazelExportTargets() []string {
	out := make([]string, len(i))
	for idx, target := range i {
		out[idx] = target.bazelExportTarget()
	}
	return out
}

func (i imageTargets) taggedImageNames() []string {
	out := make([]string, 0)
	for _, target := range i {
		out = append(out, target.taggedImageNames()...)
	}
	return out
}

func (i imageTargets) exportedImageNames() []string {
	out := make([]string, 0)
	for _, target := range i {
		out = append(out, target.taggedImageNames()...)
	}
	return out
}

func (i imageTargets) withOSArch(os, arch string) imageTargets {
	out := make(imageTargets, 0)
	for _, target := range i {
		if target.os == os && target.arch == arch {
			out = append(out, target)
		}
	}
	return out
}

type imageTarget struct {
	name, os, arch string
}

func (i imageTarget) bazelTarget() string {
	return fmt.Sprintf("//cmd/%s:image", i.name)
}

func (i imageTarget) bazelExportTarget() string {
	return fmt.Sprintf("//cmd/%s:image.export", i.name)
}

func (i imageTarget) exportedImageName() string {
	return fmt.Sprintf("%s:%s", i.name, flags.Default.GitCommitRef)
}

func (i imageTarget) taggedImageNames() []string {
	if i.arch == "amd64" {
		return []string{
			fmt.Sprintf("%s/cert-manager-%s:%s", flags.Default.DockerRepo, i.name, flags.Default.AppVersion),
			fmt.Sprintf("%s/cert-manager-%s:%s", flags.Default.DockerRepo, i.name, flags.Default.GitCommitRef),
		}
	}

	return []string{
		fmt.Sprintf("%s/cert-manager-%s-%s:%s", flags.Default.DockerRepo, i.name, i.arch, flags.Default.AppVersion),
		fmt.Sprintf("%s/cert-manager-%s-%s:%s", flags.Default.DockerRepo, i.name, i.arch, flags.Default.GitCommitRef),
	}
}
<commit_msg>Create multi-arch manifest lists when publish docker images<commit_after>/*
Copyright 2019 The Jetstack cert-manager contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package images

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"path"

	flag "github.com/spf13/pflag"

	"github.com/jetstack/cert-manager/hack/release/pkg/bazel"
	"github.com/jetstack/cert-manager/hack/release/pkg/flags"
	logf "github.com/jetstack/cert-manager/hack/release/pkg/log"
	"github.com/jetstack/cert-manager/hack/release/pkg/util"
)

var (
	Default = &Plugin{}

	supportedComponents = []string{"acmesolver", "controller", "webhook", "cainjector"}
	log                 = logf.Log.WithName("images")
)

type Plugin struct {
	// The list of images to build (e.g. acmesolver, controller, webhook)
	Components []string

	// If true, the built images will be exported to the configured docker
	// daemon when the Build() method is called.
	ExportToDocker bool

	// List of architectures to build images for
	GoArch []string

	// DockerConfig is a path to a directory containing a config.json file that
	// is used for Docker authentication
	DockerConfig string

	// If true, a multi-arch manifest list will be created for each set of
	// images
	CreateManifestList bool

	// TODO: add GOOS support once the build system supports more than linux

	// built is set to true if Build() has completed successfully
	built bool
	// configFileName is computed based on the DockerConfig field
	configFileName string
}

func (g *Plugin) AddFlags(fs *flag.FlagSet) {
	fs.BoolVar(&g.ExportToDocker, "images.export", false, "if true, images will be exported to the currently configured docker daemon")
	fs.BoolVar(&g.CreateManifestList, "images.publish-manifest-list", true, "if true, a multi-arch manifest list will be published when images are pushed")
	fs.StringSliceVar(&g.Components, "images.components", []string{"acmesolver", "controller", "webhook", "cainjector"}, "the list of components to build images for")
	fs.StringSliceVar(&g.GoArch, "images.goarch", []string{"amd64", "arm64", "arm"}, "list of architectures to build images for")
	fs.StringVar(&g.DockerConfig, "images.docker-config", "", "path to a directory containing a docker config.json file used when pushing images")
}

func (g *Plugin) Validate() []error {
	var errs []error

	// validate components flag
	for _, a := range g.Components {
		valid := false
		for _, sa := range supportedComponents {
			if a == sa {
				valid = true
				break
			}
		}
		if !valid {
			errs = append(errs, fmt.Errorf("invalid component name %q", a))
		}
	}

	return errs
}

func (g *Plugin) InitPublish() []error {
	var errs []error

	if g.DockerConfig != "" {
		configFileName := path.Join(g.DockerConfig, "config.json")
		f, err := os.Stat(configFileName)
		if err != nil {
			return []error{fmt.Errorf("error checking config file: %v", err)}
		}
		if f.IsDir() {
			return []error{fmt.Errorf("docker config.json is not a file")}
		}
		g.configFileName = g.DockerConfig
	}

	return errs
}

func (g *Plugin) Build(ctx context.Context) error {
	_, err := g.build(ctx)
	if err != nil {
		return err
	}

	if g.ExportToDocker {
		log.Info("Exporting docker images to local docker daemon")
		if err := g.exportToDocker(ctx); err != nil {
			return err
		}
	} else {
		log.Info("skipping exporting docker images to docker daemon")
	}

	return nil
}

func (g *Plugin) Publish(ctx context.Context) error {
	log.Info("running publish for image plugin")
	// this case should never be reached, but we check it to be safe
	if !g.built {
		if _, err := g.build(ctx); err != nil {
			return err
		}
	}

	log.Info("pushing images")
	targets := g.generateTargets()
	err := g.pushImages(ctx, targets)
	if err != nil {
		return err
	}

	log.Info("published all docker images")

	return nil
}

func (g *Plugin) Complete() error {
	log = log.WithName("default-flags")

	if g.DockerConfig == "" {
		g.DockerConfig = os.Getenv("DOCKER_CONFIG")
		if g.DockerConfig != "" {
			log.Info("set default value", "flag", "images.docker-config", "value", g.DockerConfig)
		}
	}
	return nil
}

func (g *Plugin) build(ctx context.Context) (imageTargets, error) {
	targets := g.generateTargets()

	// only support building docker images for linux for now
	os := "linux"
	for _, arch := range g.GoArch {
		filteredTargets := targets.withOSArch(os, arch)
		bazelTargets := filteredTargets.bazelTargets()
		log := log.WithValues("images", bazelTargets)
		log.Info("building bazel image targets")

		err := bazel.Default.BuildPlatformE(ctx, log, os, arch, bazelTargets...)
		if err != nil {
			return nil, fmt.Errorf("error building docker images (%v): %v", targets, err)
		}
	}

	g.built = true
	return targets, nil
}

func (g *Plugin) exportToDocker(ctx context.Context) error {
	targets := g.generateTargets()
	log.WithValues("images", targets.bazelExportTargets()).Info("exporting images to docker daemon")
	for _, target := range targets {
		log := log.WithValues("target", target.name, "os", target.os, "arch", target.arch)
		log.Info("exporting image to docker daemon")
		exportTarget := target.bazelExportTarget()
		err := bazel.Default.RunPlatformE(ctx, log, target.os, target.arch, exportTarget)
		if err != nil {
			return fmt.Errorf("error exporting image %q to docker daemon: %v", target, err)
		}

		for _, taggedImage := range target.taggedImageNames() {
			log.Info("tagging image", "tag", taggedImage)
			cmd := exec.CommandContext(ctx, "docker", "tag", target.exportedImageName(), taggedImage)
			err := util.RunE(log, cmd)
			if err != nil {
				return err
			}
		}
	}

	log.WithValues("images", targets.taggedImageNames()).Info("exported all docker images")

	return nil
}

// generateTargets generates a list of Bazel target names that must be
// built for this invocation of the image builder
func (g *Plugin) generateTargets() imageTargets {
	var targets []imageTarget
	for _, c := range g.Components {
		for _, a := range g.GoArch {
			targets = append(targets, imageTarget{c, "linux", a})
		}
	}
	return targets
}

// pushImages will push the images built for this release to the registry
// TODO: add support for calling container_push targets instead of just 'docker push'
func (p *Plugin) pushImages(ctx context.Context, targets imageTargets) error {
	err := p.exportToDocker(ctx)
	if err != nil {
		return err
	}

	images := targets.taggedImageNames()

	log.WithValues("images", images).Info("pushing docker images")
	for _, img := range images {
		log := log.WithValues("image", img)
		log.Info("pushing docker image")
		args := []string{}
		if p.configFileName != "" {
			args = append(args, "--config", p.configFileName)
		}
		args = append(args, "push", img)
		cmd := exec.CommandContext(ctx, "docker", args...)
		err := util.RunE(log, cmd)
		if err != nil {
			return err
		}
	}

	manifestsToCreate := make(map[string][]imageTarget)
	for _, target := range targets {
		manifestsToCreate[target.name] = append(manifestsToCreate[target.name], target)
	}

	if p.CreateManifestList {
		for name, targets := range manifestsToCreate {
			log := log.WithValues("component", name)
			log.Info("creating manifest list for component")
			manifestListName := manifestListNameForComponent(name)
			manifestImages := make([]string, len(targets))
			for i, target := range targets {
				manifestImages[i] = target.taggedImageNames()[0]
			}
			log.Info("running manifest create", "manifest_name", manifestListName, "images", manifestImages)
			cmd := exec.CommandContext(ctx, "docker", append([]string{"manifest", "create", manifestListName}, manifestImages...)...)
			if err := util.RunE(log, cmd); err != nil {
				return fmt.Errorf("failed to create manifest list: %v", err)
			}
			for _, target := range targets {
				a := manifestListAnnotationsForTarget(target)
				cmd := exec.CommandContext(ctx, "docker", "manifest", "annotate", "--os", a.os, "--arch", a.arch, "--variant", a.variant, manifestListName, target.taggedImageNames()[0])
				if err := util.RunE(log, cmd); err != nil {
					return fmt.Errorf("failed to annotate manifest list: %v", err)
				}
			}

			cmd = exec.CommandContext(ctx, "docker", "manifest", "push", manifestListName)
			if err := util.RunE(log, cmd); err != nil {
				return fmt.Errorf("failed to push manifest list: %v", err)
			}
		}
	}

	return nil
}

type imageTargets []imageTarget

func (i imageTargets) bazelTargets() []string {
	out := make([]string, len(i))
	for idx, target := range i {
		out[idx] = target.bazelTarget()
	}
	return out
}

func (i imageTargets) bazelExportTargets() []string {
	out := make([]string, len(i))
	for idx, target := range i {
		out[idx] = target.bazelExportTarget()
	}
	return out
}

func (i imageTargets) taggedImageNames() []string {
	out := make([]string, 0)
	for _, target := range i {
		out = append(out, target.taggedImageNames()...)
	}
	return out
}

func (i imageTargets) exportedImageNames() []string {
	out := make([]string, 0)
	for _, target := range i {
		out = append(out, target.taggedImageNames()...)
	}
	return out
}

func (i imageTargets) withOSArch(os, arch string) imageTargets {
	out := make(imageTargets, 0)
	for _, target := range i {
		if target.os == os && target.arch == arch {
			out = append(out, target)
		}
	}
	return out
}

func manifestListNameForComponent(s string) string {
	return fmt.Sprintf("%s/cert-manager-%s:%s", flags.Default.DockerRepo, s, flags.Default.AppVersion)
}

type manifestAnnotation struct {
	os, arch, variant string
}

func manifestListAnnotationsForTarget(t imageTarget) manifestAnnotation {
	if t.arch == "amd64" {
		return manifestAnnotation{
			os:   t.os,
			arch: t.arch,
		}
	}
	if t.arch == "arm" {
		return manifestAnnotation{
			os:      t.os,
			arch:    "arm",
			variant: "v7",
		}
	}
	if t.arch == "arm64" {
		return manifestAnnotation{
			os:      t.os,
			arch:    "arm64",
			variant: "v8",
		}
	}
	return manifestAnnotation{}
}

type imageTarget struct {
	name, os, arch string
}

func (i imageTarget) bazelTarget() string {
	return fmt.Sprintf("//cmd/%s:image", i.name)
}

func (i imageTarget) bazelExportTarget() string {
	return fmt.Sprintf("//cmd/%s:image.export", i.name)
}

func (i imageTarget) exportedImageName() string {
	return fmt.Sprintf("%s:%s", i.name, flags.Default.GitCommitRef)
}

func (i imageTarget) taggedImageNames() []string {
	return []string{
		fmt.Sprintf("%s/cert-manager-%s-%s:%s", flags.Default.DockerRepo, i.name, i.arch, flags.Default.AppVersion),
		fmt.Sprintf("%s/cert-manager-%s-%s:%s", flags.Default.DockerRepo, i.name, i.arch, flags.Default.GitCommitRef),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net"
	"time"

	log "github.com/cihub/seelog"
)

type squeezebox struct {
	conn     net.Conn
	read     chan string
	write    chan string
	response chan struct{}
}

func NewSqueezebox() *squeezebox {

	return &squeezebox{
		read:     make(chan string, 100),
		write:    make(chan string, 100),
		response: make(chan struct{}),
	}

}

func (s *squeezebox) Read() chan string {
	return s.read
}

func (s *squeezebox) Connect(host, username, password string) (err error) {
	s.conn, err = net.Dial("tcp", host)
	if err != nil {
		return err
	}
	go s.writer()
	go s.reader()

	s.Sendf("login %s %s", username, password)
	return
}
func (s *squeezebox) SendTo(d *Device, cmd string) {
	s.Send(d.IdUrlEncoded() + " " + cmd)
}

func (s *squeezebox) Send(cmd string) {
	log.Info("Sending: ", cmd)
	wait := make(chan struct{})
	go s.waitForResponse(wait)
	s.write <- (cmd) + "\n"
	//s.write <- url.QueryEscape(cmd) + "\n"
	<-wait
}
func (s *squeezebox) Sendf(format string, a ...interface{}) {
	s.Send(fmt.Sprintf(format, a...))
}

func (s *squeezebox) waitForResponse(c chan struct{}) {
	select {
	case <-s.response:
		c <- struct{}{}
		return
	case <-time.After(time.Second * 1):
		log.Error("We got TIMOUT after send")
		c <- struct{}{}
		return
	}

}

func (s *squeezebox) writer() {
	for v := range s.write {
		_, err := s.conn.Write([]byte(v))
		if err != nil {
			log.Error(err)
		}
	}
}

func (s *squeezebox) reader() {
	connbuf := bufio.NewReader(s.conn)
	for {
		str, err := connbuf.ReadString('\n')
		if err != nil {
			log.Error(err)
			break
		}
		if len(str) > 0 {
			s.response <- struct{}{}
			s.read <- str
		}
	}
}
<commit_msg>Dont wait for response channel when it does not exist<commit_after>package main

import (
	"bufio"
	"fmt"
	"net"
	"time"

	log "github.com/cihub/seelog"
)

type squeezebox struct {
	conn     net.Conn
	read     chan string
	write    chan string
	response chan struct{}
}

func NewSqueezebox() *squeezebox {

	return &squeezebox{
		read:     make(chan string, 100),
		write:    make(chan string, 100),
		response: make(chan struct{}),
	}

}

func (s *squeezebox) Read() chan string {
	return s.read
}

func (s *squeezebox) Connect(host, username, password string) (err error) {
	s.conn, err = net.Dial("tcp", host)
	if err != nil {
		return err
	}
	go s.writer()
	go s.reader()

	s.Sendf("login %s %s", username, password)
	return
}
func (s *squeezebox) SendTo(d *Device, cmd string) {
	s.Send(d.IdUrlEncoded() + " " + cmd)
}

func (s *squeezebox) Send(cmd string) {
	log.Info("Sending: ", cmd)
	wait := make(chan struct{})
	go s.waitForResponse(wait)
	s.write <- (cmd) + "\n"
	//s.write <- url.QueryEscape(cmd) + "\n"
	<-wait
}
func (s *squeezebox) Sendf(format string, a ...interface{}) {
	s.Send(fmt.Sprintf(format, a...))
}

func (s *squeezebox) waitForResponse(c chan struct{}) {
	select {
	case <-s.response:
		c <- struct{}{}
		return
	case <-time.After(time.Second * 1):
		log.Error("We got TIMOUT after send")
		c <- struct{}{}
		return
	}

}

func (s *squeezebox) writer() {
	for v := range s.write {
		_, err := s.conn.Write([]byte(v))
		if err != nil {
			log.Error(err)
		}
	}
}

func (s *squeezebox) reader() {
	connbuf := bufio.NewReader(s.conn)
	for {
		str, err := connbuf.ReadString('\n')
		if err != nil {
			log.Error(err)
			break
		}
		if len(str) > 0 {
			select {
			case s.response <- struct{}{}:
			default:
			}
			s.read <- str
		}
	}
}
<|endoftext|>


<commit_before>package ungo

import(
	"net/http"
	"io/ioutil"
)

func htmlDownload(url string,host string) string {
    client := &http.Client{}

    req, err := http.NewRequest("GET", url , nil)
    if err != nil {
        panic(err)
    }

    req.Header.Set("Host", host)
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko")
    req.Header.Set("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    req.Header.Set("Accept-Language", "de,en;q=0.7,en-us;q=0.3")
    req.Header.Set("Cache-Control", "no")

    resp, err := client.Do(req)
    if err != nil {
        panic(err)
   	}

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

    if err != nil {
        panic(err)
    }

    return string(body)
}<commit_msg>Delete navegador.go<commit_after><|endoftext|>


<commit_before>package main

import (
	"github.com/therecipe/qt/gui"
	"github.com/therecipe/qt/widgets"
	"os"
)

/*
#include "mainwindow.h"
MainWindow::MainWindow() {
    setWindowTitle("RMS System");
    setFixedSize(500, 500);

    setWindowIcon(QIcon("window_logo.png"));

    createIcons();
    setupCoreWidgets();
    createMenuBar();
    createToolBar();

    centralWidgetLayout->addLayout(formLayout);
    centralWidgetLayout->addWidget(appTable);
    //centralWidgetLayout->addStretch();
    centralWidgetLayout->addLayout(buttonsLayout);

    mainWidget->setLayout(centralWidgetLayout);

    setCentralWidget(mainWidget);

    setupSignalsAndSlots();

}

void MainWindow::createIcons() {
    newIcon = QPixmap("new.png");
    openIcon = QPixmap("open.png");
    closeIcon = QPixmap("close.png");
    clearIcon = QPixmap("clear.png");
    deleteIcon = QPixmap("delete.png");
}

void MainWindow::setupCoreWidgets() {
    mainWidget = new QWidget();
    centralWidgetLayout = new QVBoxLayout();
    formLayout = new QGridLayout();
    buttonsLayout = new QHBoxLayout();

    nameLabel = new QLabel("Name:");
    dateOfBirthLabel= new QLabel("Date Of Birth:");
    phoneNumberLabel = new QLabel("Phone Number");
    savePushButton = new QPushButton("Save");
    clearPushButton = new QPushButton("Clear All");

    nameLineEdit = new QLineEdit();
    dateOfBirthEdit = new QDateEdit(QDate(1980, 1, 1));
    phoneNumberLineEdit = new QLineEdit();

    // TableView
    appTable = new QTableView();
    model = new QStandardItemModel(1, 3, this);
    appTable->setContextMenuPolicy(Qt::CustomContextMenu);
    appTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // Note

	model->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	model->setHorizontalHeaderItem(1, new QStandardItem(QString("Date of Birth")));
	model->setHorizontalHeaderItem(2, new QStandardItem(QString("Phone Number")));

	appTable->setModel(model);
	QStandardItem *firstItem = new QStandardItem(QString("G. Sohne"));
	QDate dateOfBirth(1980, 1, 1);
	QStandardItem *secondItem = new QStandardItem(dateOfBirth.toString());
	QStandardItem *thirdItem = new QStandardItem(QString("05443394858"));
	model->setItem(0,0,firstItem);
	model->setItem(0,1,secondItem);
	model->setItem(0,2,thirdItem);

	//Layouts
	formLayout->addWidget(nameLabel, 0, 0);
	formLayout->addWidget(nameLineEdit, 0, 1);
	formLayout->addWidget(dateOfBirthLabel, 1, 0);
	formLayout->addWidget(dateOfBirthEdit, 1, 1);
	formLayout->addWidget(phoneNumberLabel, 2, 0);
	formLayout->addWidget(phoneNumberLineEdit, 2, 1);

	buttonsLayout->addStretch();
	buttonsLayout->addWidget(savePushButton);
	buttonsLayout->addWidget(clearPushButton);
}

void MainWindow::createMenuBar() {
	// Setup File Menu
	fileMenu = menuBar()->addMenu("&File");
	quitAction = new QAction(closeIcon, "Quit", this);
	quitAction->setShortcuts(QKeySequence::Quit);

	newAction = new QAction(newIcon, "&New", this);
	newAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
	openAction = new QAction(openIcon, "&New", this);
	openAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));

	fileMenu->addAction(newAction);
	fileMenu->addAction(openAction);
	fileMenu->addSeparator();
	fileMenu->addAction(quitAction);

	helpMenu = menuBar()->addMenu("Help");
	aboutAction = new QAction("About", this);
	aboutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H));

	helpMenu->addAction(aboutAction);
}

void MainWindow::createToolBar() {
	// Setup Tool bar menu
	toolbar = addToolBar("main toolbar");
	// toolbar->setMovable( false );

	newToolBarAction = toolbar->addAction(QIcon(newIcon), "New File");
	openToolBarAction = toolbar->addAction(QIcon(openIcon), "Open File");
	toolbar->addSeparator();
	clearToolBarAction = toolbar->addAction(QIcon(clearIcon), "Clear All");
	deleteOneEntryToolBarAction = toolbar->addAction(QIcon(deleteIcon), "Delete a record");
	closeToolBarAction = toolbar->addAction(QIcon(closeIcon), "Quit Application");
}

void MainWindow::setupSignalsAndSlots() {
	// Setup Signals and Slots
	connect(quitAction, &QAction::triggered, this, &QApplication::quit);
	connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutDialog()));
	connect(clearToolBarAction, SIGNAL(triggered()), this, SLOT(clearAllRecords()));
	connect(closeToolBarAction, &QAction::triggered, this, &QApplication::quit);
	connect(deleteOneEntryToolBarAction, SIGNAL(triggered()), this, SLOT(deleteSavedRecord()));
	connect(savePushButton, SIGNAL(clicked()), this, SLOT(saveButtonClicked()));
	connect(clearPushButton, SIGNAL(clicked()), this, SLOT(clearAllRecords()));
}

void MainWindow::deleteSavedRecord() {
	bool ok;
	int rowId = QInputDialog::getInt(this, tr("Select Row to delete"),
	tr("Please enter Row ID of record (Eg. 1)"),
	1, 1, model->rowCount(), 1, &ok );
	if (ok) {
		model->removeRow(rowId-1);
	}
}

void MainWindow::saveButtonClicked() {
	QStandardItem *name = new QStandardItem(nameLineEdit->text());
	QStandardItem *dob = new QStandardItem(dateOfBirthEdit->date().toString());
	QStandardItem *phoneNumber = new QStandardItem(phoneNumberLineEdit->text());

	model->appendRow({ name, dob, phoneNumber});
	clearFields();

	QMessageBox::information(this, tr("RMS System"), tr("Record saved successfully!"),
	QMessageBox::Ok|QMessageBox::Default,
	QMessageBox::NoButton, QMessageBox::NoButton);
}

void MainWindow::clearFields() {
	nameLineEdit->clear();
	phoneNumberLineEdit->setText("");
	QDate dateOfBirth(1980, 1, 1);
	dateOfBirthEdit->setDate(dateOfBirth);
}

void MainWindow::clearAllRecords() {

//   int status = QMessageBox::question( this, tr("Delete Records ?"),
//                                       tr("You are about to delete all saved records "
//                                          "
Are you sure you want to delete all records "),
//                                       QMessageBox::No|QMessageBox::Default, QMessageBox::No|QMessageBox::Escape, QMessageBox::NoButton);
//   if (status == QMessageBox::Yes) return model->clear();

	int status = QMessageBox::question(this, tr("Delete all Records ?"),
				tr("This operation will delete all saved records. "
				"
Do you want to remove all saved records ? "
				), tr("Yes, Delete all records"), tr("No !"), 		QString(), 1, 1);
	if (status == 0) {
		int rowCount = model->rowCount();
		model->removeRows(0, rowCount);
	}
}

void MainWindow::aboutDialog() {
	QMessageBox::about(this, "About RMS System",
	"RMS System 2.0"
	"
Copyright © 2005 Inc."
	"This is a simple application to demonstrate the use of windows,"
	"tool bars, menus and dialog boxes");
}

*/

func main() {
	// Create application
	app := widgets.NewQApplication(len(os.Args), os.Args) // func NewQApplication(argc int, argv []string) *QApplication

	// Start from here, ie the beginning.
	// Create main window
	window := widgets.NewQMainWindow(nil, 0)  //  func NewQMainWindow(parent QWidget_ITF, flags core.Qt__WindowType) *QMainWindow
	window.SetWindowTitle("SRM System Example")  // func (ptr *QGraphicsWidget) SetWindowTitle(title string)
	window.SetFixedSize2(500, 500) //  func (ptr *QWidget) SetMinimumSize2(minw int, minh int)

	newIcon := gui.QIcon_FromTheme2("document-new", gui.NewQIcon5("new.png"))
	openIcon := gui.QIcon_FromTheme2("document-open", gui.NewQIcon5("open.png"))
	closeIcon := gui.QIcon_FromTheme2("document-close", gui.NewQIcon5("close.png"))

	// set up menu bar, and maybe toolbar
	menubar := window.MenuBar()

	qactionpointerslice := make([]*widgets.QAction,0,5)

	// set up file menu option
	fileMenu := menubar.AddMenu2("&File")
    a := fileMenu.AddAction2(newIcon,"&New")  // a has type *QAction
    filenewmenuoption := func () {
    	// need a function here.  I'll make it a dummy function
    	widgets.QMessageBox_About(window, "File New", "File New Menu option was selected")
		return
	}
	a.ConnectTriggered(func(checked bool) {
		filenewmenuoption()
		return
	})  // function to execute when option is triggered

	a.SetPriority(widgets.QAction__LowPriority)
	a.SetShortcuts2(gui.QKeySequence__New)

	qactionpointerslice = append(qactionpointerslice, a)

	b := fileMenu.AddAction2(openIcon, "&Open") // b has type *QAction
    fileopenmenuoption := func () {
    	// need a function here.  I'll make it a dummy function
    	widgets.QMessageBox_About(window, "File Open", "File Open Menu option was selected")
		return
	}
	b.ConnectTriggered(func(checked bool) {
		fileopenmenuoption()
		return
	}) // function to execute when option is triggered

	b.SetPriority(widgets.QAction__LowPriority)
	b.SetShortcuts2(gui.QKeySequence__Open)

	qactionpointerslice = append(qactionpointerslice, b)

	fileMenu.AddSeparator()

	e := fileMenu.AddAction2(closeIcon, "&Close")
	fileclosemenuoption := func() {
		widgets.QMessageBox_About(window, "File Close", "File Close menu option was selected")
		return
	}
	e.ConnectTriggered(func(checked bool) {
		fileclosemenuoption()
		return
	})
	e.SetPriority(widgets.QAction__LowPriority)
	e.SetShortcuts2(gui.QKeySequence__Close)
	qactionpointerslice = append(qactionpointerslice, e)

	quitIcon := gui.QIcon_FromTheme2("document-quit", gui.NewQIcon5("quit-512.png"))
	c := fileMenu.AddAction2(quitIcon, "&Quit")
	filequitmenuoption := func() {
		widgets.QMessageBox_About(window, "File Quit", "File Quit Menu option was selected")
		app.Quit()
		return
	}
	c.ConnectTriggered(func(checked bool) {
		filequitmenuoption()
		return
	})
	c.SetPriority(widgets.QAction__LowPriority)
	c.SetShortcuts2(gui.QKeySequence__Quit)
	qactionpointerslice = append(qactionpointerslice, c)

	helpIcon := gui.QIcon_FromTheme2("document-help", gui.NewQIcon5("help2.png"))
	d := fileMenu.AddAction2(helpIcon, "&Help")
	filehelpmenuoption := func() {
		widgets.QMessageBox_About(window, "Help", "File Help menu option was selected")
		return
	}
	d.ConnectTriggered(func(checked bool) {
		filehelpmenuoption()
		return
	})
	d.SetPriority(widgets.QAction__LowPriority)
	d.SetShortcuts2(gui.QKeySequence__HelpContents)
	qactionpointerslice = append(qactionpointerslice, d)

	aboutIcon := gui.QIcon_FromTheme2("document-about", gui.NewQIcon5("about.png"))
	f := fileMenu.AddAction2(aboutIcon, "&About")
	fileaboutmenuoption := func() {
		widgets.QMessageBox_About(window, "about", "File About menu option was selected")
		return
	}
	f.ConnectTriggered(func(checked bool) {
		fileaboutmenuoption()
		return
	})
	f.SetPriority(widgets.QAction__LowPriority)
	f.SetShortcuts2(gui.QKeySequence__WhatsThis)
	qactionpointerslice = append(qactionpointerslice, f)

// turns out that this is not needed
	//fileMenu.AddActions(qactionpointerslice)

//	window.SetLayout(layout)  I'm getting an error that says attempting to set layout on QMainWindow which already has a layout
	window.Show()

	// Execute app
	app.Exec()
}
<commit_msg>03/18/2020 03:52:11 PM dialog.go -- just in case<commit_after>package main

import (
	"github.com/therecipe/qt/core"
	"github.com/therecipe/qt/gui"
	"github.com/therecipe/qt/widgets"
	"os"
)

/*
#include "mainwindow.h"
MainWindow::MainWindow() {
    setWindowTitle("RMS System");
    setFixedSize(500, 500);

    setWindowIcon(QIcon("window_logo.png"));

    createIcons();
    setupCoreWidgets();
    createMenuBar();
    createToolBar();

    centralWidgetLayout->addLayout(formLayout);
    centralWidgetLayout->addWidget(appTable);
    //centralWidgetLayout->addStretch();
    centralWidgetLayout->addLayout(buttonsLayout);

    mainWidget->setLayout(centralWidgetLayout);

    setCentralWidget(mainWidget);

    setupSignalsAndSlots();

}

void MainWindow::createIcons() {
    newIcon = QPixmap("new.png");
    openIcon = QPixmap("open.png");
    closeIcon = QPixmap("close.png");
    clearIcon = QPixmap("clear.png");
    deleteIcon = QPixmap("delete.png");
}

void MainWindow::setupCoreWidgets() {
    mainWidget = new QWidget();
    centralWidgetLayout = new QVBoxLayout();
    formLayout = new QGridLayout();
    buttonsLayout = new QHBoxLayout();

    nameLabel = new QLabel("Name:");
    dateOfBirthLabel= new QLabel("Date Of Birth:");
    phoneNumberLabel = new QLabel("Phone Number");
    savePushButton = new QPushButton("Save");
    clearPushButton = new QPushButton("Clear All");

    nameLineEdit = new QLineEdit();
    dateOfBirthEdit = new QDateEdit(QDate(1980, 1, 1));
    phoneNumberLineEdit = new QLineEdit();

    // TableView
    appTable = new QTableView();
    model = new QStandardItemModel(1, 3, this);
    appTable->setContextMenuPolicy(Qt::CustomContextMenu);
    appTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); // Note

	model->setHorizontalHeaderItem(0, new QStandardItem(QString("Name")));
	model->setHorizontalHeaderItem(1, new QStandardItem(QString("Date of Birth")));
	model->setHorizontalHeaderItem(2, new QStandardItem(QString("Phone Number")));

	appTable->setModel(model);
	QStandardItem *firstItem = new QStandardItem(QString("G. Sohne"));
	QDate dateOfBirth(1980, 1, 1);
	QStandardItem *secondItem = new QStandardItem(dateOfBirth.toString());
	QStandardItem *thirdItem = new QStandardItem(QString("05443394858"));
	model->setItem(0,0,firstItem);
	model->setItem(0,1,secondItem);
	model->setItem(0,2,thirdItem);

	//Layouts
	formLayout->addWidget(nameLabel, 0, 0);
	formLayout->addWidget(nameLineEdit, 0, 1);
	formLayout->addWidget(dateOfBirthLabel, 1, 0);
	formLayout->addWidget(dateOfBirthEdit, 1, 1);
	formLayout->addWidget(phoneNumberLabel, 2, 0);
	formLayout->addWidget(phoneNumberLineEdit, 2, 1);

	buttonsLayout->addStretch();
	buttonsLayout->addWidget(savePushButton);
	buttonsLayout->addWidget(clearPushButton);
}

void MainWindow::createMenuBar() {
	// Setup File Menu
	fileMenu = menuBar()->addMenu("&File");
	quitAction = new QAction(closeIcon, "Quit", this);
	quitAction->setShortcuts(QKeySequence::Quit);

	newAction = new QAction(newIcon, "&New", this);
	newAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C));
	openAction = new QAction(openIcon, "&New", this);
	openAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));

	fileMenu->addAction(newAction);
	fileMenu->addAction(openAction);
	fileMenu->addSeparator();
	fileMenu->addAction(quitAction);

	helpMenu = menuBar()->addMenu("Help");
	aboutAction = new QAction("About", this);
	aboutAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_H));

	helpMenu->addAction(aboutAction);
}

void MainWindow::createToolBar() {
	// Setup Tool bar menu
	toolbar = addToolBar("main toolbar");
	// toolbar->setMovable( false );

	newToolBarAction = toolbar->addAction(QIcon(newIcon), "New File");
	openToolBarAction = toolbar->addAction(QIcon(openIcon), "Open File");
	toolbar->addSeparator();
	clearToolBarAction = toolbar->addAction(QIcon(clearIcon), "Clear All");
	deleteOneEntryToolBarAction = toolbar->addAction(QIcon(deleteIcon), "Delete a record");
	closeToolBarAction = toolbar->addAction(QIcon(closeIcon), "Quit Application");
}

void MainWindow::setupSignalsAndSlots() {
	// Setup Signals and Slots
	connect(quitAction, &QAction::triggered, this, &QApplication::quit);
	connect(aboutAction, SIGNAL(triggered()), this, SLOT(aboutDialog()));
	connect(clearToolBarAction, SIGNAL(triggered()), this, SLOT(clearAllRecords()));
	connect(closeToolBarAction, &QAction::triggered, this, &QApplication::quit);
	connect(deleteOneEntryToolBarAction, SIGNAL(triggered()), this, SLOT(deleteSavedRecord()));
	connect(savePushButton, SIGNAL(clicked()), this, SLOT(saveButtonClicked()));
	connect(clearPushButton, SIGNAL(clicked()), this, SLOT(clearAllRecords()));
}

void MainWindow::deleteSavedRecord() {
	bool ok;
	int rowId = QInputDialog::getInt(this, tr("Select Row to delete"),
	tr("Please enter Row ID of record (Eg. 1)"),
	1, 1, model->rowCount(), 1, &ok );
	if (ok) {
		model->removeRow(rowId-1);
	}
}

void MainWindow::saveButtonClicked() {
	QStandardItem *name = new QStandardItem(nameLineEdit->text());
	QStandardItem *dob = new QStandardItem(dateOfBirthEdit->date().toString());
	QStandardItem *phoneNumber = new QStandardItem(phoneNumberLineEdit->text());

	model->appendRow({ name, dob, phoneNumber});
	clearFields();

	QMessageBox::information(this, tr("RMS System"), tr("Record saved successfully!"),
	QMessageBox::Ok|QMessageBox::Default,
	QMessageBox::NoButton, QMessageBox::NoButton);
}

void MainWindow::clearFields() {
	nameLineEdit->clear();
	phoneNumberLineEdit->setText("");
	QDate dateOfBirth(1980, 1, 1);
	dateOfBirthEdit->setDate(dateOfBirth);
}

void MainWindow::clearAllRecords() {

//   int status = QMessageBox::question( this, tr("Delete Records ?"),
//                                       tr("You are about to delete all saved records "
//                                          "
Are you sure you want to delete all records "),
//                                       QMessageBox::No|QMessageBox::Default, QMessageBox::No|QMessageBox::Escape, QMessageBox::NoButton);
//   if (status == QMessageBox::Yes) return model->clear();

	int status = QMessageBox::question(this, tr("Delete all Records ?"),
				tr("This operation will delete all saved records. "
				"
Do you want to remove all saved records ? "
				), tr("Yes, Delete all records"), tr("No !"), 		QString(), 1, 1);
	if (status == 0) {
		int rowCount = model->rowCount();
		model->removeRows(0, rowCount);
	}
}

void MainWindow::aboutDialog() {
	QMessageBox::about(this, "About RMS System",
	"RMS System 2.0"
	"
Copyright © 2005 Inc."
	"This is a simple application to demonstrate the use of windows,"
	"tool bars, menus and dialog boxes");
}

*/

func main() {
	// Create application
	app := widgets.NewQApplication(len(os.Args), os.Args) // func NewQApplication(argc int, argv []string) *QApplication

	// Create main window
	window := widgets.NewQMainWindow(nil, 0)  //  func NewQMainWindow(parent QWidget_ITF, flags core.Qt__WindowType) *QMainWindow
	window.SetWindowTitle("Dialog Example translated into Go")  // func (ptr *QGraphicsWidget) SetWindowTitle(title string)
	window.SetFixedSize2(500, 500) //  func (ptr *QWidget) SetMinimumSize2(minw int, minh int)

	windowIcon := gui.QIcon_FromTheme2("window-icon", gui.NewQIcon5("window_logo.png"))
	window.SetWindowIcon(windowIcon)

	newIcon := gui.QIcon_FromTheme2("new", gui.NewQIcon5("new.png"))
	openIcon := gui.QIcon_FromTheme2("open", gui.NewQIcon5("open.png"))
	closeIcon := gui.QIcon_FromTheme2("close", gui.NewQIcon5("close.png"))
	clearIcon := gui.QIcon_FromTheme2("clear", gui.NewQIcon5("clear.png"))
	deleteIcon := gui.QIcon_FromTheme2("delete", gui.NewQIcon5("delete.png"))

	centralwidget := widgets.NewQWidget(nil, 0)
	vboxLayout := widgets.NewQVBoxLayout()  // this more closely matches the example code above
	formLayout := widgets.NewQGridLayout2()
	buttonsLayout := widgets.NewQHBoxLayout()

	nameLabel := widgets.NewQLabel2("Name:", centralwidget, 0)
	DOBLabel := widgets.NewQLabel2("Date of Birth:", centralwidget, 0)
	phoneNumberLabel := widgets.NewQLabel2("Phone Number", centralwidget, 0)
	savePushButton :=  widgets.NewQPushButton2("Save", centralwidget)
	clearPushButton := widgets.NewQPushButton2("Clear All", centralwidget)
	nameLineEdit := widgets.NewQLineEdit(centralwidget)
	DOBEdit := widgets.NewQDateEdit2(core.NewQDate3(1980,1,1),centralwidget)
	phoneNumberLineEdit := widgets.NewQLineEdit(centralwidget)

    // table view
    appTable := widgets.NewQTableView(centralwidget)
    model := gui.NewQStandardItemModel2(1,3,centralwidget)
    appTable.SetContextMenuPolicy(core.Qt__CustomContextMenu)
    appTable.HorizontalHeader().SetSectionResizeMode(widgets.QHeaderView__Stretch)

	centralwidget.SetLayout(hboxlayout)  // this line gives an error of attempting to set layout which already has a layout
	window.SetCentralWidget(centralwidget)

	urlLineEdit.SetPlaceholderText("Enter Url to export")
	urlLineEdit.SetFixedWidth(200)
	centralwidget.Layout().AddWidget(urlLineEdit)

	centralwidget.Layout().AddWidget(exportbutton)

	// set up menu bar, and maybe toolbar
	menubar := window.MenuBar()

	qactionpointerslice := make([]*widgets.QAction,0,5)

	// set up file menu option
	fileMenu := menubar.AddMenu2("&File")
    a := fileMenu.AddAction2(newIcon,"&New")  // a has type *QAction
    filenewmenuoption := func () {
    	// need a function here.  I'll make it a dummy function
    	widgets.QMessageBox_About(window, "File New", "File New Menu option was selected")
		return
	}
	a.ConnectTriggered(func(checked bool) {
		filenewmenuoption()
		return
	})  // function to execute when option is triggered

	a.SetPriority(widgets.QAction__LowPriority)
	a.SetShortcuts2(gui.QKeySequence__New)

	qactionpointerslice = append(qactionpointerslice, a)

	b := fileMenu.AddAction2(openIcon, "&Open") // b has type *QAction
    fileopenmenuoption := func () {
    	// need a function here.  I'll make it a dummy function
    	widgets.QMessageBox_About(window, "File Open", "File Open Menu option was selected")
		return
	}
	b.ConnectTriggered(func(checked bool) {
		fileopenmenuoption()
		return
	}) // function to execute when option is triggered

	b.SetPriority(widgets.QAction__LowPriority)
	b.SetShortcuts2(gui.QKeySequence__Open)

	qactionpointerslice = append(qactionpointerslice, b)

	fileMenu.AddSeparator()

	e := fileMenu.AddAction2(closeIcon, "&Close")
	fileclosemenuoption := func() {
		widgets.QMessageBox_About(window, "File Close", "File Close menu option was selected")
		return
	}
	e.ConnectTriggered(func(checked bool) {
		fileclosemenuoption()
		return
	})
	e.SetPriority(widgets.QAction__LowPriority)
	e.SetShortcuts2(gui.QKeySequence__Close)
	qactionpointerslice = append(qactionpointerslice, e)

	//quitIcon := gui.QIcon_FromTheme2("document-quit", gui.NewQIcon5("quit-512.png"))
	c := fileMenu.AddAction2(windowIcon, "&Quit")
	filequitmenuoption := func() {
		widgets.QMessageBox_About(window, "File Quit", "File Quit Menu option was selected")
		app.Quit()
		return
	}
	c.ConnectTriggered(func(checked bool) {
		filequitmenuoption()
		return
	})
	c.SetPriority(widgets.QAction__LowPriority)
	c.SetShortcuts2(gui.QKeySequence__Quit)
	qactionpointerslice = append(qactionpointerslice, c)

	helpIcon := gui.QIcon_FromTheme2("document-help", gui.NewQIcon5("help2.png"))
	d := fileMenu.AddAction2(helpIcon, "&Help")
	filehelpmenuoption := func() {
		widgets.QMessageBox_About(window, "Help", "File Help menu option was selected")
		return
	}
	d.ConnectTriggered(func(checked bool) {
		filehelpmenuoption()
		return
	})
	d.SetPriority(widgets.QAction__LowPriority)
	d.SetShortcuts2(gui.QKeySequence__HelpContents)
	qactionpointerslice = append(qactionpointerslice, d)

	aboutIcon := gui.QIcon_FromTheme2("document-about", gui.NewQIcon5("about.png"))
	f := fileMenu.AddAction2(aboutIcon, "&About")
	fileaboutmenuoption := func() {
		widgets.QMessageBox_About(window, "about", "File About menu option was selected")
		return
	}
	f.ConnectTriggered(func(checked bool) {
		fileaboutmenuoption()
		return
	})
	f.SetPriority(widgets.QAction__LowPriority)
	f.SetShortcuts2(gui.QKeySequence__WhatsThis)
	qactionpointerslice = append(qactionpointerslice, f)

// turns out that this is not needed
	//fileMenu.AddActions(qactionpointerslice)

//	window.SetLayout(layout)  I'm getting an error that says attempting to set layout on QMainWindow which already has a layout
	window.Show()

	// Execute app
	app.Exec()
}
<|endoftext|>


<commit_before>// Compact implementation of a static 2D quadtree
//
// Caution : Work In Progress
//
// 1st Goal is to support a Barnes Hut algorithm implementation.
// This variation of the BH is not for cosmology but for the problem of bodies on a 2D square that you want
// to put the most apart (like dancers in a crowded night club).
//
// 2nd Goal is to support more than 1 million bodies
//
// A quatree is a hierarchical set of nodes that divide the 2D space.
// Each node holds the bodies that are located in its area.
//
// This implementation put constraints on inputs :
//
//	Bodies's X,Y coordinates are float64 between 0 & 1
//	Quadtree architecture is static
//	Depth of nodes is limited to 8 (256 * 256 cells at the level 8)
//
// to see the doc
//
// 		godoc -goroot=$GOPATH -http=:8080 -index
//		godoc -http=:8080 -index
package quadtree

import (
	"fmt"
	"testing"
	"sort"
	"math"

)
type QuadtreeGini [9][10]float64

// a Quadtree store Nodes. It is a an array with direct access to the Nodes with the Nodes coordinate
// see Coord
type Quadtree struct {
	Nodes [1<<20]Node
	bodies * []Body // pointer to the body slice
	BodyCountGini QuadtreeGini // for each of the 9 levels, tencentile of bodies
}

var optim bool

func init() {
	optim = true
}

// constants used to navigate from one node to the other
const (
	NW = 0x0000
	NE = 0x0100
	SW = 0x0001
	SE = 0x0101
)

// init quadtree
func (q * Quadtree) Init( bodies * []Body) {
	q.bodies = bodies
	q.setupNodesCoord()
	q.setupNodesLinks()
	q.updateNodesList()
	q.updateNodesCOM()
}

// updates nodes according to bodies locations
// this function should be called when bodies have been moved
func (q * Quadtree) UpdateNodesListsAndCOM() {

	q.updateNodesList()
	q.updateNodesCOM()

	var t testing.T
	q.CheckIntegrity( &t)
}

// compute quadtree Nodes for levels from 0 to 7
func (q * Quadtree) updateNodesCOMAbove8() {

	for level := 7; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {
				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				node.updateCOM()
			}
		}
	}
}

// get nodes coords below
func NodesBelow(c Coord)  (coordNW, coordNE, coordSW, coordSE Coord) {

	levelBelow := c.Level() + 1
	i := c.X()
	j := c.Y()
	shift := uint( 8-levelBelow)

	// to go east at the level below, we flip to 1 the bit that is significant at that level
	coordNW = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | NW << shift)
	coordNE = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | NE << shift)
	coordSW = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | SW << shift)
	coordSE = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | SE << shift)

	return coordNW, coordNE, coordSW, coordSE
}

// setup node coord
func (q * Quadtree) setupNodesCoord() {
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {
				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				node.coord = coord
			}
		}
	}
}

// setup quadtree Nodes for levels from 7 to 0
func (q * Quadtree) setupNodesLinks() {

		Trace.Println( "setupNodesLinks")
	for level := 7; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// s := fmt.Sprintf("SetupNodesLinks level %8d i %8d j %8d coord %s",
					// level, i, j, node.coord.String())
				// fmt.Println(s)

				coordNW, coordNE, coordSW, coordSE := NodesBelow(coord)

				nodeNW := &q.Nodes[coordNW]
				nodeNE := &q.Nodes[coordNE]
				nodeSW := &q.Nodes[coordSW]
				nodeSE := &q.Nodes[coordSE]

				// bodies of the nodes below are chained
				// fmt.Printf("%8x\n", coord)
				node.first = & (nodeNW.Body)
				nodeNW.Body.next = & (nodeNE.Body)
				nodeNE.Body.next = & (nodeSW.Body)
				nodeSW.Body.next = & (nodeSE.Body)
			}
		}
	}
}

// fill quadtree at level 8 with bodies
func (q * Quadtree) updateNodesList() {

	Trace.Println( "updateNodesList")

	for idx, _ := range (*q.bodies) {

		b := &((*q.bodies)[idx])

		// 1st phase, remove the body from its current double linked list
		// link the next body to the previous one
		if( b.next != nil) {
			b.next.prev = b.prev
		}
		// link the previous body to the next one
		if (b.prev != nil) {
			b.prev.next = b.next
		} else {
			// if body prev is nil,
			// it can be either the current first of a node or it has not been initialized
			// if it is the current first of the node,
			// the first of the node shall point to the next of the body
			if( (q.Nodes[b.coord]).first == b) {
				(q.Nodes[b.coord]).first = b.next
			}
		}

		// 2nd Phase
		// put body as the first body of the node
		// shift the first body if it is already there
		// compute coord of body (this is direct access)
		coord := b.getCoord8()
		node := & (q.Nodes[coord])
		initialFirstBody := node.first
		if( ( initialFirstBody != nil) && (initialFirstBody != b)) {
			// double link body to the current node's first
			b.next = initialFirstBody
			initialFirstBody.prev = b
		}

		// body b is the new node's first
		node.first = b
		b.prev = nil

		// setup new coord
		b.coord = coord

		if( b.next == b) {
			s := fmt.Sprintf("updateNodesList: Node linked to itself coord : idx %d, %s", idx, b.coord.String())
			panic(s)
		}
	}
}

// compute COM of quadtree from level 8 to level 0
func (q * Quadtree) updateNodesCOM() {

	Trace.Println( "updateNodesCOM")
	// compute is bottom up
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// fmt.Println("updateNodesCOM ", q.Nodes[coord].coord.String())
				// s := fmt.Sprintf("updateNodesCOM level %8d i %8d j %8d coord %s",
					// level, i, j, node.coord.String())
				// fmt.Println(s)
				node.updateCOM()
			}
		}
	}
}

// check integrity of the quadtree by performing
// all kinds of test
func (q *Quadtree)CheckIntegrity(t * testing.T) {

	Trace.Printf("CheckIntegrity")
	nbBodies := 0

	// perform some tests on the links of each nodes
	for level := 8; level >= 8; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// test that the node coord is correct
				if q.Nodes[coord].coord != coord {
					s := fmt.Sprintf("node coord = %s, want %s",
						q.Nodes[coord].coord.String(), coord.String())
					t.Errorf(s)
				}

				// test that the node first body
				// has a nil previous body
				if( node.first != nil && node.first.prev != nil) {
					s := fmt.Sprintf("node coord = %s, has first body with non nil prev",
						q.Nodes[coord].coord.String())
					t.Errorf(s)
				}

				// test for each body of the chain of bodies
				// - that the next body previous body is the body
				rank := 0
				for b := node.first ; b != nil; b = b.next {

					if( b.next != nil && b.next.prev != b) {
						s := fmt.Sprintf("node coord = %s, has %d nth body with next body not point to him for prev",
							q.Nodes[coord].coord.String(), rank)
						t.Errorf(s)
					}
					nbBodies++
					rank++
				}
			}
		}
	}

	// check that all bodies are accounted for
	if nbBodies != len(*q.bodies) {
		t.Errorf("Nb bodies do not match expected %d, got %d", len(*q.bodies), nbBodies)
	}
}

// compute number of bodies per node
// update the counting of bodies per node for all levels
func (q* Quadtree) ComputeNbBodiesPerNode() {

	// perform some tests on the links of each nodes
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				q.updateBodiesNb( node)
			}
		}
	}
}

// compute the gini of body density par node at level 8
func (q* Quadtree) ComputeQuadtreeGini() {

	Info.Printf("ComputeQuadtreeGini begin")
	q.ComputeNbBodiesPerNode()

	// perform some tests on the links of each nodes
	for level := 8; level >= 0; level-- {

		rank := 0

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// var bodyCount []int
		bodyCountPerLevel := make([]int, nbNodesX*nbNodesY)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				bodyCountPerLevel[rank] += node.nbBodies
				rank++
				// fmt.Println( fmt.Sprintf("i %d j %d: %d", i, j, nbBodies))
			}
		}
		sort.Ints(bodyCountPerLevel)

		for tencile := 0; tencile< 10; tencile ++ {
			lowIndex  := int(math.Floor(float64(nbNodesX*nbNodesY) * float64(tencile)/10.0))
			highIndex := int(math.Floor(float64(nbNodesX*nbNodesY) * float64(tencile+1)/10.0))

			nbBodiesInTencile := 0
			for _, nbBodies := range bodyCountPerLevel[lowIndex:highIndex] {
				nbBodiesInTencile += nbBodies
			}
			q.BodyCountGini[level][tencile] = float64(nbBodiesInTencile) // /float64(len(*q.bodies))
		}
	}
	Info.Printf("ComputeQuadtreeGini end")
}

// consolidate the number of bodies attached to the node
// at level 8, this is the number of bodies
// above level 8, this is an aggregate of the number of bodies at the level below
func (q * Quadtree) updateBodiesNb(n * Node) {
	n.nbBodies = 0

	if n.coord.Level() == 8 {
		for b := n.first ; b != nil; b = b.next {
			n.nbBodies++
		}
	} else {
		coordNW, coordNE, coordSW, coordSE := NodesBelow(n.coord)

		nodeNW := &q.Nodes[coordNW]
		nodeNE := &q.Nodes[coordNE]
		nodeSW := &q.Nodes[coordSW]
		nodeSE := &q.Nodes[coordSE]

		n.nbBodies += nodeNW.nbBodies
		n.nbBodies += nodeNE.nbBodies
		n.nbBodies += nodeSW.nbBodies
		n.nbBodies += nodeSE.nbBodies
	}
}<commit_msg>removed a trace<commit_after>// Compact implementation of a static 2D quadtree
//
// Caution : Work In Progress
//
// 1st Goal is to support a Barnes Hut algorithm implementation.
// This variation of the BH is not for cosmology but for the problem of bodies on a 2D square that you want
// to put the most apart (like dancers in a crowded night club).
//
// 2nd Goal is to support more than 1 million bodies
//
// A quatree is a hierarchical set of nodes that divide the 2D space.
// Each node holds the bodies that are located in its area.
//
// This implementation put constraints on inputs :
//
//	Bodies's X,Y coordinates are float64 between 0 & 1
//	Quadtree architecture is static
//	Depth of nodes is limited to 8 (256 * 256 cells at the level 8)
//
// to see the doc
//
// 		godoc -goroot=$GOPATH -http=:8080 -index
//		godoc -http=:8080 -index
package quadtree

import (
	"fmt"
	"testing"
	"sort"
	"math"

)
type QuadtreeGini [9][10]float64

// a Quadtree store Nodes. It is a an array with direct access to the Nodes with the Nodes coordinate
// see Coord
type Quadtree struct {
	Nodes [1<<20]Node
	bodies * []Body // pointer to the body slice
	BodyCountGini QuadtreeGini // for each of the 9 levels, tencentile of bodies
}

var optim bool

func init() {
	optim = true
}

// constants used to navigate from one node to the other
const (
	NW = 0x0000
	NE = 0x0100
	SW = 0x0001
	SE = 0x0101
)

// init quadtree
func (q * Quadtree) Init( bodies * []Body) {
	q.bodies = bodies
	q.setupNodesCoord()
	q.setupNodesLinks()
	q.updateNodesList()
	q.updateNodesCOM()
}

// updates nodes according to bodies locations
// this function should be called when bodies have been moved
func (q * Quadtree) UpdateNodesListsAndCOM() {

	q.updateNodesList()
	q.updateNodesCOM()

	var t testing.T
	q.CheckIntegrity( &t)
}

// compute quadtree Nodes for levels from 0 to 7
func (q * Quadtree) updateNodesCOMAbove8() {

	for level := 7; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {
				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				node.updateCOM()
			}
		}
	}
}

// get nodes coords below
func NodesBelow(c Coord)  (coordNW, coordNE, coordSW, coordSE Coord) {

	levelBelow := c.Level() + 1
	i := c.X()
	j := c.Y()
	shift := uint( 8-levelBelow)

	// to go east at the level below, we flip to 1 the bit that is significant at that level
	coordNW = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | NW << shift)
	coordNE = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | NE << shift)
	coordSW = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | SW << shift)
	coordSE = Coord( uint(levelBelow)<<16 | uint(i)<<8 | uint(j) | SE << shift)

	return coordNW, coordNE, coordSW, coordSE
}

// setup node coord
func (q * Quadtree) setupNodesCoord() {
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {
				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				node.coord = coord
			}
		}
	}
}

// setup quadtree Nodes for levels from 7 to 0
func (q * Quadtree) setupNodesLinks() {

		Trace.Println( "setupNodesLinks")
	for level := 7; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// s := fmt.Sprintf("SetupNodesLinks level %8d i %8d j %8d coord %s",
					// level, i, j, node.coord.String())
				// fmt.Println(s)

				coordNW, coordNE, coordSW, coordSE := NodesBelow(coord)

				nodeNW := &q.Nodes[coordNW]
				nodeNE := &q.Nodes[coordNE]
				nodeSW := &q.Nodes[coordSW]
				nodeSE := &q.Nodes[coordSE]

				// bodies of the nodes below are chained
				// fmt.Printf("%8x\n", coord)
				node.first = & (nodeNW.Body)
				nodeNW.Body.next = & (nodeNE.Body)
				nodeNE.Body.next = & (nodeSW.Body)
				nodeSW.Body.next = & (nodeSE.Body)
			}
		}
	}
}

// fill quadtree at level 8 with bodies
func (q * Quadtree) updateNodesList() {

	Trace.Println( "updateNodesList")

	for idx, _ := range (*q.bodies) {

		b := &((*q.bodies)[idx])

		// 1st phase, remove the body from its current double linked list
		// link the next body to the previous one
		if( b.next != nil) {
			b.next.prev = b.prev
		}
		// link the previous body to the next one
		if (b.prev != nil) {
			b.prev.next = b.next
		} else {
			// if body prev is nil,
			// it can be either the current first of a node or it has not been initialized
			// if it is the current first of the node,
			// the first of the node shall point to the next of the body
			if( (q.Nodes[b.coord]).first == b) {
				(q.Nodes[b.coord]).first = b.next
			}
		}

		// 2nd Phase
		// put body as the first body of the node
		// shift the first body if it is already there
		// compute coord of body (this is direct access)
		coord := b.getCoord8()
		node := & (q.Nodes[coord])
		initialFirstBody := node.first
		if( ( initialFirstBody != nil) && (initialFirstBody != b)) {
			// double link body to the current node's first
			b.next = initialFirstBody
			initialFirstBody.prev = b
		}

		// body b is the new node's first
		node.first = b
		b.prev = nil

		// setup new coord
		b.coord = coord

		if( b.next == b) {
			s := fmt.Sprintf("updateNodesList: Node linked to itself coord : idx %d, %s", idx, b.coord.String())
			panic(s)
		}
	}
}

// compute COM of quadtree from level 8 to level 0
func (q * Quadtree) updateNodesCOM() {

	Trace.Println( "updateNodesCOM")
	// compute is bottom up
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// fmt.Println("updateNodesCOM ", q.Nodes[coord].coord.String())
				// s := fmt.Sprintf("updateNodesCOM level %8d i %8d j %8d coord %s",
					// level, i, j, node.coord.String())
				// fmt.Println(s)
				node.updateCOM()
			}
		}
	}
}

// check integrity of the quadtree by performing
// all kinds of test
func (q *Quadtree)CheckIntegrity(t * testing.T) {

	Trace.Printf("CheckIntegrity")
	nbBodies := 0

	// perform some tests on the links of each nodes
	for level := 8; level >= 8; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])

				// test that the node coord is correct
				if q.Nodes[coord].coord != coord {
					s := fmt.Sprintf("node coord = %s, want %s",
						q.Nodes[coord].coord.String(), coord.String())
					t.Errorf(s)
				}

				// test that the node first body
				// has a nil previous body
				if( node.first != nil && node.first.prev != nil) {
					s := fmt.Sprintf("node coord = %s, has first body with non nil prev",
						q.Nodes[coord].coord.String())
					t.Errorf(s)
				}

				// test for each body of the chain of bodies
				// - that the next body previous body is the body
				rank := 0
				for b := node.first ; b != nil; b = b.next {

					if( b.next != nil && b.next.prev != b) {
						s := fmt.Sprintf("node coord = %s, has %d nth body with next body not point to him for prev",
							q.Nodes[coord].coord.String(), rank)
						t.Errorf(s)
					}
					nbBodies++
					rank++
				}
			}
		}
	}

	// check that all bodies are accounted for
	if nbBodies != len(*q.bodies) {
		t.Errorf("Nb bodies do not match expected %d, got %d", len(*q.bodies), nbBodies)
	}
}

// compute number of bodies per node
// update the counting of bodies per node for all levels
func (q* Quadtree) ComputeNbBodiesPerNode() {

	// perform some tests on the links of each nodes
	for level := 8; level >= 0; level-- {

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				q.updateBodiesNb( node)
			}
		}
	}
}

// compute the gini of body density par node at level 8
func (q* Quadtree) ComputeQuadtreeGini() {

	Trace.Printf("ComputeQuadtreeGini begin")
	q.ComputeNbBodiesPerNode()

	// perform some tests on the links of each nodes
	for level := 8; level >= 0; level-- {

		rank := 0

		// nb of nodes for the current level
		nbNodesX := 1 << uint(level)
		nbNodesY := 1 << uint(level)

		// var bodyCount []int
		bodyCountPerLevel := make([]int, nbNodesX*nbNodesY)

		// parse nodes of level
		for i := 0; i < nbNodesX; i++ {
			for j := 0; j < nbNodesY; j++ {

				coord := GetCoord( level, i, j)
				node := &(q.Nodes[coord])
				bodyCountPerLevel[rank] += node.nbBodies
				rank++
				// fmt.Println( fmt.Sprintf("i %d j %d: %d", i, j, nbBodies))
			}
		}
		sort.Ints(bodyCountPerLevel)

		for tencile := 0; tencile< 10; tencile ++ {
			lowIndex  := int(math.Floor(float64(nbNodesX*nbNodesY) * float64(tencile)/10.0))
			highIndex := int(math.Floor(float64(nbNodesX*nbNodesY) * float64(tencile+1)/10.0))

			nbBodiesInTencile := 0
			for _, nbBodies := range bodyCountPerLevel[lowIndex:highIndex] {
				nbBodiesInTencile += nbBodies
			}
			q.BodyCountGini[level][tencile] = float64(nbBodiesInTencile) // /float64(len(*q.bodies))
		}
	}
	Trace.Printf("ComputeQuadtreeGini end")
}

// consolidate the number of bodies attached to the node
// at level 8, this is the number of bodies
// above level 8, this is an aggregate of the number of bodies at the level below
func (q * Quadtree) updateBodiesNb(n * Node) {
	n.nbBodies = 0

	if n.coord.Level() == 8 {
		for b := n.first ; b != nil; b = b.next {
			n.nbBodies++
		}
	} else {
		coordNW, coordNE, coordSW, coordSE := NodesBelow(n.coord)

		nodeNW := &q.Nodes[coordNW]
		nodeNE := &q.Nodes[coordNE]
		nodeSW := &q.Nodes[coordSW]
		nodeSE := &q.Nodes[coordSE]

		n.nbBodies += nodeNW.nbBodies
		n.nbBodies += nodeNE.nbBodies
		n.nbBodies += nodeSW.nbBodies
		n.nbBodies += nodeSE.nbBodies
	}
}<|endoftext|>


<commit_before>package pingdom

import (
	"github.com/stretchr/testify/assert"
	"testing"
	"net/http"
	"fmt"
	"strconv"
)

func TestUserService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@test.com"
                }
            ]
		}
		]
		}`)
	})
	want := []UsersResponse{
		{
			Id:       12,
			Paused:   "NO",
			Username: "John Doe",
			Sms:      []UserSmsResponse{
				{
					Id: 352,
					Severity: "HIGH",
					CountryCode: "1",
					Number: "6095555555",
					Provider: "nexmo",
				},
			},
			Email:    nil,
		},
		{
			Id:       234,
			Paused:   "NO",
			Username: "Jane Doe",
			Sms:      nil,
			Email:    []UserEmailResponse{
				{
					Id: 314,
					Severity: "HIGH",
					Address: "test@example.com",
				},
			},
		},
	}

	users, err := client.Users.List()
	assert.NoError(t, err)
	assert.Equal(t, want, users, "Users.List() should return correct result")
}

func TestUserService_Read(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@example.com"
                }
            ]
		}
		]
		}`)
	})
	want := UsersResponse{
		Id:       12,
		Paused:   "NO",
		Username: "John Doe",
		Sms:      []UserSmsResponse{
			{
				Id: 352,
				Severity: "HIGH",
				CountryCode: "1",
				Number: "6095555555",
				Provider: "nexmo",
			},
		},
		Email:    nil,
	}

	users, err := client.Users.Read(12)
	assert.NoError(t, err)
	assert.Equal(t, &want, users, "Users.Read(12) should return a user")
}

func TestUserService_Read_Failure(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@example.com"
                }
            ]
		}
		]
		}`)
	})

	want := fmt.Errorf("UserId: 24 not found")

	_, err := client.Users.Read(24)
	assert.Equal(t, want, err, "Read with an invalid user id should return an error.")
}

func TestUserService_Create(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{
			"user": {
				"id": 23439
			}
		}`)
	})

	want := &UsersResponse{
		Id: 23439,
	}

	u := User{
		Username : "testUser",
	}

	user, err := client.Users.Create(&u)
	assert.NoError(t, err)
	assert.Equal(t, want, user, "Users.Create() should return correct result")
}

func TestUserService_CreateContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{
			"contact_target": {
				"id": 23439
			}
		}`)
	})

	want := &CreateUserContactResponse{
		Id: 23439,
	}

	c := Contact{
		Email: "test@example.com",
		CountryCode: "1",
		Number: "5559995555",
	}

	contact, err := client.Users.CreateContact(userId, c)
	assert.NoError(t, err)
	assert.Equal(t, want, contact, "Users.CreateContact() should return contact_target.id")
}

func TestUserService_Delete(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		fmt.Fprint(w, `{
			"message":"Deletion of user was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Deletion of user was successful!",
	}

	response, err := client.Users.Delete(userId)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.Delete() should return PingdomResponse with message")

}

func TestUserService_DeleteContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	contactId := 87655

	mux.HandleFunc("/users/" + strconv.Itoa(userId)+"/" + strconv.Itoa(contactId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		fmt.Fprint(w, `{
			"message":"Deletion of contact target successful"
		}`)
	})

	want := &PingdomResponse{
		Message: "Deletion of contact target successful",
	}

	response, err := client.Users.DeleteContact(userId,contactId)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.DeleteContact() should return PingdomResponse with message")

}

func TestUserService_Update(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	user := User{
		Username: "updatedUsername",
	}

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
		fmt.Fprint(w, `{
			"message":"Modification of user was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Modification of user was successful!",
	}

	response, err := client.Users.Update(userId, &user)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.Update() should return PingdomResponse with message")

}

func TestUserService_UpdateContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	contactId := 87655
	contact := Contact{
		Email: "test@example.com",
	}

	mux.HandleFunc("/users/" + strconv.Itoa(userId)+"/" + strconv.Itoa(contactId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
		fmt.Fprint(w, `{
			"message":"Modification of contact target was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Modification of contact target was successful!",
	}

	response, err := client.Users.UpdateContact(userId,contactId,contact)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.UpdateContact() should return PingdomResponse with message")

}<commit_msg><commit_after>package pingdom

import (
	"github.com/stretchr/testify/assert"
	"testing"
	"net/http"
	"fmt"
	"strconv"
)

func TestUserService_List(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@test.com"
                }
            ]
		}
		]
		}`)
	})
	want := []UsersResponse{
		{
			Id:       12,
			Paused:   "NO",
			Username: "John Doe",
			Sms:      []UserSmsResponse{
				{
					Id: 352,
					Severity: "HIGH",
					CountryCode: "1",
					Number: "6095555555",
					Provider: "nexmo",
				},
			},
			Email:    nil,
		},
		{
			Id:       234,
			Paused:   "NO",
			Username: "Jane Doe",
			Sms:      nil,
			Email:    []UserEmailResponse{
				{
					Id: 314,
					Severity: "HIGH",
					Address: "test@test.com",
				},
			},
		},
	}

	users, err := client.Users.List()
	assert.NoError(t, err)
	assert.Equal(t, want, users, "Users.List() should return correct result")
}

func TestUserService_Read(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@example.com"
                }
            ]
		}
		]
		}`)
	})
	want := UsersResponse{
		Id:       12,
		Paused:   "NO",
		Username: "John Doe",
		Sms:      []UserSmsResponse{
			{
				Id: 352,
				Severity: "HIGH",
				CountryCode: "1",
				Number: "6095555555",
				Provider: "nexmo",
			},
		},
		Email:    nil,
	}

	users, err := client.Users.Read(12)
	assert.NoError(t, err)
	assert.Equal(t, &want, users, "Users.Read(12) should return a user")
}

func TestUserService_Read_Failure(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		fmt.Fprint(w, `{
	    "users": [
        {
            "id": 12,
            "name": "John Doe",
            "paused": "NO",
            "access_level": "contact",
            "use_severity_levels": false,
            "sms": [
                {
                    "id": 352,
                    "severity": "HIGH",
                    "country_code": "1",
                    "number": "6095555555",
                    "provider": "nexmo"
                }
            ]
        },
        {
            "id": 234,
            "name": "Jane Doe",
            "paused": "NO",
            "access_level": "default",
            "use_severity_levels": false,
            "email": [
                {
                    "id": 314,
                    "severity": "HIGH",
                    "address": "test@example.com"
                }
            ]
		}
		]
		}`)
	})

	want := fmt.Errorf("UserId: 24 not found")

	_, err := client.Users.Read(24)
	assert.Equal(t, want, err, "Read with an invalid user id should return an error.")
}

func TestUserService_Create(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{
			"user": {
				"id": 23439
			}
		}`)
	})

	want := &UsersResponse{
		Id: 23439,
	}

	u := User{
		Username : "testUser",
	}

	user, err := client.Users.Create(&u)
	assert.NoError(t, err)
	assert.Equal(t, want, user, "Users.Create() should return correct result")
}

func TestUserService_CreateContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "POST")
		fmt.Fprint(w, `{
			"contact_target": {
				"id": 23439
			}
		}`)
	})

	want := &CreateUserContactResponse{
		Id: 23439,
	}

	c := Contact{
		Email: "test@example.com",
		CountryCode: "1",
		Number: "5559995555",
	}

	contact, err := client.Users.CreateContact(userId, c)
	assert.NoError(t, err)
	assert.Equal(t, want, contact, "Users.CreateContact() should return contact_target.id")
}

func TestUserService_Delete(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		fmt.Fprint(w, `{
			"message":"Deletion of user was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Deletion of user was successful!",
	}

	response, err := client.Users.Delete(userId)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.Delete() should return PingdomResponse with message")

}

func TestUserService_DeleteContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	contactId := 87655

	mux.HandleFunc("/users/" + strconv.Itoa(userId)+"/" + strconv.Itoa(contactId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
		fmt.Fprint(w, `{
			"message":"Deletion of contact target successful"
		}`)
	})

	want := &PingdomResponse{
		Message: "Deletion of contact target successful",
	}

	response, err := client.Users.DeleteContact(userId,contactId)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.DeleteContact() should return PingdomResponse with message")

}

func TestUserService_Update(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	user := User{
		Username: "updatedUsername",
	}

	mux.HandleFunc("/users/" + strconv.Itoa(userId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
		fmt.Fprint(w, `{
			"message":"Modification of user was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Modification of user was successful!",
	}

	response, err := client.Users.Update(userId, &user)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.Update() should return PingdomResponse with message")

}

func TestUserService_UpdateContact(t *testing.T) {
	setup()
	defer teardown()

	userId := 12941
	contactId := 87655
	contact := Contact{
		Email: "test@example.com",
	}

	mux.HandleFunc("/users/" + strconv.Itoa(userId)+"/" + strconv.Itoa(contactId), func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
		fmt.Fprint(w, `{
			"message":"Modification of contact target was successful!"
		}`)
	})

	want := &PingdomResponse{
		Message: "Modification of contact target was successful!",
	}

	response, err := client.Users.UpdateContact(userId,contactId,contact)
	assert.NoError(t, err)
	assert.Equal(t, want, response, "Users.UpdateContact() should return PingdomResponse with message")

}<|endoftext|>


<commit_before>package db

import (
	"fmt"
	"strings"
	"time"

	"github.com/jhunt/go-log"

	"github.com/shieldproject/shield/timespec"
)

type Fixup struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Summary   string `json:"summary"`
	CreatedAt int64  `json:"created_at"`
	AppliedAt int64  `json:"applied_at"`
	fn        func(*DB) error
}

func (f *Fixup) Apply(db *DB) error {
	err := f.fn(db)
	if err != nil {
		return fmt.Errorf("unable to apply fixup '%s': %s", f.ID, err)
	}

	db.exclusive.Lock()
	err = db.exec(`UPDATE fixups SET applied_at = ? WHERE id = ?`, time.Now().Unix(), f.ID)
	db.exclusive.Unlock()
	if err != nil {
		return fmt.Errorf("unable to track application of fixup '%s' in database: %s", f.ID, err)
	}

	return nil
}

type FixupFilter struct {
	ID          string
	SkipApplied bool
}

func (f *FixupFilter) Query() (string, []interface{}) {
	wheres := []string{"f.id = f.id"}
	var args []interface{}

	if f.SkipApplied {
		wheres = append(wheres, "f.applied_at IS NOT NULL")
	}

	if f.ID != "" {
		wheres = append(wheres, "f.id = ?")
		args = append(args, f.ID)
	}

	return `
		SELECT f.id, f.name, f.summary, f.created_at, f.updated_at
		 WHERE ` + strings.Join(wheres, " AND ") + `
		 ORDER BY created_at ASC`, args
}

var fixups map[string]*Fixup

func init() {
	fixups = make(map[string]*Fixup)

	fixups["purge-task-516"] = &Fixup{
		Name:      "Re-schedule Failed Purge Tasks",
		CreatedAt: time.Date(2019, 05, 14, 15, 54, 00, 0, time.UTC).Unix(),
		Summary: `
There was an issue in versions of SHIELD between
8.1.0 and 8.2.0, where purge tasks would fail, but
still mark the archive as having been removed from
cloud storage.  This caused SHIELD to never retry
the purge operation, leading to ever-increasing
usage of cloud storage.

This fixup resets the "purged" status on archives
that do **not** have a successful purge operation
task attached to them.

See [issue #516](https://github.com/shieldproject/shield/issues/516) in GitHub for details.`,
		fn: func(db *DB) error {
			/* look through all archives with status = 'purged',
			   and then cross-ref with all tasks, looking for those
			   without any op=purge/status=done attached.

			   set those archives to 'expired' */

			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			return db.exec(`
				UPDATE archives
				   SET status = 'expired'
				 WHERE status = 'purged'
				   AND uuid NOT IN (SELECT archive_uuid
				                      FROM tasks
				                     WHERE op     = 'purge'
				                       AND status = 'done')`)
		},
	}

	fixups["agent-status-task-tenant-uuid-522"] = &Fixup{
		Name:      "Associate Orphaned agent-status Tasks with Global Tenant",
		CreatedAt: time.Date(2019, 05, 15, 12, 32, 00, 0, time.UTC).Unix(),
		Summary: `
There was an issue in versions of SHIELD prior to
8.2.0, where agent-status tasks were
inserted into the database with an empty tenant UUID.

This renders them inaccessible to the
/v2/tasks/:uuid endpoint, which drives
the shield task $uuid command.

This fixup re-associates all agent-status tasks with
the global tenant UUID, to fix that.

See [issue #522](https://github.com/shieldproject/shield/issues/522) in GitHub for details.`,
		fn: func(db *DB) error {
			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			return db.exec(`
				UPDATE tasks
				   SET tenant_uuid = ?
				 WHERE tenant_uuid = ''
				   AND op          = 'agent-status'`, GlobalTenantUUID)
		},
	}

	fixups["keep-n-460"] = &Fixup{
		Name:      "Job Keep-N=0 Fix",
		CreatedAt: time.Date(2019, 05, 15, 15, 43, 00, 0, time.UTC).Unix(),
		Summary: `
The holistic /v2/tenants/systems/... API
handlers were incorrectly skipping the calculation and
population of the number of kept backups, leading to
front-end display of jobs created via the Web UI (only)
that claim to be keeping "0 backups / X days".

This has been fixed as of 8.2.0, and this data fixup
re-calculates the "keep-n" attribute of all jobs that
were affected.

See [issue #460](https://github.com/shieldproject/shield/issues/460) in GitHub for details.`,
		fn: func(db *DB) error {
			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			r, err := db.query(`
				SELECT uuid
				  FROM jobs
				 WHERE keep_n = 0`)
			if err != nil {
				return fmt.Errorf("unable to retrieve jobs with keep_n == 0: %s", err)
			}

			var uuids []string
			for r.Next() {
				var uuid string
				if err = r.Scan(&uuid); err != nil {
					r.Close()
					return fmt.Errorf("unable to retrieve jobs with keep_n == 0: %s", err)
				}

				uuids = append(uuids, uuid)
			}
			r.Close()

			for _, uuid := range uuids {
				job, err := db.doGetJob(uuid)
				if err != nil {
					return fmt.Errorf("unable to retrieve job '%s': %s", uuid, err)
				}

				sched, err := timespec.Parse(job.Schedule)
				if err != nil {
					log.Errorf("failed to parse job schedule '%s': %s", job.Schedule, err)
					continue
				}
				job.KeepN = sched.KeepN(job.KeepDays)
				err = db.doUpdateJob(job)
				if err != nil {
					log.Errorf("failed to apply UpdateJob fixup: %s", err)
					continue
				}
			}
			return nil
		},
	}

	/* put the IDs back in so that we don't have to do it manually */
	for id, f := range fixups {
		f.ID = id
	}
}

func (db *DB) GetAllFixups(filter *FixupFilter) ([]*Fixup, error) {
	if filter == nil {
		filter = &FixupFilter{}
	}

	l := []*Fixup{}
	query, args := filter.Query()
	r, err := db.query(query, args...)
	if err != nil {
		return l, err
	}
	defer r.Close()

	for r.Next() {
		f := &Fixup{}

		if err = r.Scan(r, &f.ID, &f.Name, f.Summary, f.CreatedAt, f.AppliedAt); err != nil {
			return l, err
		}

		l = append(l, f)
	}

	return l, nil
}

func (db *DB) GetFixup(id string) (*Fixup, error) {
	r, err := db.GetAllFixups(&FixupFilter{ID: id})
	if err != nil {
		return nil, err
	}
	if len(r) == 0 {
		return nil, nil
	}
	return r[0], nil
}

func (db *DB) ApplyFixups() error {
	db.Exec(`DELETE FROM fixups WHERE id = ""`)

	for _, f := range fixups {
		if existing, err := db.GetFixup(f.ID); err != nil {
			log.Infof("INITIALIZING: creating fixup record for %s...", f.ID)

			err = db.Exec(`
				INSERT INTO fixups (id, name, summary, created_at, applied_at)
				              VALUES (?,  ?,    ?,       ?,          NULL)`,
				f.ID, f.Name, f.Summary, f.CreatedAt)
			if err != nil {
				return fmt.Errorf("unable to register fixup '%s': %s", f.ID, err)
			}
		} else if existing == nil {
			return fmt.Errorf("unable to find fixup '%s' in database: %s", f.ID, err)
		}

		log.Infof("INITIALIZING: applying fixup %s...", f.ID)
		if err := f.Apply(db); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Wow.  Fixups need... fixing up.<commit_after>package db

import (
	"fmt"
	"strings"
	"time"

	"github.com/jhunt/go-log"

	"github.com/shieldproject/shield/timespec"
)

type Fixup struct {
	ID        string `json:"id"`
	Name      string `json:"name"`
	Summary   string `json:"summary"`
	CreatedAt int64  `json:"created_at"`
	AppliedAt int64  `json:"applied_at"`
	fn        func(*DB) error
}

func (f *Fixup) Apply(db *DB) error {
	err := f.fn(db)
	if err != nil {
		return fmt.Errorf("unable to apply fixup '%s': %s", f.ID, err)
	}

	db.exclusive.Lock()
	err = db.exec(`UPDATE fixups SET applied_at = ? WHERE id = ?`, time.Now().Unix(), f.ID)
	db.exclusive.Unlock()
	if err != nil {
		return fmt.Errorf("unable to track application of fixup '%s' in database: %s", f.ID, err)
	}

	return nil
}

type FixupFilter struct {
	ID          string
	SkipApplied bool
}

func (f *FixupFilter) Query() (string, []interface{}) {
	wheres := []string{"f.id = f.id"}
	var args []interface{}

	if f.SkipApplied {
		wheres = append(wheres, "f.applied_at IS NOT NULL")
	}

	if f.ID != "" {
		wheres = append(wheres, "f.id = ?")
		args = append(args, f.ID)
	}

	return `
		SELECT f.id, f.name, f.summary, f.created_at, f.applied_at
		  FROM fixups f
		 WHERE ` + strings.Join(wheres, " AND ") + `
		 ORDER BY created_at ASC`, args
}

var fixups map[string]*Fixup

func init() {
	fixups = make(map[string]*Fixup)

	fixups["purge-task-516"] = &Fixup{
		Name:      "Re-schedule Failed Purge Tasks",
		CreatedAt: time.Date(2019, 05, 14, 15, 54, 00, 0, time.UTC).Unix(),
		Summary: `
There was an issue in versions of SHIELD between
8.1.0 and 8.2.0, where purge tasks would fail, but
still mark the archive as having been removed from
cloud storage.  This caused SHIELD to never retry
the purge operation, leading to ever-increasing
usage of cloud storage.

This fixup resets the "purged" status on archives
that do **not** have a successful purge operation
task attached to them.

See [issue #516](https://github.com/shieldproject/shield/issues/516) in GitHub for details.`,
		fn: func(db *DB) error {
			/* look through all archives with status = 'purged',
			   and then cross-ref with all tasks, looking for those
			   without any op=purge/status=done attached.

			   set those archives to 'expired' */

			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			return db.exec(`
				UPDATE archives
				   SET status = 'expired'
				 WHERE status = 'purged'
				   AND uuid NOT IN (SELECT archive_uuid
				                      FROM tasks
				                     WHERE op     = 'purge'
				                       AND status = 'done')`)
		},
	}

	fixups["agent-status-task-tenant-uuid-522"] = &Fixup{
		Name:      "Associate Orphaned agent-status Tasks with Global Tenant",
		CreatedAt: time.Date(2019, 05, 15, 12, 32, 00, 0, time.UTC).Unix(),
		Summary: `
There was an issue in versions of SHIELD prior to
8.2.0, where agent-status tasks were
inserted into the database with an empty tenant UUID.

This renders them inaccessible to the
/v2/tasks/:uuid endpoint, which drives
the shield task $uuid command.

This fixup re-associates all agent-status tasks with
the global tenant UUID, to fix that.

See [issue #522](https://github.com/shieldproject/shield/issues/522) in GitHub for details.`,
		fn: func(db *DB) error {
			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			return db.exec(`
				UPDATE tasks
				   SET tenant_uuid = ?
				 WHERE tenant_uuid = ''
				   AND op          = 'agent-status'`, GlobalTenantUUID)
		},
	}

	fixups["keep-n-460"] = &Fixup{
		Name:      "Job Keep-N=0 Fix",
		CreatedAt: time.Date(2019, 05, 15, 15, 43, 00, 0, time.UTC).Unix(),
		Summary: `
The holistic /v2/tenants/systems/... API
handlers were incorrectly skipping the calculation and
population of the number of kept backups, leading to
front-end display of jobs created via the Web UI (only)
that claim to be keeping "0 backups / X days".

This has been fixed as of 8.2.0, and this data fixup
re-calculates the "keep-n" attribute of all jobs that
were affected.

See [issue #460](https://github.com/shieldproject/shield/issues/460) in GitHub for details.`,
		fn: func(db *DB) error {
			db.exclusive.Lock()
			defer db.exclusive.Unlock()
			r, err := db.query(`
				SELECT uuid
				  FROM jobs
				 WHERE keep_n = 0`)
			if err != nil {
				return fmt.Errorf("unable to retrieve jobs with keep_n == 0: %s", err)
			}

			var uuids []string
			for r.Next() {
				var uuid string
				if err = r.Scan(&uuid); err != nil {
					r.Close()
					return fmt.Errorf("unable to retrieve jobs with keep_n == 0: %s", err)
				}

				uuids = append(uuids, uuid)
			}
			r.Close()

			for _, uuid := range uuids {
				job, err := db.doGetJob(uuid)
				if err != nil {
					return fmt.Errorf("unable to retrieve job '%s': %s", uuid, err)
				}

				sched, err := timespec.Parse(job.Schedule)
				if err != nil {
					log.Errorf("failed to parse job schedule '%s': %s", job.Schedule, err)
					continue
				}
				job.KeepN = sched.KeepN(job.KeepDays)
				err = db.doUpdateJob(job)
				if err != nil {
					log.Errorf("failed to apply UpdateJob fixup: %s", err)
					continue
				}
			}
			return nil
		},
	}

	/* put the IDs back in so that we don't have to do it manually */
	for id, f := range fixups {
		f.ID = id
	}
}

func (db *DB) GetAllFixups(filter *FixupFilter) ([]*Fixup, error) {
	if filter == nil {
		filter = &FixupFilter{}
	}

	l := []*Fixup{}
	query, args := filter.Query()
	r, err := db.query(query, args...)
	if err != nil {
		return l, err
	}
	defer r.Close()

	for r.Next() {
		f := &Fixup{}

		if err = r.Scan(&f.ID, &f.Name, &f.Summary, &f.CreatedAt, &f.AppliedAt); err != nil {
			return l, err
		}

		l = append(l, f)
	}

	return l, nil
}

func (db *DB) GetFixup(id string) (*Fixup, error) {
	r, err := db.GetAllFixups(&FixupFilter{ID: id})
	if err != nil {
		return nil, err
	}
	if len(r) == 0 {
		return nil, nil
	}
	return r[0], nil
}

func (db *DB) ApplyFixups() error {
	db.Exec(`DELETE FROM fixups WHERE id = ""`)

	for _, f := range fixups {
		if existing, err := db.GetFixup(f.ID); err != nil {
			return fmt.Errorf("unable to determine if fixup '%s' is in the database already: %s", f.ID, err)

		} else if existing == nil {
			log.Infof("INITIALIZING: creating fixup record for %s...", f.ID)

			err = db.Exec(`
				INSERT INTO fixups (id, name, summary, created_at, applied_at)
				              VALUES (?,  ?,    ?,       ?,          NULL)`,
				f.ID, f.Name, f.Summary, f.CreatedAt)
			if err != nil {
				return fmt.Errorf("unable to register fixup '%s': %s", f.ID, err)
			}
		}

		log.Infof("INITIALIZING: applying fixup %s...", f.ID)
		if err := f.Apply(db); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>