Browse Source
Fixed log service bugs with importing and character list not updating.
thegreatrefactor
Fixed log service bugs with importing and character list not updating.
thegreatrefactor
Gisle Aune
5 years ago
10 changed files with 32 additions and 417 deletions
-
2database/mongodb/posts.go
-
55models/logs/add.go
-
48models/logs/db.go
-
50models/logs/edit.go
-
16models/logs/find.go
-
70models/logs/list.go
-
22models/logs/remove.go
-
138models/logs/update-characters.go
-
5services/channels.go
-
43services/logs.go
@ -1,55 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"errors" |
|||
"strconv" |
|||
"time" |
|||
|
|||
"git.aiterp.net/rpdata/api/internal/counter" |
|||
"git.aiterp.net/rpdata/api/models" |
|||
"git.aiterp.net/rpdata/api/models/channels" |
|||
"github.com/globalsign/mgo/bson" |
|||
) |
|||
|
|||
// Add creates a new Log
|
|||
func Add(date time.Time, channelName, title, eventName, description string, open bool) (models.Log, error) { |
|||
nextID, err := counter.Next("auto_increment", "Log") |
|||
if err != nil { |
|||
return models.Log{}, errors.New("Failed to allocate short ID: " + err.Error()) |
|||
} |
|||
|
|||
_, err = channels.Ensure(channelName, open) |
|||
if err != nil { |
|||
return models.Log{}, errors.New("Failed to ensure channel: " + err.Error()) |
|||
} |
|||
|
|||
log := models.Log{ |
|||
ID: makeLogID(date, channelName), |
|||
ShortID: "L" + strconv.Itoa(nextID), |
|||
Date: date, |
|||
ChannelName: channelName, |
|||
Title: title, |
|||
EventName: eventName, |
|||
Description: description, |
|||
Open: open, |
|||
CharacterIDs: nil, |
|||
} |
|||
|
|||
err = collection.Insert(log) |
|||
if err != nil { |
|||
return models.Log{}, err |
|||
} |
|||
|
|||
// There can be only one open log in the same channel. TODO: Transaction
|
|||
if open { |
|||
query := bson.M{ |
|||
"_id": bson.M{"$ne": log.ID}, |
|||
"open": true, |
|||
"channel": log.ChannelName, |
|||
} |
|||
|
|||
go collection.UpdateAll(query, bson.M{"$set": bson.M{"open": false}}) |
|||
} |
|||
|
|||
return log, nil |
|||
} |
@ -1,48 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"fmt" |
|||
"time" |
|||
|
|||
"git.aiterp.net/rpdata/api/internal/store" |
|||
"git.aiterp.net/rpdata/api/models" |
|||
"github.com/globalsign/mgo" |
|||
) |
|||
|
|||
var collection *mgo.Collection |
|||
var postCollection *mgo.Collection |
|||
|
|||
func find(query interface{}) (models.Log, error) { |
|||
log := models.Log{} |
|||
err := collection.Find(query).One(&log) |
|||
if err != nil { |
|||
return models.Log{}, err |
|||
} |
|||
|
|||
return log, nil |
|||
} |
|||
|
|||
func list(query interface{}, limit int) ([]models.Log, error) { |
|||
logs := make([]models.Log, 0, 64) |
|||
err := collection.Find(query).Limit(limit).Sort("-date").All(&logs) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
return logs, nil |
|||
} |
|||
|
|||
func iter(query interface{}, limit int) *mgo.Iter { |
|||
return collection.Find(query).Sort("-date").Limit(limit).Batch(8).Iter() |
|||
} |
|||
|
|||
func makeLogID(date time.Time, channel string) string { |
|||
return fmt.Sprintf("%s%03d_%s", date.UTC().Format("2006-01-02_150405"), (date.Nanosecond() / int(time.Millisecond/time.Nanosecond)), channel[1:]) |
|||
} |
|||
|
|||
func init() { |
|||
store.HandleInit(func(db *mgo.Database) { |
|||
collection = db.C("logbot3.logs") |
|||
postCollection = db.C("logbot3.posts") |
|||
}) |
|||
} |
@ -1,50 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"git.aiterp.net/rpdata/api/models" |
|||
"github.com/globalsign/mgo/bson" |
|||
) |
|||
|
|||
// Edit sets a log's meta-data.
|
|||
func Edit(log models.Log, title *string, event *string, description *string, open *bool) (models.Log, error) { |
|||
changes := bson.M{} |
|||
|
|||
if title != nil && *title != log.Title { |
|||
changes["title"] = *title |
|||
log.Title = *title |
|||
} |
|||
if event != nil && *event != log.EventName { |
|||
changes["event"] = *event |
|||
log.EventName = *event |
|||
} |
|||
if description != nil && *description != log.Description { |
|||
changes["description"] = *description |
|||
log.Description = *description |
|||
} |
|||
if open != nil && *open != log.Open { |
|||
changes["open"] = *open |
|||
log.Open = *open |
|||
} |
|||
|
|||
if len(changes) == 0 { |
|||
return log, nil |
|||
} |
|||
|
|||
err := collection.UpdateId(log.ID, bson.M{"$set": changes}) |
|||
if err != nil { |
|||
return models.Log{}, err |
|||
} |
|||
|
|||
// There can be only one open log. TODO: Transaction
|
|||
if changes["open"] != nil && *open { |
|||
query := bson.M{ |
|||
"_id": bson.M{"$ne": log.ID}, |
|||
"open": true, |
|||
"channel": log.ChannelName, |
|||
} |
|||
|
|||
go collection.UpdateAll(query, bson.M{"$set": bson.M{"open": false}}) |
|||
} |
|||
|
|||
return log, nil |
|||
} |
@ -1,16 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"git.aiterp.net/rpdata/api/models" |
|||
"github.com/globalsign/mgo/bson" |
|||
) |
|||
|
|||
// FindID finds a log either by it's ID or short ID.
|
|||
func FindID(id string) (models.Log, error) { |
|||
return find(bson.M{ |
|||
"$or": []bson.M{ |
|||
bson.M{"_id": id}, |
|||
bson.M{"shortId": id}, |
|||
}, |
|||
}) |
|||
} |
@ -1,70 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"git.aiterp.net/rpdata/api/models" |
|||
"github.com/globalsign/mgo/bson" |
|||
) |
|||
|
|||
// Filter for the List() function
|
|||
type Filter struct { |
|||
Search *string |
|||
Characters *[]string |
|||
Channels *[]string |
|||
Events *[]string |
|||
Open *bool |
|||
Limit int |
|||
} |
|||
|
|||
// List lists all logs
|
|||
func List(filter *Filter) ([]models.Log, error) { |
|||
query := bson.M{} |
|||
limit := 0 |
|||
|
|||
if filter != nil { |
|||
// Run a text search
|
|||
if filter.Search != nil { |
|||
searchResults, err := search(*filter.Search) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
|
|||
// Posts always use shortId to refer to the log
|
|||
query["shortId"] = bson.M{"$in": searchResults} |
|||
} |
|||
|
|||
// Find logs including any of the specified events and channels
|
|||
if filter.Channels != nil { |
|||
query["channel"] = bson.M{"$in": *filter.Channels} |
|||
} |
|||
if filter.Events != nil { |
|||
query["event"] = bson.M{"$in": *filter.Events} |
|||
} |
|||
|
|||
// Find logs including all of the specified character IDs.
|
|||
if filter.Characters != nil { |
|||
query["characterIds"] = bson.M{"$all": *filter.Characters} |
|||
} |
|||
|
|||
// Limit to only open logs
|
|||
if filter.Open != nil { |
|||
query["open"] = *filter.Open |
|||
} |
|||
|
|||
// Set the limit from the filter
|
|||
limit = filter.Limit |
|||
} |
|||
|
|||
return list(query, limit) |
|||
} |
|||
|
|||
func search(text string) ([]string, error) { |
|||
query := bson.M{ |
|||
"$text": bson.M{"$search": text}, |
|||
"logId": bson.M{"$ne": nil}, |
|||
} |
|||
|
|||
ids := make([]string, 0, 64) |
|||
err := postCollection.Find(query).Distinct("logId", &ids) |
|||
|
|||
return ids, err |
|||
} |
@ -1,22 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"errors" |
|||
|
|||
"git.aiterp.net/rpdata/api/models" |
|||
"git.aiterp.net/rpdata/api/models/posts" |
|||
) |
|||
|
|||
// Remove removes the log.
|
|||
func Remove(log models.Log) (models.Log, error) { |
|||
err := collection.RemoveId(log.ID) |
|||
if err != nil { |
|||
return models.Log{}, err |
|||
} |
|||
|
|||
if err := posts.RemoveAllInLog(log); err != nil { |
|||
return models.Log{}, errors.New("The log was removed, but its posts couldn't be: " + err.Error()) |
|||
} |
|||
|
|||
return log, nil |
|||
} |
@ -1,138 +0,0 @@ |
|||
package logs |
|||
|
|||
import ( |
|||
"errors" |
|||
"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" |
|||
"git.aiterp.net/rpdata/api/models/unknownnicks" |
|||
"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, unknowns map[string]int) (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 |
|||
} |
|||
|
|||
counts := make(map[string]int) |
|||
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]") || strings.HasSuffix(post.Nick, "|") { |
|||
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 |
|||
counts[post.Nick]++ |
|||
} |
|||
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 { |
|||
if char.Name == "(Hidden)" { |
|||
continue |
|||
} |
|||
|
|||
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 |
|||
|
|||
if len(nicks) > 0 && unknowns != nil { |
|||
NickLoop: |
|||
for nick := range added { |
|||
if !added[nick] { |
|||
continue |
|||
} |
|||
|
|||
for _, character := range characters { |
|||
if character.HasNick(nick) { |
|||
continue NickLoop |
|||
} |
|||
} |
|||
|
|||
unknowns[nick] += counts[nick] |
|||
} |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
unknowns := make(map[string]int, 256) |
|||
|
|||
log := models.Log{} |
|||
for iter.Next(&log) { |
|||
_, err = UpdateCharacters(log, unknowns) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
|
|||
err = iter.Err() |
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
err = unknownnicks.Update(unknowns) |
|||
if err != nil { |
|||
return errors.New("Failed to commit unknown nicks update: " + err.Error()) |
|||
} |
|||
|
|||
return nil |
|||
} |
|||
|
|||
// ScheduleFullUpdate runs a full character update within the next 60 seconds.
|
|||
func ScheduleFullUpdate() { |
|||
updateTask.Schedule() |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue