Example #1
0
// Sort chronologically sorts a resume's work and volunteer experience items.
func (rsm *Resume) Sort() {
	slice.Sort(rsm.Work, func(i, j int) bool {
		return rsm.Work[i].StartDate.Before(rsm.Work[j].StartDate.Time)
	})

	slice.Sort(rsm.Volunteer, func(i, j int) bool {
		return rsm.Volunteer[i].StartDate.Before(rsm.Volunteer[j].StartDate.Time)
	})
}
Example #2
0
func GetNotification(c web.C, w http.ResponseWriter, r *http.Request) {
	var token = r.FormValue("token")
	var userId int

	//init my empty struct who contains an array string for login user
	notifs := Notifications{}

	//check if token exist
	if CheckToken(token, w) == false {
		return
	}
	//check if token matches with an user Id
	if userId = GetUserIdWithToken(token, w); userId == -1 {
		return
	}
	a := MyAppend(GetMessageNotification(userId), GetLikeNotification(userId))
	notifs.Notif = MyAppend(a, GetVisiteNotification(userId))

	slice.Sort(notifs.Notif[:], func(i, j int) bool {
		if strings.Compare(notifs.Notif[i].CreateAt, notifs.Notif[j].CreateAt) > 0 {
			return true
		} else {
			return false
		}
	})

	notifs.Status = "OK"
	go MakeNotifAsRead(userId)
	RenderJSON(w, notifs, http.StatusOK)
}
Example #3
0
func makeChart(r opentsdb.ResponseSet, m_units map[string]string) ([]*chartSeries, error) {
	var series []*chartSeries
	for _, resp := range r {
		dps := make([][2]float64, 0)
		for k, v := range resp.DPS {
			ki, err := strconv.ParseInt(k, 10, 64)
			if err != nil {
				return nil, err
			}
			dps = append(dps, [2]float64{float64(ki), float64(v)})
		}
		if len(dps) > 0 {
			slice.Sort(dps, func(i, j int) bool {
				return dps[i][0] < dps[j][0]
			})
			name := resp.Metric
			if len(resp.Tags) > 0 {
				name += resp.Tags.String()
			}
			series = append(series, &chartSeries{
				Name:   name,
				Metric: resp.Metric,
				Tags:   resp.Tags,
				Data:   dps,
				Unit:   m_units[resp.Metric],
			})
		}
	}
	return series, nil
}
Example #4
0
// SortByCounter sorts the list by the counter of the word
func (digester *Digester) SortByCounter(order string) {
	data := digester.GetData()
	slice.Sort(data, func(i, j int) bool {
		if order == "ASC" {
			return data[i].GetCounter() <= data[j].GetCounter()
		}
		return data[i].GetCounter() > data[j].GetCounter()
	})
}
Example #5
0
func BenchmarkIntSlice(b *testing.B) {
	s := make([]int, len(intdata))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		copy(s, intdata)
		slice.Sort(s, func(i, j int) bool {
			return s[i] > s[j]
		})
	}
}
Example #6
0
func BenchmarkStringSlice(b *testing.B) {
	s := make([]string, len(strdata))
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		copy(s, strdata)
		slice.Sort(s, func(i, j int) bool {
			return s[i] > s[j]
		})
	}
}
Example #7
0
func (r Route) String() string {
	rt := []string{}
	for _, part := range r.parts {
		rt = append(rt, part.String())
	}
	slice.Sort(rt, func(i, j int) bool {
		return rt[i] < rt[j]
	})
	return fmt.Sprintf("Route(%s)", strings.Join(rt, " && "))
}
Example #8
0
func (notification *Notification) getLastNotification() SimpleNotificationList {
	v := GroupedList{}
	ret := SimpleNotificationList{}

	lastWeek := time.Now().AddDate(0, 0, -7)
	//Get from database all notification between now and a week ago, we group them by type and IdLink
	curs, _ := r.Table("notifications").
		Filter(r.Row.Field("UserId").Eq(notification.user.Id)).
		Filter(r.Row.Field("CreatedAt").Gt(lastWeek)).
		OrderBy("-CreatedAt").
		Group("Type", "IdLink").
		Run(api.Sess)
	curs.All(&v.List)

	//We sort all notification to avoid had identic notifications.

	var arraySimpleNotification []SimpleNotification
	for _, group := range v.List {

		simple := SimpleNotification{
			Type:   group.Group[0].(string),
			IdLink: group.Group[1].(string),
			Read:   true,
		}
		var create time.Time
		var index = -1
		for j, notif := range group.Reduction {

			if notif.Read == false {
				simple.Read = false
			}
			if create.Before(notif.CreatedAt) == true {
				create = notif.CreatedAt
				index = j
			}
		}
		simple.CreatedAt = create
		simple.Name = group.Reduction[index].Name
		simple.Others = len(group.Reduction) - 1
		arraySimpleNotification = append(arraySimpleNotification, simple)
	}
	//Sort by createDate
	slice.Sort(arraySimpleNotification[:], func(i, j int) bool {
		return arraySimpleNotification[i].CreatedAt.After(arraySimpleNotification[j].CreatedAt)
	})

	ret.List = arraySimpleNotification
	return ret
}
Example #9
0
File: cmds.go Project: hiroapp/cli
func cmdSummary(d db.DB, periodS, firstDayS string) {
	period, err := datetime.ParsePeriod(periodS)
	if err != nil {
		fatal(err)
	}
	firstDay, err := datetime.ParseWeekday(firstDayS)
	if err != nil {
		fatal(err)
	}
	categories, err := d.Categories()
	if err != nil {
		fatal(err)
	}
	itr, err := NewSummaryIterator(d, period, firstDay, time.Now())
	if err != nil {
		fatal(err)
	}
	defer itr.Close()
	for {
		if summary, err := itr.Next(); err == io.EOF {
			break
		} else if err != nil {
			fatal(err)
		} else {
			fmt.Printf("%s\n\n", PeriodHeadline(summary.From, summary.To, period))
			names := make(map[string]string)
			order := make([]string, 0, len(summary.Categories))
			for id, _ := range summary.Categories {
				names[id] = FormatCategory(categories.Path(id))
				order = append(order, id)
			}
			slice.Sort(order, func(i, j int) bool {
				return summary.Categories[order[i]] > summary.Categories[order[j]]
			})
			t := table.New().Padding(" ")
			for _, id := range order {
				d := FormatDuration(summary.Categories[id])
				t.Add(table.String(names[id]), table.String(d).Align(table.Right))
			}
			fmt.Printf("%s\n", Indent(t.String(), "  "))
		}
	}
}
Example #10
0
// Available return the migrations in the environment's directory sorted in
// ascending lexicographic order.
func (s Service) Available() ([]*Migration, error) {
	files, _ := filepath.Glob(filepath.Join(s.env.Directory, "*.sql")) // The only possible error here is a pattern error

	var migrations []*Migration
	for _, file := range files {
		migration, err := NewMigration(file)
		if err != nil {
			return nil, err
		}

		migrations = append(migrations, migration)
	}

	slice.Sort(migrations, func(i, j int) bool {
		return migrations[i].Name < migrations[j].Name
	})

	return migrations, nil
}
Example #11
0
// Tree returns the root node of the category tree.
func (c CategoryMap) Root() *CategoryNode {
	index := map[string]*CategoryNode{"": &CategoryNode{}}
	for _, category := range c {
		parent := index[category.ParentID]
		if parent == nil {
			parent = &CategoryNode{}
			index[category.ParentID] = parent
		}
		node := index[category.ID]
		if node == nil {
			node = &CategoryNode{}
			index[category.ID] = node
		}
		node.Category = category
		parent.Children = append(parent.Children, node)
		slice.Sort(parent.Children, func(i, j int) bool {
			return parent.Children[i].Name < parent.Children[j].Name
		})
	}
	return index[""]
}
Example #12
0
func (s *Schedule) ExprGraph(t miniprofiler.Timer, unit string, res []*expr.Result) (chart.Chart, error) {
	c := chart.ScatterChart{
		Key:    chart.Key{Pos: "itl"},
		YRange: chart.Range{Label: unit},
	}
	c.XRange.Time = true
	for ri, r := range res {
		rv := r.Value.(expr.Series)
		pts := make([]chart.EPoint, len(rv))
		idx := 0
		for k, v := range rv {
			pts[idx].X = float64(k.Unix())
			pts[idx].Y = v
			idx++
		}
		slice.Sort(pts, func(i, j int) bool {
			return pts[i].X < pts[j].X
		})
		c.AddData(r.Group.String(), pts, chart.PlotStyleLinesPoints, Autostyle(ri))
	}
	return &c, nil
}
Example #13
0
// Applied return the migrations in the environment's directory that are marked
// as applied in the database sorted in ascending lexicographic order.
func (s Service) Applied() ([]*Migration, error) {
	files, err := s.conn.GetApplied()
	if err != nil {
		return nil, err
	}

	var migrations []*Migration
	for _, file := range files {
		migration, err := NewMigration(filepath.Join(s.env.Directory, file))
		if err != nil {
			return nil, err
		}

		migrations = append(migrations, migration)
	}

	slice.Sort(migrations, func(i, j int) bool {
		return migrations[i].Name < migrations[j].Name
	})

	return migrations, nil
}
Example #14
0
// GET /points/nearest?lat=*&lng=*&dist=_
func getNearestPoints(w http.ResponseWriter, r *http.Request) {
	badRequestError := Response{"error", 400, "Bad Request", nil}
	params := r.URL.Query()

	lat, err1 := strconv.ParseFloat(params.Get("lat"), 64)
	lng, err2 := strconv.ParseFloat(params.Get("lng"), 64)

	if err1 != nil || err2 != nil {
		routes.ServeJson(w, badRequestError)
		return
	}

	maxDistance := float64(50)
	if d, err := strconv.ParseFloat(params.Get("dist"), 32); err == nil {
		maxDistance = d
	}

	currentPoint := geo.NewPoint(lat, lng)

	var p Point
	result := make([]distanceRecord, 0)
	iter := Points.Find(bson.M{}).Iter()
	for iter.Next(&p) {
		lat, _ := strconv.ParseFloat(p.Latitude, 64)
		lng, _ := strconv.ParseFloat(p.Longitude, 64)

		point := geo.NewPoint(lat, lng)
		distance := currentPoint.GreatCircleDistance(point) * 1000

		if distance <= maxDistance {
			result = append(result, distanceRecord{p.Id, distance})
		}
	}

	slice.Sort(result, func(i, j int) bool {
		return result[i].Distance < result[j].Distance
	})
	routes.ServeJson(w, result)
}
Example #15
0
func GatherStats(config models.Configuration) (stats []models.Status) {
	stats = []models.Status{}
	for _, server := range config.Servers {
		pingSuccess := testPing(server.Hostname, false, config)

		var httpSuccess bool
		if server.Url != "" {
			httpSuccess = testHttp(server.Url, config)
		}

		status := models.Status{
			Name:        server.Name,
			HttpPresent: server.Url != "",
			HttpSuccess: httpSuccess,
			PingSuccess: pingSuccess,
			Created:     time.Now(),
		}
		stats = append(stats, status)
	}
	slice.Sort(stats[:], func(i, j int) bool {
		return stats[i].Name < stats[j].Name
	})
	return stats
}
Example #16
0
// Graph takes an OpenTSDB request data structure and queries OpenTSDB. Use the
// json parameter to pass JSON. Use the b64 parameter to pass base64-encoded
// JSON.
func Graph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	j := []byte(r.FormValue("json"))
	if bs := r.FormValue("b64"); bs != "" {
		b, err := base64.StdEncoding.DecodeString(bs)
		if err != nil {
			return nil, err
		}
		j = b
	}
	if len(j) == 0 {
		return nil, fmt.Errorf("either json or b64 required")
	}
	oreq, err := opentsdb.RequestFromJSON(j)
	if err != nil {
		return nil, err
	}
	if ads_v := r.FormValue("autods"); ads_v != "" {
		ads_i, err := strconv.Atoi(ads_v)
		if err != nil {
			return nil, err
		}
		if err := oreq.AutoDownsample(ads_i); err != nil {
			return nil, err
		}
	}
	ar := make(map[int]bool)
	for _, v := range r.Form["autorate"] {
		if i, err := strconv.Atoi(v); err == nil {
			ar[i] = true
		}
	}
	queries := make([]string, len(oreq.Queries))
	var start, end string
	var startT, endT time.Time
	if s, ok := oreq.Start.(string); ok && strings.Contains(s, "-ago") {
		startT, err = opentsdb.ParseTime(s)
		if err != nil {
			return nil, err
		}
		start = strings.TrimSuffix(s, "-ago")
	}
	if s, ok := oreq.End.(string); ok && strings.Contains(s, "-ago") {
		endT, err = opentsdb.ParseTime(s)
		if err != nil {
			return nil, err
		}
		end = strings.TrimSuffix(s, "-ago")
	}
	if start == "" && end == "" {
		s, sok := oreq.Start.(int64)
		e, eok := oreq.End.(int64)
		if sok && eok {
			start = fmt.Sprintf("%vs", e-s)
			startT = time.Unix(s, 0)
			endT = time.Unix(e, 0)
			if err != nil {
				return nil, err
			}
		}
	}
	if endT.Equal(time.Time{}) {
		endT = time.Now().UTC()
	}
	m_units := make(map[string]string)
	for i, q := range oreq.Queries {
		if ar[i] {

			meta, err := schedule.MetadataMetrics(q.Metric)
			if err != nil {
				return nil, err
			}
			if meta == nil {
				return nil, fmt.Errorf("no metadata for %s: cannot use auto rate", q)
			}
			if meta.Unit != "" {
				m_units[q.Metric] = meta.Unit
			}
			if meta.Rate != "" {
				switch meta.Rate {
				case metadata.Gauge:
					// ignore
				case metadata.Rate:
					q.Rate = true
				case metadata.Counter:
					q.Rate = true
					q.RateOptions = opentsdb.RateOptions{
						Counter:    true,
						ResetValue: 1,
					}
				default:
					return nil, fmt.Errorf("unknown metadata rate: %s", meta.Rate)
				}
			}
		}
		queries[i] = fmt.Sprintf(`q("%v", "%v", "%v")`, q, start, end)
		if !schedule.SystemConf.GetTSDBContext().Version().FilterSupport() {
			if err := schedule.Search.Expand(q); err != nil {
				return nil, err
			}
		}
	}
	var tr opentsdb.ResponseSet
	b, _ := json.MarshalIndent(oreq, "", "  ")
	t.StepCustomTiming("tsdb", "query", string(b), func() {
		h := schedule.SystemConf.GetTSDBHost()
		if h == "" {
			err = fmt.Errorf("tsdbHost not set")
			return
		}
		tr, err = oreq.Query(h)
	})
	if err != nil {
		return nil, err
	}
	cs, err := makeChart(tr, m_units)
	if err != nil {
		return nil, err
	}
	if _, present := r.Form["png"]; present {
		c := chart.ScatterChart{
			Title: fmt.Sprintf("%v - %v", oreq.Start, queries),
		}
		c.XRange.Time = true
		if min, err := strconv.ParseFloat(r.FormValue("min"), 64); err == nil {
			c.YRange.MinMode.Fixed = true
			c.YRange.MinMode.Value = min
		}
		if max, err := strconv.ParseFloat(r.FormValue("max"), 64); err == nil {
			c.YRange.MaxMode.Fixed = true
			c.YRange.MaxMode.Value = max
		}
		for ri, r := range cs {
			pts := make([]chart.EPoint, len(r.Data))
			for idx, v := range r.Data {
				pts[idx].X = v[0]
				pts[idx].Y = v[1]
			}
			slice.Sort(pts, func(i, j int) bool {
				return pts[i].X < pts[j].X
			})
			c.AddData(r.Name, pts, chart.PlotStyleLinesPoints, sched.Autostyle(ri))
		}
		w.Header().Set("Content-Type", "image/svg+xml")
		white := color.RGBA{0xff, 0xff, 0xff, 0xff}
		const width = 800
		const height = 600
		s := svg.New(w)
		s.Start(width, height)
		s.Rect(0, 0, width, height, "fill: #ffffff")
		sgr := svgg.AddTo(s, 0, 0, width, height, "", 12, white)
		c.Plot(sgr)
		s.End()
		return nil, nil
	}
	var a []annotate.Annotation
	warnings := []string{}
	if schedule.SystemConf.AnnotateEnabled() {
		a, err = annotateBackend.GetAnnotations(&startT, &endT)
		if err != nil {
			warnings = append(warnings, fmt.Sprintf("unable to get annotations: %v", err))
		}
	}
	return struct {
		Queries     []string
		Series      []*chartSeries
		Annotations []annotate.Annotation
		Warnings    []string
	}{
		queries,
		cs,
		a,
		warnings,
	}, nil
}
Example #17
0
func reverse(service Servicer, all bool) error {
	initialized, err := service.Initialized()
	if err != nil {
		return fmt.Errorf("unable to check for database state: %s", err)
	}

	if !initialized {
		return fmt.Errorf("uninitialized database")
	}

	available, err := service.Available()
	if err != nil {
		return fmt.Errorf("unable to retrieve available migrations: %s", err)
	}

	applied, err := service.Applied()
	if err != nil {
		return fmt.Errorf("unable to retrieve applied migrations: %s", err)
	}

	if len(applied) == 0 {
		return nil
	}

	var i, j = len(available) - 1, len(applied) - 1
	for i >= 0 && j >= 0 && available[i].Name > applied[j].Name {
		i--
	}

	for i >= 0 && j >= 0 {
		if available[i].Name == applied[j].Name {
			i--
			j--
			continue
		}

		if available[i].Name < applied[j].Name {
			return fmt.Errorf("missing migration: %s", applied[j].Name)
		}

		if available[i].Name > applied[j].Name {
			return fmt.Errorf("out of order migration: %s", available[i].Name)
		}
	}

	if i >= 0 {
		return fmt.Errorf("out of order migration: %s", available[i].Name)
	}

	if j >= 0 {
		return fmt.Errorf("missing migration: %s", applied[j].Name)
	}

	slice.Sort(applied, func(i, j int) bool {
		return applied[i].Name > applied[j].Name
	})

	for _, migration := range applied {
		err := service.Reverse(migration)
		if err != nil {
			return err
		}

		if !all {
			return nil
		}
	}

	return nil
}
Example #18
0
func (s *Schedule) MarshalGroups(T miniprofiler.Timer, filter string) (*StateGroups, error) {
	var silenced SilenceTester
	T.Step("Silenced", func(miniprofiler.Timer) {
		silenced = s.Silenced()
	})
	var groups map[StateTuple]States
	var err error
	status := make(States)
	t := StateGroups{
		TimeAndDate: s.SystemConf.GetTimeAndDate(),
	}
	t.FailingAlerts, t.UnclosedErrors = s.getErrorCounts()
	T.Step("Setup", func(miniprofiler.Timer) {
		status2, err2 := s.GetOpenStates()
		if err2 != nil {
			err = err2
			return
		}
		var parsedExpr *boolq.Tree
		parsedExpr, err2 = boolq.Parse(filter)
		if err2 != nil {
			err = err2
			return
		}
		for k, v := range status2 {
			a := s.RuleConf.GetAlert(k.Name())
			if a == nil {
				slog.Errorf("unknown alert %s. Force closing.", k.Name())
				if err2 = s.ActionByAlertKey("bosun", "closing because alert doesn't exist.", models.ActionForceClose, k); err2 != nil {
					slog.Error(err2)
				}
				continue
			}
			is, err2 := MakeIncidentSummary(s.RuleConf, silenced, v)
			if err2 != nil {
				err = err2
				return
			}
			match := false
			match, err2 = boolq.AskParsedExpr(parsedExpr, is)
			if err2 != nil {
				err = err2
				return
			}
			if match {
				status[k] = v
			}
		}
	})
	if err != nil {
		return nil, err
	}
	T.Step("GroupStates", func(T miniprofiler.Timer) {
		groups = status.GroupStates(silenced)
	})
	T.Step("groups", func(T miniprofiler.Timer) {
		for tuple, states := range groups {
			var grouped []*StateGroup
			switch tuple.Status {
			case models.StWarning, models.StCritical, models.StUnknown:
				var sets map[string]models.AlertKeys
				T.Step(fmt.Sprintf("GroupSets (%d): %v", len(states), tuple), func(T miniprofiler.Timer) {
					sets = states.GroupSets(s.SystemConf.GetMinGroupSize())
				})
				for name, group := range sets {
					g := StateGroup{
						Active:        tuple.Active,
						Status:        tuple.Status,
						CurrentStatus: tuple.CurrentStatus,
						Silenced:      tuple.Silenced,
						Subject:       fmt.Sprintf("%s - %s", tuple.Status, name),
					}
					for _, ak := range group {
						st := status[ak]
						st.Body = ""
						st.EmailBody = nil
						st.Attachments = nil
						g.Children = append(g.Children, &StateGroup{
							Active:   tuple.Active,
							Status:   tuple.Status,
							Silenced: tuple.Silenced,
							AlertKey: ak,
							Alert:    ak.Name(),
							Subject:  string(st.Subject),
							Ago:      marshalTime(st.Last().Time),
							State:    st,
							IsError:  !s.AlertSuccessful(ak.Name()),
						})
					}
					if len(g.Children) == 1 && g.Children[0].Subject != "" {
						g.Subject = g.Children[0].Subject
					}
					grouped = append(grouped, &g)
				}
			default:
				continue
			}
			if tuple.NeedAck {
				t.Groups.NeedAck = append(t.Groups.NeedAck, grouped...)
			} else {
				t.Groups.Acknowledged = append(t.Groups.Acknowledged, grouped...)
			}
		}
	})
	T.Step("sort", func(T miniprofiler.Timer) {
		gsort := func(grp []*StateGroup) func(i, j int) bool {
			return func(i, j int) bool {
				a := grp[i]
				b := grp[j]
				if a.Active && !b.Active {
					return true
				} else if !a.Active && b.Active {
					return false
				}
				if a.Status != b.Status {
					return a.Status > b.Status
				}
				if a.AlertKey != b.AlertKey {
					return a.AlertKey < b.AlertKey
				}
				return a.Subject < b.Subject
			}
		}
		slice.Sort(t.Groups.NeedAck, gsort(t.Groups.NeedAck))
		slice.Sort(t.Groups.Acknowledged, gsort(t.Groups.Acknowledged))
	})
	return &t, nil
}
Example #19
0
func (srv *Server) commands() {
	srv.state = stateStop
	var next, stop, tick, play, pause, prev func()
	var timer <-chan time.Time
	waiters := make(map[*websocket.Conn]chan struct{})
	broadcastData := func(wd *waitData) {
		for ws := range waiters {
			go func(ws *websocket.Conn) {
				if err := websocket.JSON.Send(ws, wd); err != nil {
					srv.ch <- cmdDeleteWS(ws)
				}
			}(ws)
		}
	}
	broadcast := func(wt waitType) {
		wd := srv.makeWaitData(wt)
		broadcastData(wd)
	}
	broadcastErr := func(err error) {
		printErr(err)
		v := struct {
			Time  time.Time
			Error string
		}{
			time.Now().UTC(),
			err.Error(),
		}
		broadcastData(&waitData{
			Type: waitError,
			Data: v,
		})
	}
	newWS := func(c cmdNewWS) {
		ws := (*websocket.Conn)(c.ws)
		waiters[ws] = c.done
		inits := []waitType{
			waitPlaylist,
			waitProtocols,
			waitStatus,
			waitTracks,
		}
		for _, wt := range inits {
			data := srv.makeWaitData(wt)
			go func() {
				if err := websocket.JSON.Send(ws, data); err != nil {
					srv.ch <- cmdDeleteWS(ws)
					return
				}
			}()
		}
	}
	deleteWS := func(c cmdDeleteWS) {
		ws := (*websocket.Conn)(c)
		ch := waiters[ws]
		if ch == nil {
			return
		}
		close(ch)
		delete(waiters, ws)
	}
	prev = func() {
		log.Println("prev")
		srv.PlaylistIndex--
		if srv.elapsed < time.Second*3 {
			srv.PlaylistIndex--
		}
		if srv.PlaylistIndex < 0 {
			srv.PlaylistIndex = 0
		}
		next()
	}
	pause = func() {
		log.Println("pause")
		switch srv.state {
		case statePause, stateStop:
			log.Println("pause: resume")
			srv.audioch <- audioPlay{}
			srv.state = statePlay
		case statePlay:
			log.Println("pause: pause")
			srv.audioch <- audioStop{}
			srv.state = statePause
		}
	}
	next = func() {
		log.Println("next")
		stop()
		play()
	}
	var forceNext = false
	stop = func() {
		log.Println("stop")
		srv.state = stateStop
		srv.audioch <- audioStop{}
		if srv.song != nil || forceNext {
			if srv.Random && len(srv.Queue) > 1 {
				n := srv.PlaylistIndex
				for n == srv.PlaylistIndex {
					n = rand.Intn(len(srv.Queue))
				}
				srv.PlaylistIndex = n
			} else {
				srv.PlaylistIndex++
			}
		}
		forceNext = false
		srv.song = nil
		srv.elapsed = 0
	}
	var inst protocol.Instance
	var sid SongID
	sendNext := func() {
		go func() {
			srv.ch <- cmdNext
		}()
	}
	nextOpen := time.After(0)
	tick = func() {
		const expected = 4096
		if false && srv.elapsed > srv.info.Time {
			log.Println("elapsed time completed", srv.elapsed, srv.info.Time)
			stop()
		}
		if srv.song == nil {
			<-nextOpen
			nextOpen = time.After(time.Second / 2)
			defer broadcast(waitStatus)
			if len(srv.Queue) == 0 {
				log.Println("empty queue")
				stop()
				return
			}
			if srv.PlaylistIndex >= len(srv.Queue) {
				if srv.Repeat {
					srv.PlaylistIndex = 0
				} else {
					log.Println("end of queue", srv.PlaylistIndex, len(srv.Queue))
					stop()
					return
				}
			}

			srv.songID = srv.Queue[srv.PlaylistIndex]
			sid = srv.songID
			if info, err := srv.getSong(sid); err != nil {
				broadcastErr(err)
				forceNext = true
				sendNext()
				return
			} else {
				srv.info = *info
			}
			inst = srv.Protocols[sid.Protocol()][sid.Key()]
			song, err := inst.GetSong(sid.ID())
			if err != nil {
				forceNext = true
				broadcastErr(err)
				sendNext()
				return
			}
			srv.song = song
			sr, ch, err := srv.song.Init()
			if err != nil {
				srv.song.Close()
				srv.song = nil
				broadcastErr(err)
				sendNext()
				return
			}
			params := audioSetParams{
				sr:   sr,
				ch:   ch,
				dur:  srv.info.Time,
				play: srv.song.Play,
				err:  make(chan error),
			}
			srv.audioch <- params
			if err := <-params.err; err != nil {
				broadcastErr(err)
				sendNext()
				return
			}
			srv.elapsed = 0
			log.Println("playing", srv.info.Title, sr, ch)
			srv.state = statePlay
		}
	}
	infoTimer := func() {
		timer = time.After(time.Second)
		if inst == nil {
			return
		}
		// Check for updated song info.
		if info, err := inst.Info(sid.ID()); err != nil {
			broadcastErr(err)
		} else if srv.info != *info {
			srv.info = *info
			broadcast(waitStatus)
		}
	}
	restart := func() {
		log.Println("attempting to restart song")
		n := srv.PlaylistIndex
		stop()
		srv.PlaylistIndex = n
		play()
	}
	play = func() {
		log.Println("play")
		if srv.PlaylistIndex > len(srv.Queue) {
			srv.PlaylistIndex = 0
		}
		tick()
	}
	playIdx := func(c cmdPlayIdx) {
		stop()
		srv.PlaylistIndex = int(c)
		play()
	}
	playTrack := func(c cmdPlayTrack) {
		t := SongID(c)
		info, err := srv.getSong(t)
		if err != nil {
			broadcastErr(err)
			return
		}
		album := info.Album
		p, err := srv.getInstance(t.Protocol(), t.Key())
		if err != nil {
			broadcastErr(err)
			return
		}
		list, err := p.List()
		if err != nil {
			broadcastErr(err)
			return
		}
		top := codec.NewID(t.Protocol(), t.Key())
		var ids []codec.ID
		for id, si := range list {
			if si.Album == album {
				ids = append(ids, id)
			}
		}
		slice.Sort(ids, func(i, j int) bool {
			a := list[ids[i]]
			b := list[ids[j]]
			return a.Track < b.Track
		})
		plc := PlaylistChange{[]string{"clear"}}
		for _, v := range ids {
			plc = append(plc, []string{"add", string(top.Push(string(v)))})
		}
		n, _, err := srv.playlistChange(srv.Queue, plc)
		if err != nil {
			broadcastErr(err)
			return
		}
		stop()
		srv.Queue = n
		srv.PlaylistIndex = 0
		for i, s := range srv.Queue {
			if s == t {
				srv.PlaylistIndex = i
			}
		}
		play()
		broadcast(waitPlaylist)
	}
	removeDeleted := func() {
		for n, p := range srv.Playlists {
			p = srv.removeDeleted(p)
			if len(p) == 0 {
				delete(srv.Playlists, n)
			} else {
				srv.Playlists[n] = p
			}
		}
		srv.Queue = srv.removeDeleted(srv.Queue)
		if info, _ := srv.getSong(srv.songID); info == nil {
			playing := srv.state == statePlay
			stop()
			if playing {
				srv.PlaylistIndex = 0
				play()
			}
		}
		broadcast(waitPlaylist)
	}
	protocolRemove := func(c cmdProtocolRemove) {
		prots, ok := srv.Protocols[c.protocol]
		if !ok {
			return
		}
		delete(prots, c.key)
		if srv.Token != "" {
			d := models.Delete{
				Protocol: c.protocol,
				Name:     c.key,
			}
			go func() {
				r, err := srv.request("/api/source/delete", &d)
				if err != nil {
					srv.ch <- cmdError(err)
					return
				}
				r.Close()
			}()
		}
		broadcast(waitTracks)
		broadcast(waitProtocols)
	}
	removeInProgress := func(c cmdRemoveInProgress) {
		delete(srv.inprogress, codec.ID(c))
		broadcast(waitProtocols)
	}
	protocolAdd := func(c cmdProtocolAdd) {
		name, key := c.Name, c.Instance.Key()
		id := codec.NewID(name, key)
		if srv.inprogress[id] {
			broadcastErr(fmt.Errorf("already adding %s: %s", name, key))
			return
		}
		if _, err := srv.getInstance(name, key); err == nil {
			broadcastErr(fmt.Errorf("already have %s: %s", name, key))
			return
		}
		srv.inprogress[id] = true
		broadcast(waitProtocols)
		go func() {
			defer func() {
				srv.ch <- cmdRemoveInProgress(id)
			}()
			songs, err := c.Instance.Refresh()
			if err != nil {
				srv.ch <- cmdError(err)
				return
			}
			for k, v := range songs {
				if v.Time > 0 && v.Time < srv.MinDuration {
					delete(songs, k)
				}
			}
			srv.ch <- cmdProtocolAddInstance(c)
		}()
	}
	protocolAddInstance := func(c cmdProtocolAddInstance) {
		srv.Protocols[c.Name][c.Instance.Key()] = c.Instance
		if srv.Token != "" {
			srv.ch <- cmdPutSource{
				protocol: c.Name,
				key:      c.Instance.Key(),
			}
		}
		broadcast(waitTracks)
		broadcast(waitProtocols)
	}
	queueChange := func(c cmdQueueChange) {
		n, clear, err := srv.playlistChange(srv.Queue, PlaylistChange(c))
		if err != nil {
			broadcastErr(err)
			return
		}
		srv.Queue = n
		if clear || len(n) == 0 {
			stop()
			srv.PlaylistIndex = 0
		}
		broadcast(waitPlaylist)
	}
	playlistChange := func(c cmdPlaylistChange) {
		p := srv.Playlists[c.name]
		n, _, err := srv.playlistChange(p, c.plc)
		if err != nil {
			broadcastErr(err)
			return
		}
		if len(n) == 0 {
			delete(srv.Playlists, c.name)
		} else {
			srv.Playlists[c.name] = n
		}
		broadcast(waitPlaylist)
	}
	queueSave := func() {
		if srv.savePending {
			return
		}
		srv.savePending = true
		time.AfterFunc(time.Second, func() {
			srv.ch <- cmdDoSave{}
		})
	}
	doSave := func() {
		if err := srv.save(); err != nil {
			broadcastErr(err)
		}
	}
	addOAuth := func(c cmdAddOAuth) {
		go func() {
			prot, err := protocol.ByName(c.name)
			if err != nil {
				c.done <- err
				return
			}
			t, err := prot.OAuth.Exchange(oauth2.NoContext, c.r.FormValue("code"))
			if err != nil {
				c.done <- err
				return
			}
			// "Bearer" was added for dropbox. It happens to work also with Google Music's
			// OAuth. This may need to be changed to be protocol-specific in the future.
			t.TokenType = "Bearer"
			instance, err := prot.NewInstance(nil, t)
			if err != nil {
				c.done <- err
				return
			}
			srv.ch <- cmdProtocolAdd{
				Name:     c.name,
				Instance: instance,
			}
			c.done <- nil
		}()
	}
	setMinDuration := func(c cmdMinDuration) {
		srv.MinDuration = time.Duration(c)
	}
	doSeek := func(c cmdSeek) {
		if time.Duration(c) > srv.info.Time {
			return
		}
		srv.audioch <- c
	}
	setUsername := func(c cmdSetUsername) {
		srv.Username = string(c)
	}
	makeSource := func(protocol, key string) ([]*models.Source, error) {
		// name and key may be empty to match all
		var ss []*models.Source
		for prot, m := range srv.Protocols {
			if prot != protocol && protocol != "" {
				continue
			}
			for name, p := range m {
				if name != key && key != "" {
					continue
				}
				buf := new(bytes.Buffer)
				gw := gzip.NewWriter(buf)
				if err := gob.NewEncoder(gw).Encode(p); err != nil {
					return nil, err
				}
				if err := gw.Close(); err != nil {
					return nil, err
				}
				ss = append(ss, &models.Source{
					Protocol: prot,
					Name:     name,
					Blob:     buf.Bytes(),
				})
			}
		}
		return ss, nil
	}
	sendSource := func(ss []*models.Source) error {
		r, err := srv.request("/api/source/set", &ss)
		if err != nil {
			return err
		}
		r.Close()
		return nil
	}
	putSource := func(c cmdPutSource) {
		ss, err := makeSource(c.protocol, c.key)
		if err != nil {
			broadcastErr(fmt.Errorf("could not set sources: %v", err))
			return
		}
		if err := sendSource(ss); err != nil {
			broadcastErr(fmt.Errorf("could not set sources: %v", err))
		}
	}
	tokenRegister := func(c cmdTokenRegister) {
		srv.Token = ""
		if c != "" {
			srv.Token = string(c)
			ss, err := makeSource("", "")
			if err != nil {
				broadcastErr(err)
				return
			}
			go func() {
				if err := sendSource(ss); err != nil {
					srv.ch <- cmdError(err)
					return
				}
				r, err := srv.request("/api/source/get", nil)
				if err != nil {
					srv.ch <- cmdError(fmt.Errorf("could not get sources: %v", err))
					return
				}
				defer r.Close()
				if err := json.NewDecoder(r).Decode(&ss); err != nil {
					srv.ch <- cmdError(err)
					return
				}
				srv.ch <- cmdSetSources(ss)
			}()
		}
		go func() {
			srv.ch <- cmdSetUsername("")
			if c == "" {
				return
			}
			r, err := srv.request("/api/username", nil)
			if err != nil {
				srv.ch <- cmdError(err)
				return
			}
			defer r.Close()
			var u cmdSetUsername
			if err := json.NewDecoder(r).Decode(&u); err != nil {
				srv.ch <- cmdError(err)
				return
			}
			srv.ch <- u
		}()
	}
	setSources := func(c cmdSetSources) {
		ps := protocol.Map()
		for _, s := range c {
			proto, err := protocol.ByName(s.Protocol)
			if err != nil {
				broadcastErr(err)
				return
			}
			if _, ok := ps[s.Protocol]; !ok {
				ps[s.Protocol] = make(map[string]protocol.Instance)
			}
			r, err := gzip.NewReader(bytes.NewReader(s.Blob))
			if err != nil {
				broadcastErr(err)
				return
			}
			defer r.Close()
			p, err := proto.Decode(r)
			if err != nil {
				broadcastErr(err)
				return
			}
			ps[s.Protocol][s.Name] = p
		}
		srv.Protocols = ps
		go func() {
			// protocolRefresh uses srv.Protocols, so
			for i, s := range c {
				last := i == len(c)-1
				srv.ch <- cmdProtocolRefresh{
					protocol: s.Protocol,
					key:      s.Name,
					list:     true,
					doDelete: last,
					err:      make(chan error, 1),
				}
			}
		}()
	}
	sendWaitData := func(c cmdWaitData) {
		c.done <- srv.makeWaitData(c.wt)
	}
	isRefreshing := make(map[codec.ID]bool)
	protocolRefresh := func(c cmdProtocolRefresh) {
		id := codec.NewID(c.protocol, c.key)
		if isRefreshing[id] {
			c.err <- nil
			return
		}
		inst, err := srv.getInstance(c.protocol, c.key)
		if err != nil {
			c.err <- err
			return
		}
		isRefreshing[id] = true
		go func() {
			defer func() {
				delete(isRefreshing, id)
			}()
			f := inst.Refresh
			if c.list {
				f = inst.List
			}
			songs, err := f()
			if err != nil {
				c.err <- err
				return
			}
			for k, v := range songs {
				if v.Time > 0 && v.Time < srv.MinDuration {
					delete(songs, k)
				}
			}
			if c.doDelete {
				srv.ch <- cmdRemoveDeleted{}
			}
			if srv.Token != "" {
				srv.ch <- cmdPutSource{
					protocol: c.protocol,
					key:      c.key,
				}
			}
			c.err <- nil
		}()
	}
	ch := make(chan interface{})
	go func() {
		for c := range srv.ch {
			go func(c interface{}) {
				timer := time.AfterFunc(time.Second*10, func() {
					log.Printf("%T: %#v\n", c, c)
					panic("delay timer expired")
				})
				ch <- c
				timer.Stop()
			}(c)
		}
	}()
	infoTimer()
	for {
		select {
		case <-timer:
			infoTimer()
		case c := <-ch:
			if c, ok := c.(cmdSetTime); ok {
				d := time.Duration(c)
				change := srv.elapsed - d
				if change < 0 {
					change = -change
				}
				srv.elapsed = d
				if change > time.Second {
					broadcast(waitStatus)
				}
				continue
			}
			save := true
			log.Printf("%T\n", c)
			switch c := c.(type) {
			case controlCmd:
				switch c {
				case cmdPlay:
					save = false
					play()
				case cmdStop:
					save = false
					stop()
				case cmdNext:
					next()
				case cmdPause:
					save = false
					pause()
				case cmdPrev:
					prev()
				case cmdRandom:
					srv.Random = !srv.Random
				case cmdRepeat:
					srv.Repeat = !srv.Repeat
				case cmdRestartSong:
					restart()
				default:
					panic(c)
				}
			case cmdPlayIdx:
				playIdx(c)
			case cmdPlayTrack:
				playTrack(c)
			case cmdProtocolRemove:
				protocolRemove(c)
			case cmdQueueChange:
				queueChange(c)
			case cmdPlaylistChange:
				playlistChange(c)
			case cmdNewWS:
				save = false
				newWS(c)
			case cmdDeleteWS:
				save = false
				deleteWS(c)
			case cmdDoSave:
				save = false
				doSave()
			case cmdAddOAuth:
				addOAuth(c)
			case cmdSeek:
				doSeek(c)
				save = false
			case cmdMinDuration:
				setMinDuration(c)
			case cmdTokenRegister:
				tokenRegister(c)
			case cmdSetUsername:
				setUsername(c)
			case cmdSetSources:
				setSources(c)
			case cmdProtocolAdd:
				protocolAdd(c)
			case cmdProtocolAddInstance:
				protocolAddInstance(c)
			case cmdRemoveDeleted:
				removeDeleted()
			case cmdRemoveInProgress:
				removeInProgress(c)
			case cmdError:
				broadcastErr(error(c))
				save = false
			case cmdWaitData:
				sendWaitData(c)
				save = false
			case cmdPutSource:
				putSource(c)
				save = false
			case cmdProtocolRefresh:
				protocolRefresh(c)
			default:
				panic(c)
			}
			broadcast(waitStatus)
			if save {
				queueSave()
			}
		}
	}
}
Example #20
0
// Print the character sheet on the screen
func (c *Character) Print() {
	// Print the name
	fmt.Printf("%s\t%s\n", theme.Title("Name"), c.Name)

	// Print the backgrounds
	backgrounds := []Background{}

	for _, background := range c.Backgrounds {
		backgrounds = append(backgrounds, background)
	}

	slice.Sort(backgrounds, func(i, j int) bool {
		if backgrounds[i].Type != backgrounds[j].Type {
			return backgrounds[i].Type < backgrounds[j].Type
		}

		return backgrounds[i].Name < backgrounds[j].Name
	})

	for _, background := range backgrounds {
		fmt.Printf("%s\t%s\n", theme.Title(strings.Title(background.Type)), strings.Title(background.Name))
	}

	// Print the aptitudes
	aptitudes := []Aptitude{}

	for _, aptitude := range c.Aptitudes {
		aptitudes = append(aptitudes, aptitude)
	}

	slice.Sort(aptitudes, func(i, j int) bool {
		return aptitudes[i] < aptitudes[j]
	})

	fmt.Printf("\n%s (%s)\n", theme.Title("Aptitudes"), theme.Value(fmt.Sprintf("%d", len(aptitudes))))
	for _, aptitude := range aptitudes {
		fmt.Printf("%s\n", strings.Title(string(aptitude)))
	}

	// Print the experience
	fmt.Printf("\n%s\t%d/%d\n", theme.Title("Experience"), c.Spent, c.Experience)

	// Print the characteristics

	var characteristicSum int
	characteristics := []Characteristic{}
	for _, characteristic := range c.Characteristics {
		characteristicSum += characteristic.Value
		characteristics = append(characteristics, characteristic)
	}

	slice.Sort(characteristics, func(i, j int) bool {
		return characteristics[i].Name < characteristics[j].Name
	})

	fmt.Printf("\n%s (%s)\n", theme.Title("Characteristics"), theme.Value(fmt.Sprintf("%d", characteristicSum)))

	for _, characteristic := range characteristics {
		fmt.Printf("%s\t%s %s\n", characteristic.Name, theme.Value(characteristic.Value), theme.Value(characteristic.Level()))
	}

	// Print the gauges

	if len(c.Gauges) != 0 {

		fmt.Printf("\n%s\n", theme.Title("Gauges"))

		gauges := []Gauge{}
		for _, gauge := range c.Gauges {
			gauges = append(gauges, gauge)
		}

		slice.Sort(gauges, func(i, j int) bool {
			return gauges[i].Name < gauges[j].Name
		})

		for _, gauge := range gauges {
			fmt.Printf("%s\t%s\n", gauge.Name, theme.Value(gauge.Value))
		}
	}

	// Print the skills

	if len(c.Skills) != 0 {

		// Print the skills using a tabwriter
		fmt.Printf("\n%s\n", theme.Title("Skills"))

		skills := []Skill{}
		for _, skill := range c.Skills {
			skills = append(skills, skill)
		}

		slice.Sort(skills, func(i, j int) bool {
			return skills[i].FullName() < skills[j].FullName()
		})

		w := tabwriter.NewWriter(os.Stdout, 10, 1, 2, ' ', 0)
		for _, skill := range skills {
			fmt.Fprintf(w, "%s\t+%s\n", strings.Title(skill.FullName()), theme.Value((skill.Tier-1)*10))
		}
		w.Flush()
	}

	// Print the talents

	if len(c.Talents) != 0 {

		fmt.Printf("\n%s\n", theme.Title("Talents"))

		talents := []Talent{}
		for _, talent := range c.Talents {
			talents = append(talents, talent)
		}

		slice.Sort(talents, func(i, j int) bool {
			return talents[i].FullName() < talents[j].FullName()
		})

		w := tabwriter.NewWriter(os.Stdout, 10, 1, 2, ' ', 0)
		for _, talent := range talents {
			if talent.Value != 1 {
				fmt.Fprintf(w, "%s (%d)\t%s\n", strings.Title(talent.FullName()), talent.Value, talent.Description)
			} else {
				fmt.Fprintf(w, "%s\t%s\n", strings.Title(talent.FullName()), talent.Description)
			}
		}
		w.Flush()
	}

	// Print the spells

	if len(c.Spells) != 0 {

		fmt.Printf("\n%s\n", theme.Title("Spells"))

		spells := []Spell{}

		for _, spell := range c.Spells {
			spells = append(spells, spell)
		}

		slice.Sort(spells, func(i, j int) bool {
			return spells[i].Name < spells[j].Name
		})

		w := tabwriter.NewWriter(os.Stdout, 10, 1, 2, ' ', 0)
		for _, spell := range spells {
			fmt.Fprintf(w, "%s\t%s\n", strings.Title(spell.Name), spell.Description)
		}
		w.Flush()
	}

	// Print the special rules

	if len(c.Rules) != 0 {
		fmt.Printf("\n%s\n", theme.Title("Rules"))

		rules := []Rule{}

		for _, rule := range c.Rules {
			rules = append(rules, rule)
		}

		slice.Sort(rules, func(i, j int) bool {
			return rules[i].Name < rules[j].Name
		})

		w := tabwriter.NewWriter(os.Stdout, 10, 1, 2, ' ', 0)
		for _, rule := range rules {
			fmt.Printf("%s\t%s\n", strings.Title(rule.Name), rule.Description)
		}
		w.Flush()
	}
}
Example #21
0
func Rule(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var from, to time.Time
	var err error
	if f := r.FormValue("from"); len(f) > 0 {
		from, err = time.Parse(tsdbFormatSecs, f)
		if err != nil {
			return nil, err
		}
	}
	if f := r.FormValue("to"); len(f) > 0 {
		to, err = time.Parse(tsdbFormatSecs, f)
		if err != nil {
			return nil, err
		}
	}
	intervals := 1
	if i := r.FormValue("intervals"); len(i) > 0 {
		intervals, err = strconv.Atoi(r.FormValue("intervals"))
		if err != nil {
			return nil, err
		}
		if intervals < 1 {
			return nil, fmt.Errorf("must be > 0 intervals")
		}
	}
	if fz, tz := from.IsZero(), to.IsZero(); fz && tz {
		from = time.Now()
	} else if fz && !tz {
		return nil, fmt.Errorf("cannot specify to without from")
	} else if !fz && tz && intervals > 1 {
		return nil, fmt.Errorf("cannot specify intervals without from and to")
	}

	c, a, hash, err := buildConfig(r)
	if err != nil {
		return nil, err
	}

	ch := make(chan int)
	errch := make(chan error, intervals)
	resch := make(chan *ruleResult, intervals)
	var wg sync.WaitGroup
	diff := -from.Sub(to)
	if intervals > 1 {
		diff /= time.Duration(intervals - 1)
	}
	worker := func() {
		wg.Add(1)
		for interval := range ch {
			t.Step(fmt.Sprintf("interval %v", interval), func(t miniprofiler.Timer) {
				now := from.Add(diff * time.Duration(interval))
				res, err := procRule(t, c, a, now, interval != 0, r.FormValue("email"), r.FormValue("template_group"))
				resch <- res
				errch <- err
			})
		}
		defer wg.Done()
	}
	for i := 0; i < 20; i++ {
		go worker()
	}
	for i := 0; i < intervals; i++ {
		ch <- i
	}
	close(ch)
	wg.Wait()
	close(errch)
	close(resch)
	type Result struct {
		Group  models.AlertKey
		Result *models.Event
	}
	type Set struct {
		Critical, Warning, Normal int
		Time                      string
		Results                   []*Result `json:",omitempty"`
	}
	type History struct {
		Time, EndTime time.Time
		Status        string
	}
	type Histories struct {
		History []*History
	}

	ret := struct {
		Errors       []string `json:",omitempty"`
		Warnings     []string `json:",omitempty"`
		Sets         []*Set
		AlertHistory map[models.AlertKey]*Histories
		Body         string      `json:",omitempty"`
		Subject      string      `json:",omitempty"`
		Data         interface{} `json:",omitempty"`
		Hash         string
	}{
		AlertHistory: make(map[models.AlertKey]*Histories),
		Hash:         hash,
	}
	for err := range errch {
		if err == nil {
			continue
		}
		ret.Errors = append(ret.Errors, err.Error())
	}
	for res := range resch {
		if res == nil {
			continue
		}
		set := Set{
			Critical: len(res.Criticals),
			Warning:  len(res.Warnings),
			Normal:   len(res.Normals),
			Time:     res.Time.Format(tsdbFormatSecs),
		}
		if res.Data != nil {
			ret.Body = res.Body
			ret.Subject = res.Subject
			ret.Data = res.Data
			for k, v := range res.Result {
				set.Results = append(set.Results, &Result{
					Group:  k,
					Result: v,
				})
			}
			slice.Sort(set.Results, func(i, j int) bool {
				a := set.Results[i]
				b := set.Results[j]
				if a.Result.Status != b.Result.Status {
					return a.Result.Status > b.Result.Status
				}
				return a.Group < b.Group
			})
		}
		for k, v := range res.Result {
			if ret.AlertHistory[k] == nil {
				ret.AlertHistory[k] = new(Histories)
			}
			h := ret.AlertHistory[k]
			h.History = append(h.History, &History{
				Time:   v.Time,
				Status: v.Status.String(),
			})
		}
		ret.Sets = append(ret.Sets, &set)
		ret.Warnings = append(ret.Warnings, res.Warning...)
	}
	slice.Sort(ret.Sets, func(i, j int) bool {
		return ret.Sets[i].Time < ret.Sets[j].Time
	})
	for _, histories := range ret.AlertHistory {
		hist := histories.History
		slice.Sort(hist, func(i, j int) bool {
			return hist[i].Time.Before(hist[j].Time)
		})
		for i := 1; i < len(hist); i++ {
			if i < len(hist)-1 && hist[i].Status == hist[i-1].Status {
				hist = append(hist[:i], hist[i+1:]...)
				i--
			}
		}
		for i, h := range hist[:len(hist)-1] {
			h.EndTime = hist[i+1].Time
		}
		histories.History = hist[:len(hist)-1]
	}
	return &ret, nil
}
Example #22
0
// Suggest the next purchasable upgrades of the character.
func (c *Character) Suggest(universe Universe, max int, all bool, allowSpells bool) {

	// Aggregate each coster into a unique slice of costers.
	costers := []Coster{}
	for _, upgrade := range universe.Characteristics {
		costers = append(costers, upgrade)
	}

	for _, upgrade := range universe.Skills {
		costers = append(costers, upgrade)
	}

	for _, upgrade := range universe.Talents {
		costers = append(costers, upgrade)
	}

	for _, upgrade := range universe.Gauges {
		upgrade.Value = 1
		costers = append(costers, upgrade)
	}

	if allowSpells {

		for _, upgrade := range universe.Spells {
			costers = append(costers, upgrade)
		}
	}

	// Default max value equals to the remaining XP.
	if max == 0 {
		max = c.Experience - c.Spent
	}

	// The slice of appliable upgrades.
	var appliable []Upgrade

	// Attempt to apply each coster once.
	for _, coster := range costers {
		var upgrade Upgrade

		// Don't propose the upgrade its cost cannot be defined
		cost, err := coster.Cost(universe, *c)
		if err != nil {
			continue
		}

		// Don't propose the upgrade if it is free.
		if cost == 0 {
			continue
		}

		// Don't propose the upgrade if it is too expensive.
		if !all && max < cost {
			continue
		}

		upgrade.Cost = &cost
		upgrade.Mark = MarkApply
		upgrade.Name = coster.DefaultName()

		err = coster.Apply(c, upgrade)
		if err != nil {
			continue
		}

		appliable = append(appliable, upgrade)
	}

	// Sort by cost then name.
	slice.Sort(appliable, func(i, j int) bool {
		ci, cj := *appliable[i].Cost, *appliable[j].Cost
		if ci == cj {
			return appliable[i].Name < appliable[j].Name
		}
		return ci < cj
	})

	// Print the name.
	fmt.Printf("%s\t%s\n", theme.Title("Name"), c.Name)

	// Print the experience
	fmt.Printf("\n%s\t%d/%d\n", theme.Title("Experience"), c.Spent, c.Experience)

	// Print the history.
	fmt.Printf("\n%s\n", theme.Title("Suggestions"))

	w := tabwriter.NewWriter(os.Stdout, 10, 1, 2, ' ', 0)
	for i, upgrade := range appliable {
		if i > 0 && *appliable[i-1].Cost != *upgrade.Cost {
			fmt.Fprintln(w)
		}
		fmt.Fprintf(w, "%s\t%s\n", theme.Value(*upgrade.Cost), strings.Title(upgrade.Name))
	}
	w.Flush()
}