Skip to content

Commit

Permalink
Load user assets with their teams and record reply daily counts for o…
Browse files Browse the repository at this point in the history
…rg, user and team
  • Loading branch information
rowanseymour committed May 10, 2022
1 parent 3a362e6 commit 33a3998
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 17 deletions.
34 changes: 27 additions & 7 deletions core/models/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,22 +697,24 @@ type BroadcastTranslation struct {
// Broadcast represents a broadcast that needs to be sent
type Broadcast struct {
b struct {
BroadcastID BroadcastID `json:"broadcast_id,omitempty" db:"id"`
BroadcastID BroadcastID `json:"broadcast_id,omitempty" db:"id"`
Translations map[envs.Language]*BroadcastTranslation `json:"translations"`
Text hstore.Hstore ` db:"text"`
Text hstore.Hstore ` db:"text"`
TemplateState TemplateState `json:"template_state"`
BaseLanguage envs.Language `json:"base_language" db:"base_language"`
BaseLanguage envs.Language `json:"base_language" db:"base_language"`
URNs []urns.URN `json:"urns,omitempty"`
ContactIDs []ContactID `json:"contact_ids,omitempty"`
GroupIDs []GroupID `json:"group_ids,omitempty"`
OrgID OrgID `json:"org_id" db:"org_id"`
ParentID BroadcastID `json:"parent_id,omitempty" db:"parent_id"`
TicketID TicketID `json:"ticket_id,omitempty" db:"ticket_id"`
OrgID OrgID `json:"org_id" db:"org_id"`
CreatedByID UserID `json:"created_by_id,omitempty" db:"created_by_id"`
ParentID BroadcastID `json:"parent_id,omitempty" db:"parent_id"`
TicketID TicketID `json:"ticket_id,omitempty" db:"ticket_id"`
}
}

func (b *Broadcast) ID() BroadcastID { return b.b.BroadcastID }
func (b *Broadcast) OrgID() OrgID { return b.b.OrgID }
func (b *Broadcast) CreatedByID() UserID { return b.b.CreatedByID }
func (b *Broadcast) ContactIDs() []ContactID { return b.b.ContactIDs }
func (b *Broadcast) GroupIDs() []GroupID { return b.b.GroupIDs }
func (b *Broadcast) URNs() []urns.URN { return b.b.URNs }
Expand Down Expand Up @@ -756,7 +758,7 @@ func InsertChildBroadcast(ctx context.Context, db Queryer, parent *Broadcast) (*
parent.b.GroupIDs,
parent.b.TicketID,
)
// populate our parent id
child.b.CreatedByID = parent.CreatedByID()
child.b.ParentID = parent.ID()

// populate text from our translations
Expand Down Expand Up @@ -904,6 +906,7 @@ func (b *Broadcast) CreateBatch(contactIDs []ContactID) *BroadcastBatch {
batch.b.Translations = b.b.Translations
batch.b.TemplateState = b.b.TemplateState
batch.b.OrgID = b.b.OrgID
batch.b.CreatedByID = b.b.CreatedByID
batch.b.TicketID = b.b.TicketID
batch.b.ContactIDs = contactIDs
return batch
Expand All @@ -920,6 +923,7 @@ type BroadcastBatch struct {
ContactIDs []ContactID `json:"contact_ids,omitempty"`
IsLast bool `json:"is_last"`
OrgID OrgID `json:"org_id"`
CreatedByID UserID `json:"created_by_id"`
TicketID TicketID `json:"ticket_id"`
}
}
Expand All @@ -929,6 +933,7 @@ func (b *BroadcastBatch) ContactIDs() []ContactID { return b.b.Conta
func (b *BroadcastBatch) URNs() map[ContactID]urns.URN { return b.b.URNs }
func (b *BroadcastBatch) SetURNs(urns map[ContactID]urns.URN) { b.b.URNs = urns }
func (b *BroadcastBatch) OrgID() OrgID { return b.b.OrgID }
func (b *BroadcastBatch) CreatedByID() UserID { return b.b.CreatedByID }
func (b *BroadcastBatch) TicketID() TicketID { return b.b.TicketID }
func (b *BroadcastBatch) Translations() map[envs.Language]*BroadcastTranslation {
return b.b.Translations
Expand Down Expand Up @@ -1146,6 +1151,21 @@ func CreateBroadcastMessages(ctx context.Context, rt *runtime.Runtime, oa *OrgAs
if err != nil {
return nil, errors.Wrapf(err, "error updating broadcast ticket")
}

// record reply counts for org, user and team
replyCounts := map[string]int{scopeOrg(oa): 1}

if bcast.CreatedByID() != NilUserID {
user := oa.UserByID(bcast.CreatedByID())
if user != nil {
replyCounts[scopeUser(oa, user)] = 1
if user.Team() != nil {
replyCounts[scopeTeam(user.Team())] = 1
}
}
}

insertTicketDailyCounts(ctx, rt.DB, TicketDailyCountReply, oa.Org().Timezone(), replyCounts)
}

return msgs, nil
Expand Down
43 changes: 43 additions & 0 deletions core/models/teams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package models

import (
"database/sql/driver"

"github.com/nyaruka/null"
)

const (
// NilTeamID is the id 0 considered as nil user id
NilTeamID = TeamID(0)
)

// TeamID is our type for team ids, which can be null
type TeamID null.Int

type TeamUUID string

type Team struct {
ID TeamID `json:"id"`
UUID TeamUUID `json:"uuid"`
Name string `json:"name"`
}

// MarshalJSON marshals into JSON. 0 values will become null
func (i TeamID) MarshalJSON() ([]byte, error) {
return null.Int(i).MarshalJSON()
}

// UnmarshalJSON unmarshals from JSON. null values become 0
func (i *TeamID) UnmarshalJSON(b []byte) error {
return null.UnmarshalInt(b, (*null.Int)(i))
}

// Value returns the db value, null is returned for 0
func (i TeamID) Value() (driver.Value, error) {
return null.Int(i).Value()
}

// Scan scans from the db value. null values become 0
func (i *TeamID) Scan(value interface{}) error {
return null.ScanInt(value, (*null.Int)(i))
}
18 changes: 14 additions & 4 deletions core/models/tickets.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,10 @@ func InsertTickets(ctx context.Context, tx Queryer, oa *OrgAssets, tickets []*Ti
ts[i] = &t.t

if t.AssigneeID() != NilUserID {
assignmentCounts[scopeUser(oa, t.AssigneeID())]++
assignee := oa.UserByID(t.AssigneeID())
if assignee != nil {
assignmentCounts[scopeUser(oa, assignee)]++
}
}
}

Expand Down Expand Up @@ -449,7 +452,10 @@ func TicketsAssign(ctx context.Context, db Queryer, oa *OrgAssets, userID UserID

// if this is an initial assignment record count for user
if ticket.AssigneeID() == NilUserID && assigneeID != NilUserID {
assignmentCounts[scopeUser(oa, assigneeID)]++
assignee := oa.UserByID(assigneeID)
if assignee != nil {
assignmentCounts[scopeUser(oa, assignee)]++
}
}

ids = append(ids, ticket.ID())
Expand Down Expand Up @@ -899,6 +905,10 @@ func scopeOrg(oa *OrgAssets) string {
return fmt.Sprintf("o:%d", oa.OrgID())
}

func scopeUser(oa *OrgAssets, uid UserID) string {
return fmt.Sprintf("o:%d:u:%d", oa.OrgID(), uid)
func scopeTeam(t *Team) string {
return fmt.Sprintf("t:%d", t.ID)
}

func scopeUser(oa *OrgAssets, u *User) string {
return fmt.Sprintf("o:%d:u:%d", oa.OrgID(), u.ID())
}
11 changes: 10 additions & 1 deletion core/models/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Role UserRole `json:"role"`
Team *Team `json:"team"`
}
}

Expand All @@ -85,6 +86,11 @@ func (u *User) Name() string {
return strings.Join(names, " ")
}

// Team returns the user's ticketing team if any
func (u *User) Team() *Team {
return u.u.Team
}

var _ assets.User = (*User)(nil)

const selectOrgUsersSQL = `
Expand All @@ -93,7 +99,8 @@ SELECT ROW_TO_JSON(r) FROM (SELECT
u.email AS "email",
u.first_name as "first_name",
u.last_name as "last_name",
o.role AS "role"
o.role AS "role",
row_to_json(team_struct) AS team
FROM
auth_user u
INNER JOIN (
Expand All @@ -107,6 +114,8 @@ INNER JOIN (
UNION
SELECT user_id, 'S' AS "role" FROM orgs_org_surveyors WHERE org_id = $1
) o ON o.user_id = u.id
LEFT JOIN orgs_usersettings s ON s.user_id = u.id
LEFT JOIN LATERAL (SELECT id, uuid, name FROM tickets_team WHERE tickets_team.id = s.team_id) AS team_struct ON True
WHERE
u.is_active = TRUE
ORDER BY
Expand Down
15 changes: 10 additions & 5 deletions core/models/users_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@ func TestLoadUsers(t *testing.T) {
users, err := oa.Users()
require.NoError(t, err)

partners := &models.Team{testdata.Partners.ID, testdata.Partners.UUID, "Partners"}
office := &models.Team{testdata.Office.ID, testdata.Office.UUID, "Office"}

expectedUsers := []struct {
id models.UserID
email string
name string
role models.UserRole
team *models.Team
}{
{testdata.Admin.ID, testdata.Admin.Email, "Andy Admin", models.UserRoleAdministrator},
{testdata.Agent.ID, testdata.Agent.Email, "Ann D'Agent", models.UserRoleAgent},
{testdata.Editor.ID, testdata.Editor.Email, "Ed McEditor", models.UserRoleEditor},
{testdata.Surveyor.ID, testdata.Surveyor.Email, "Steve Surveys", models.UserRoleSurveyor},
{testdata.Viewer.ID, testdata.Viewer.Email, "Veronica Views", models.UserRoleViewer},
{id: testdata.Admin.ID, email: testdata.Admin.Email, name: "Andy Admin", role: models.UserRoleAdministrator, team: office},
{id: testdata.Agent.ID, email: testdata.Agent.Email, name: "Ann D'Agent", role: models.UserRoleAgent, team: partners},
{id: testdata.Editor.ID, email: testdata.Editor.Email, name: "Ed McEditor", role: models.UserRoleEditor, team: office},
{id: testdata.Surveyor.ID, email: testdata.Surveyor.Email, name: "Steve Surveys", role: models.UserRoleSurveyor, team: nil},
{id: testdata.Viewer.ID, email: testdata.Viewer.Email, name: "Veronica Views", role: models.UserRoleViewer, team: nil},
}

require.Equal(t, len(expectedUsers), len(users))
Expand All @@ -43,6 +47,7 @@ func TestLoadUsers(t *testing.T) {
assert.Equal(t, expected.id, modelUser.ID())
assert.Equal(t, expected.email, modelUser.Email())
assert.Equal(t, expected.role, modelUser.Role())
assert.Equal(t, expected.team, modelUser.Team())

assert.Equal(t, modelUser, oa.UserByID(expected.id))
assert.Equal(t, modelUser, oa.UserByEmail(expected.email))
Expand Down
Binary file modified mailroom_test.dump
Binary file not shown.
3 changes: 3 additions & 0 deletions testsuite/testdata/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ var Mailgun = &Ticketer{2, "f9c9447f-a291-4f3c-8c79-c089bbd4e713"}
var Zendesk = &Ticketer{3, "4ee6d4f3-f92b-439b-9718-8da90c05490b"}
var RocketChat = &Ticketer{4, "6c50665f-b4ff-4e37-9625-bc464fe6a999"}

var Partners = &Team{1, "4321c30b-b596-46fa-adb4-4a46d37923f6"}
var Office = &Team{2, "f14c1762-d38b-4072-ae63-2705332a3719"}

var Luis = &Classifier{1, "097e026c-ae79-4740-af67-656dbedf0263"}
var Wit = &Classifier{2, "ff2a817c-040a-4eb2-8404-7d92e8b79dd0"}
var Bothub = &Classifier{3, "859b436d-3005-4e43-9ad5-3de5f26ede4c"}
Expand Down
5 changes: 5 additions & 0 deletions testsuite/testdata/tickets.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ type Ticket struct {
UUID flows.TicketUUID
}

type Team struct {
ID models.TeamID
UUID models.TeamUUID
}

func (k *Ticket) Load(db *sqlx.DB) *models.Ticket {
tickets, err := models.LoadTickets(context.Background(), db, []models.TicketID{k.ID})
must(err, len(tickets) == 1)
Expand Down

0 comments on commit 33a3998

Please sign in to comment.