Browse Source

models2: Added log character updates.

1.0
Gisle Aune 6 years ago
parent
commit
73fe125870
  1. 11
      cmd/rpdata-server/main.go
  2. 7
      graph2/queries/character.go
  3. 29
      graph2/queries/post.go
  4. 84
      internal/task/task.go
  5. 6
      models/characters/list.go
  6. 106
      models/logs/update-characters.go
  7. BIN
      test.prof

11
cmd/rpdata-server/main.go

@ -8,6 +8,8 @@ import (
"runtime/debug"
"strings"
"git.aiterp.net/rpdata/api/models/logs"
"git.aiterp.net/rpdata/api/graph2"
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/internal/loader"
@ -24,6 +26,15 @@ func main() {
http.Handle("/", handler.Playground("RPData API", "/graphql"))
http.Handle("/graphql", queryHandler())
go func() {
err := logs.RunFullUpdate()
if err != nil {
log.Println(err)
}
log.Println("Characters updated")
}()
log.Fatal(http.ListenAndServe(":8081", nil))
}

7
graph2/queries/character.go

@ -9,6 +9,7 @@ import (
"git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models/logs"
)
// Queries
@ -59,6 +60,8 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact
author = *input.Author
}
logs.ScheduleFullUpdate()
return characters.Add(input.Nick, input.Name, shortName, author, description)
}
@ -77,6 +80,8 @@ func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.Cha
return models.Character{}, errors.New("You are not permitted to edit this character")
}
logs.ScheduleFullUpdate()
return characters.AddNick(character, input.Nick)
}
@ -91,6 +96,8 @@ func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.
return models.Character{}, errors.New("You are not permitted to edit this character")
}
logs.ScheduleFullUpdate()
return characters.RemoveNick(character, input.Nick)
}

29
graph2/queries/post.go

@ -52,7 +52,14 @@ func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput
return models.Post{}, err
}
return posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
if err != nil {
return models.Post{}, err
}
go logs.UpdateCharacters(log)
return post, nil
}
func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) {
@ -66,6 +73,17 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp
return models.Post{}, errors.New("Post not found")
}
if input.Nick != nil {
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
return
}
logs.UpdateCharacters(log)
}()
}
return posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text)
}
@ -94,5 +112,14 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov
return models.Post{}, errors.New("Post not found (before removing, of course)")
}
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
return
}
logs.UpdateCharacters(log)
}()
return posts.Remove(post)
}

84
internal/task/task.go

@ -0,0 +1,84 @@
package task
import (
"context"
"sync"
"time"
)
var globalCtx, globalCancel = context.WithCancel(context.Background())
// A Task is a wrapper around a function that offers scheduling
// and syncronization.
type Task struct {
mutex sync.Mutex
ctx context.Context
ctxCancel context.CancelFunc
waitDuration time.Duration
scheduled bool
callback func() error
}
// Context gets the task's context.
func (task *Task) Context() context.Context {
return task.ctx
}
// Stop stops a task. This should not be done to stop a single run,
// but as a way to cancel it forever.
func (task *Task) Stop() {
task.ctxCancel()
}
// Schedule schedules a task for later execution.
func (task *Task) Schedule() {
// Don't if the context is closed.
select {
case <-task.Context().Done():
return
default:
}
task.mutex.Lock()
if task.scheduled {
task.mutex.Unlock()
return
}
task.scheduled = true
task.mutex.Unlock()
go func() {
<-time.After(task.waitDuration)
// Mark as no longer scheduled
task.mutex.Lock()
task.scheduled = false
task.mutex.Unlock()
// Don't if the task is cancelled
select {
case <-task.Context().Done():
return
default:
}
task.callback()
}()
}
// New makes a new task with the callback.
func New(waitDuration time.Duration, callback func() error) *Task {
ctx, ctxCancel := context.WithCancel(globalCtx)
return &Task{
callback: callback,
ctx: ctx,
ctxCancel: ctxCancel,
waitDuration: waitDuration,
}
}
// StopAll stops all tasks.
func StopAll() {
globalCancel()
}

6
models/characters/list.go

@ -25,10 +25,10 @@ func List(filter *Filter) ([]models.Character, error) {
query["_id"] = filter.IDs[0]
}
if len(filter.Nicks) > 1 {
query["nicks"] = bson.M{"$in": filter.Nicks}
} else if len(filter.Nicks) == 1 {
if len(filter.Nicks) == 1 {
query["nicks"] = filter.Nicks[0]
} else if filter.Nicks != nil {
query["nicks"] = bson.M{"$in": filter.Nicks}
}
if len(filter.Names) > 1 {

106
models/logs/update-characters.go

@ -0,0 +1,106 @@
package logs
import (
"strings"
"time"
"git.aiterp.net/rpdata/api/internal/task"
"git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models/posts"
"github.com/globalsign/mgo/bson"
)
var updateTask = task.New(time.Second*60, RunFullUpdate)
// UpdateCharacters updates the characters for the given log.
func UpdateCharacters(log models.Log) (models.Log, error) {
posts, err := posts.List(&posts.Filter{LogID: &log.ShortID, Kind: []string{"action", "text", "chars"}, Limit: 0})
if err != nil {
return models.Log{}, err
}
added := make(map[string]bool)
removed := make(map[string]bool)
for _, post := range posts {
if post.Kind == "text" || post.Kind == "action" {
if strings.HasPrefix(post.Text, "(") || strings.Contains(post.Nick, "(") || strings.Contains(post.Nick, "[E]") {
continue
}
// Clean up the nick (remove possessive suffix, comma, formatting stuff)
if strings.HasSuffix(post.Nick, "'s") || strings.HasSuffix(post.Nick, "`s") {
post.Nick = post.Nick[:len(post.Nick)-2]
} else if strings.HasSuffix(post.Nick, "'") || strings.HasSuffix(post.Nick, "`") || strings.HasSuffix(post.Nick, ",") || strings.HasSuffix(post.Nick, "\x0f") {
post.Nick = post.Nick[:len(post.Nick)-1]
}
added[post.Nick] = true
}
if post.Kind == "chars" {
tokens := strings.Fields(post.Text)
for _, token := range tokens {
if strings.HasPrefix(token, "-") {
removed[token[1:]] = true
} else {
added[strings.Replace(token, "+", "", 1)] = true
}
}
}
}
nicks := make([]string, 0, len(added))
for nick := range added {
if added[nick] && !removed[nick] {
nicks = append(nicks, nick)
}
}
characters, err := characters.List(&characters.Filter{Nicks: nicks})
if err != nil {
return models.Log{}, err
}
characterIDs := make([]string, len(characters))
for i, char := range characters {
characterIDs[i] = char.ID
}
err = collection.UpdateId(log.ID, bson.M{"$set": bson.M{"characterIds": characterIDs}})
if err != nil {
return models.Log{}, err
}
log.CharacterIDs = characterIDs
return log, nil
}
// RunFullUpdate runs a full update on all logs.
func RunFullUpdate() error {
iter := iter(bson.M{}, 0)
err := iter.Err()
if err != nil {
return err
}
log := models.Log{}
for iter.Next(&log) {
_, err = UpdateCharacters(log)
if err != nil {
return err
}
}
err = iter.Err()
if err != nil {
return err
}
return nil
}
// ScheduleFullUpdate runs a full character update within the next 60 seconds.
func ScheduleFullUpdate() {
updateTask.Schedule()
}

BIN
test.prof

Loading…
Cancel
Save