github.com/resonatecoop/user-api@v1.0.0-13.0.20220915120639-05dc9c04014a/model/user_group.go (about)

     1  package model
     2  
     3  import (
     4  
     5  	// "log"
     6  
     7  	"github.com/go-pg/pg"
     8  	"github.com/go-pg/pg/orm"
     9  
    10  	// trackpb "user-api/rpc/track"
    11  	//tagpb "github.com/resonatecoop/user-api/proto/api"
    12  
    13  	pbUser "github.com/resonatecoop/user-api/proto/user"
    14  
    15  	uuid "github.com/google/uuid"
    16  )
    17  
    18  // UserGroup represents a group of Users and maintains a set of metadata
    19  type UserGroup struct {
    20  	IDRecord
    21  	DisplayName    string `bun:",unique,notnull"`
    22  	Description    string
    23  	ShortBio       string
    24  	GroupEmail     string
    25  	AddressID      uuid.UUID `bun:"type:uuid,notnull"` //for Country see User model
    26  	Address        *StreetAddress
    27  	TypeID         uuid.UUID `bun:"type:uuid,notnull"` //for e.g. Persona Type
    28  	Type           *GroupType
    29  	OwnerID        uuid.UUID   `bun:"type:uuid,notnull"`
    30  	Owner          *User       `bun:"rel:has-one"`
    31  	Links          []uuid.UUID `bun:",type:uuid[],array"`
    32  	Members        []UserGroup `pg:"many2many:user_group_members,fk:user_group_id,joinFK:member_id"`
    33  	MemberOfGroups []UserGroup `pg:"many2many:user_group_members,fk:member_id,joinFK:user_group_id"`
    34  	Avatar         uuid.UUID   `bun:"type:uuid"`
    35  	Banner         uuid.UUID   `bun:"type:uuid"`
    36  	Tags           []uuid.UUID `bun:",type:uuid[],array"`
    37  	// AdminUsers         []uuid.UUID `bun:",type:uuid[]" pg:",array"`
    38  	// Followers          []uuid.UUID `bun:",type:uuid[]" pg:",array"`
    39  	// RecommendedArtists []uuid.UUID `bun:",type:uuid[]" pg:",array"`
    40  	// RecommendedBy      []uuid.UUID `bun:",type:uuid[]" pg:",array"`
    41  	// PrivacyID          uuid.UUID   `bun:"type:uuid,notnull"`
    42  	// Privacy            *UserGroupPrivacy
    43  	// Kvstore            map[string]string `pg:",hstore"`
    44  	// Publisher          map[string]string `pg:",hstore"`
    45  	// Pro                map[string]string `pg:",hstore"`
    46  }
    47  
    48  // Address            *StreetAddress
    49  // Type               GroupType
    50  
    51  //TypeID             uuid.UUID `bun:"type:uuid,notnull"`
    52  //AddressID          uuid.UUID `bun:"type:uuid,notnull"`
    53  //HighlightedTracks    []uuid.UUID `bun:",type:uuid[]" pg:",array"`
    54  //FeaturedTrackGroupID uuid.UUID   `bun:"type:uuid,default:uuid_nil()"`
    55  
    56  // Members        []UserGroup `pg:"many2many:user_group_members,fk:user_group_id,joinFK:member_id"`
    57  // MemberOfGroups []UserGroup `pg:"many2many:user_group_members,fk:member_id,joinFK:user_group_id"`
    58  
    59  //OwnerOfTracks      []Track      `pg:"fk:user_group_id"`          // user group gets paid for these tracks
    60  //ArtistOfTracks     []uuid.UUID  `bun:",type:uuid[]" pg:",array"` // user group displayed as artist for these tracks
    61  //OwnerOfTrackGroups []TrackGroup `pg:"fk:user_group_id"`          // user group owner of these track groups
    62  //LabelOfTrackGroups []TrackGroup `pg:"fk:label_id"`               // label of these track groups
    63  
    64  // func (u *UserGroup) BeforeInsert(c context.Context, db orm.DB) error {
    65  // 	newPrivacy := &UserGroupPrivacy{Private: false, OwnedTracks: true, SupportedArtists: true}
    66  // 	_, pgerr := db.Model(newPrivacy).Returning("*").Insert()
    67  // 	if pgerr != nil {
    68  // 		return pgerr
    69  // 	}
    70  // 	u.PrivacyID = newPrivacy.ID
    71  
    72  // 	return nil
    73  // }
    74  
    75  // Create creates a new UserGroup
    76  // func (u *UserGroup) Create(db *pg.DB, userGroup *pbUser.UserGroup) (error, string) {
    77  // 	var table string
    78  // 	tx, err := db.Begin()
    79  // 	if err != nil {
    80  // 		return err, table
    81  // 	}
    82  // 	defer tx.Rollback()
    83  
    84  // 	groupTaxonomy := new(GroupTaxonomy)
    85  // 	pgerr := tx.Model(groupTaxonomy).Where("type = ?", userGroup.Type.Type).First()
    86  
    87  // 	if pgerr != nil {
    88  // 		return pgerr, "group_taxonomy"
    89  // 	}
    90  // 	u.TypeID = groupTaxonomy.ID
    91  
    92  // 	var newAddress *StreetAddress
    93  // 	if userGroup.Address != nil {
    94  // 		newAddress = &StreetAddress{Data: userGroup.Address.Data}
    95  // 		_, pgerr = tx.Model(newAddress).Returning("*").Insert()
    96  // 		if pgerr != nil {
    97  // 			return pgerr, "street_address"
    98  // 		}
    99  // 	}
   100  // 	u.AddressID = newAddress.ID
   101  
   102  // 	linkIDs, pgerr := getLinkIDs(userGroup.Links, tx)
   103  // 	if pgerr != nil {
   104  // 		return pgerr, "link"
   105  // 	}
   106  // 	u.Links = linkIDs
   107  
   108  // 	// tagIDs, pgerr := GetTagIDs(userGroup.Tags, tx)
   109  // 	// if pgerr != nil {
   110  // 	// 	return pgerr, "tag"
   111  // 	// }
   112  // 	// u.Tags = tagIDs
   113  
   114  // 	// recommendedArtistIDs, pgerr := GetRelatedUserGroupIDs(userGroup.RecommendedArtists, tx)
   115  // 	// if pgerr != nil {
   116  // 	// 	return pgerr, "user_group"
   117  // 	// }
   118  // 	// u.RecommendedArtists = recommendedArtistIDs
   119  
   120  // 	_, pgerr = tx.Model(u).Returning("*").Insert()
   121  // 	if pgerr != nil {
   122  // 		fmt.Println("insert")
   123  // 		return pgerr, "user_group"
   124  // 	}
   125  
   126  // 	// if len(recommendedArtistIDs) > 0 {
   127  // 	// 	_, pgerr = tx.Exec(`
   128  // 	//     UPDATE user_groups
   129  // 	//     SET recommended_by = (select array_agg(distinct e) from unnest(recommended_by || ?) e)
   130  // 	//     WHERE id IN (?)
   131  // 	//   `, pg.Array([]uuid.UUID{u.ID}), pg.In(recommendedArtistIDs))
   132  // 	// 	if pgerr != nil {
   133  // 	// 		return pgerr, "user_group"
   134  // 	// 	}
   135  // 	// }
   136  
   137  // 	pgerr = tx.Model(u).
   138  // 		Column("Privacy").
   139  // 		WherePK().
   140  // 		Select()
   141  // 	if pgerr != nil {
   142  // 		return pgerr, "user_group"
   143  // 	}
   144  
   145  // 	// Building response
   146  // 	userGroup.Address.Id = u.AddressID.String()
   147  // 	userGroup.Type.ID = u.TypeID.String()
   148  // 	// userGroup.Privacy = &pbUser.Privacy{
   149  // 	// 	ID:               u.Privacy.ID.String(),
   150  // 	// 	Private:          u.Privacy.Private,
   151  // 	// 	OwnedTracks:      u.Privacy.OwnedTracks,
   152  // 	// 	SupportedArtists: u.Privacy.SupportedArtists,
   153  // 	// }
   154  
   155  // 	return tx.Commit(), table
   156  // }
   157  
   158  // func (u *UserGroup) Update(db *pg.DB, userGroup *pbUser.UserGroup) (error, string) {
   159  // 	var table string
   160  // 	tx, err := db.Begin()
   161  // 	if err != nil {
   162  // 		return err, "user_group"
   163  // 	}
   164  // 	defer tx.Rollback()
   165  
   166  // 	userGroupToUpdate := &UserGroup{IDRecord: IDRecord{ID: u.ID}}
   167  // 	pgerr := tx.Model(userGroupToUpdate).
   168  // 		Column("user_group.links", "Type").
   169  // 		WherePK().
   170  // 		Select()
   171  // 	if pgerr != nil {
   172  // 		return pgerr, "user_group"
   173  // 	}
   174  
   175  // 	columns := []string{
   176  // 		"updated_at",
   177  // 		// "pro",
   178  // 		// "publisher",
   179  // 		"links",
   180  // 		// "tags",
   181  // 		"display_name",
   182  // 		// "avatar",
   183  // 		"description",
   184  // 		"short_bio",
   185  // 		// "banner",
   186  // 		"group_email_address",
   187  // 	}
   188  
   189  // 	// User group type - changes allowed: user => artist, user => label
   190  // 	groupTaxonomy := new(GroupTaxonomy)
   191  // 	pgerr = tx.Model(groupTaxonomy).Where("type = ?", userGroup.Type.Type).First()
   192  // 	if pgerr != nil {
   193  // 		return pgerr, "group_taxonomy"
   194  // 	}
   195  // 	if userGroupToUpdate.Type.ID != groupTaxonomy.ID {
   196  // 		if userGroupToUpdate.Type.Type != "user" ||
   197  // 			(userGroupToUpdate.Type.Type == "user" && !(groupTaxonomy.Type == "artist" || groupTaxonomy.Type == "label")) {
   198  // 			twerr := twirp.InvalidArgumentError("type", "not allowed")
   199  // 			return twerr.(error), "user_group"
   200  // 		}
   201  // 		u.TypeID = groupTaxonomy.ID
   202  // 		columns = append(columns, "type_id")
   203  // 	}
   204  
   205  // 	// Update address
   206  // 	addressID, twerr := uuid.Parse(userGroup.Address.Id)
   207  // 	if twerr != nil {
   208  // 		return twerr, "street_address"
   209  // 	}
   210  // 	address := &StreetAddress{ID: addressID, Data: userGroup.Address.Data}
   211  // 	_, pgerr = tx.Model(address).Column("data").WherePK().Update()
   212  // 	// _, pgerr := db.Model(address).Set("data = ?", pg.Hstore(userGroup.Address.Data)).Where("id = ?id").Update()
   213  // 	if pgerr != nil {
   214  // 		return pgerr, "street_address"
   215  // 	}
   216  
   217  // 	// Update privacy
   218  // 	// privacyID, twerr := uuidpkg.GetUUIDFromString(userGroup.Privacy.ID)
   219  // 	// if twerr != nil {
   220  // 	// 	return twerr, "user_group_privacy"
   221  // 	// }
   222  // 	// privacy := &UserGroupPrivacy{
   223  // 	// 	ID:               privacyID,
   224  // 	// 	Private:          userGroup.Privacy.Private,
   225  // 	// 	OwnedTracks:      userGroup.Privacy.OwnedTracks,
   226  // 	// 	SupportedArtists: userGroup.Privacy.SupportedArtists,
   227  // 	// }
   228  // 	// _, pgerr = tx.Model(privacy).WherePK().Returning("*").UpdateNotNull()
   229  // 	// if pgerr != nil {
   230  // 	// 	return pgerr, "user_group_privacy"
   231  // 	// }
   232  
   233  // 	// Update tags
   234  // 	// tagIDs, pgerr := GetTagIDs(userGroup.Tags, tx)
   235  // 	// if pgerr != nil {
   236  // 	// 	return pgerr, "tag"
   237  // 	// }
   238  
   239  // 	// Update links
   240  // 	linkIDs, pgerr := getLinkIDs(userGroup.Links, tx)
   241  // 	if pgerr != nil {
   242  // 		return pgerr, "link"
   243  // 	}
   244  // 	// Delete links if needed
   245  // 	linkIDsToDelete := uuidpkg.Difference(userGroupToUpdate.Links, linkIDs)
   246  // 	if len(linkIDsToDelete) > 0 {
   247  // 		_, pgerr = tx.Model((*Link)(nil)).
   248  // 			Where("id in (?)", pg.In(linkIDsToDelete)).
   249  // 			Delete()
   250  // 		if pgerr != nil {
   251  // 			return pgerr, "link"
   252  // 		}
   253  // 	}
   254  
   255  // 	// Update user group
   256  // 	// u.Tags = tagIDs
   257  // 	u.Links = linkIDs
   258  // 	// u.RecommendedArtists = recommendedArtistIDs
   259  // 	u.UpdatedAt = time.Now()
   260  // 	_, pgerr = tx.Model(u).
   261  // 		Column(columns...).
   262  // 		WherePK().
   263  // 		Returning("*").
   264  // 		Update()
   265  // 	if pgerr != nil {
   266  // 		return pgerr, "user_group"
   267  // 	}
   268  
   269  // 	return tx.Commit(), table
   270  // }
   271  
   272  // func SearchUserGroups(query string, db *pg.DB) (*pbUser.SearchResults, twirp.Error) {
   273  // 	var userGroups []UserGroup
   274  
   275  // 	pgerr := db.Model(&userGroups).
   276  // 		Column("user_group.id", "user_group.display_name", "user_group.avatar", "Privacy", "Type").
   277  // 		Where("to_tsvector('english'::regconfig, COALESCE(display_name, '') || ' ' || COALESCE(f_arr2str(tags), '')) @@ (plainto_tsquery('english'::regconfig, ?)) = true", query).
   278  // 		Where("privacy.private = false").
   279  // 		Select()
   280  // 	if pgerr != nil {
   281  // 		return nil, errorpkg.CheckError(pgerr, "user_group")
   282  // 	}
   283  
   284  // 	var people []*pbUser.RelatedUserGroup
   285  // 	var artists []*pbUser.RelatedUserGroup
   286  // 	var labels []*pbUser.RelatedUserGroup
   287  // 	for _, userGroup := range userGroups {
   288  // 		searchUserGroup := &pbUser.RelatedUserGroup{
   289  // 			Id:          userGroup.ID.String(),
   290  // 			DisplayName: userGroup.DisplayName,
   291  // 			// Avatar:      userGroup.Avatar,
   292  // 		}
   293  // 		switch userGroup.TypeID {
   294  // 		case "user":
   295  // 			people = append(people, searchUserGroup)
   296  // 		case "artist":
   297  // 			artists = append(artists, searchUserGroup)
   298  // 		case "label":
   299  // 			labels = append(labels, searchUserGroup)
   300  // 		}
   301  // 	}
   302  // 	return &pbUser.SearchResults{
   303  // 		People:  people,
   304  // 		Artists: artists,
   305  // 		Labels:  labels,
   306  // 	}, nil
   307  // }
   308  
   309  // func (u *UserGroup) Delete(tx *pg.Tx) (error, string) {
   310  // 	pgerr := tx.Model(u).
   311  // 		Column("user_group.links", "user_group.followers", "user_group.recommended_by", "user_group.recommended_artists", "Address", "Privacy",
   312  // 			"OwnerOfTrackGroups", "LabelOfTrackGroups", "user_group.artist_of_tracks").
   313  // 		WherePK().
   314  // 		Select()
   315  // 	if pgerr != nil {
   316  // 		return pgerr, "user_group"
   317  // 	}
   318  
   319  // 	// These tracks contain the user group to delete as artist
   320  // 	// so we have to remove it from the tracks' artists list
   321  // 	// if len(u.ArtistOfTracks) > 0 {
   322  // 	// 	_, pgerr = tx.Exec(`
   323  // 	//     UPDATE tracks
   324  // 	//     SET artists = array_remove(artists, ?)
   325  // 	//     WHERE id IN (?)
   326  // 	//   `, u.ID, pg.In(u.ArtistOfTracks))
   327  // 	// 	if pgerr != nil {
   328  // 	// 		return pgerr, "track"
   329  // 	// 	}
   330  // 	// }
   331  
   332  // 	// These track groups contain the user group to delete as label
   333  // 	// so we have to set their label_id as null
   334  // 	// if len(u.LabelOfTrackGroups) > 0 {
   335  // 	// 	_, pgerr = tx.Model(&u.LabelOfTrackGroups).
   336  // 	// 		Set("label_id = uuid_nil()").
   337  // 	// 		Update()
   338  // 	// 	if pgerr != nil {
   339  // 	// 		return pgerr, "track"
   340  // 	// 	}
   341  // 	// }
   342  
   343  // 	// Delete track groups owned by user group to delete
   344  // 	// if a track is a release (lp, ep, single), its tracks are owned by the same user group
   345  // 	// and they'll be deleted as well
   346  // 	// for _, trackGroup := range u.OwnerOfTrackGroups {
   347  // 	// 	pgerr, table := trackGroup.Delete(tx)
   348  // 	// 	if pgerr != nil {
   349  // 	// 		return pgerr, table
   350  // 	// 	}
   351  // 	// }
   352  
   353  // 	if len(u.Links) > 0 {
   354  // 		_, pgerr = tx.Model((*Link)(nil)).
   355  // 			Where("id in (?)", pg.In(u.Links)).
   356  // 			Delete()
   357  // 		if pgerr != nil {
   358  // 			return pgerr, "link"
   359  // 		}
   360  // 	}
   361  
   362  // if len(u.RecommendedBy) > 0 {
   363  // 	_, pgerr = tx.Exec(`
   364  //     UPDATE user_groups
   365  //     SET recommended_artists = array_remove(recommended_artists, ?)
   366  //     WHERE id IN (?)
   367  //   `, u.ID, pg.In(u.RecommendedBy))
   368  // 	if pgerr != nil {
   369  // 		return pgerr, "user_group"
   370  // 	}
   371  // }
   372  
   373  // if len(u.RecommendedArtists) > 0 {
   374  // 	_, pgerr = tx.Exec(`
   375  //     UPDATE user_groups
   376  //     SET recommended_by = array_remove(recommended_by, ?)
   377  //     WHERE id IN (?)
   378  //   `, u.ID, pg.In(u.RecommendedArtists))
   379  // 	if pgerr != nil {
   380  // 		return pgerr, "user_group"
   381  // 	}
   382  // }
   383  
   384  // if len(u.Followers) > 0 {
   385  // 	_, pgerr = tx.Exec(`
   386  //     UPDATE users
   387  //     SET followed_groups = array_remove(followed_groups, ?)
   388  //     WHERE id IN (?)
   389  //   `, u.ID, pg.In(u.Followers))
   390  // 	if pgerr != nil {
   391  // 		return pgerr, "user"
   392  // 	}
   393  // }
   394  
   395  // 	var userGroupMembers []UserGroupMember
   396  // 	_, pgerr = tx.Model(&userGroupMembers).
   397  // 		Where("user_group_id = ?", u.ID).
   398  // 		WhereOr("member_id = ?", u.ID).
   399  // 		Delete()
   400  // 	if pgerr != nil {
   401  // 		return pgerr, "user_group_member"
   402  // 	}
   403  
   404  // 	_, pgerr = tx.Model(u).WherePK().Delete()
   405  // 	if pgerr != nil {
   406  // 		return pgerr, "user_group"
   407  // 	}
   408  
   409  // 	_, pgerr = tx.Model(u.Address).WherePK().Delete()
   410  // 	if pgerr != nil {
   411  // 		return pgerr, "street_address"
   412  // 	}
   413  
   414  // 	// _, pgerr = tx.Model(u.Privacy).WherePK().Delete()
   415  // 	// if pgerr != nil {
   416  // 	// 	return pgerr, "user_group_privacy"
   417  // 	// }
   418  
   419  // 	return nil, ""
   420  // }
   421  
   422  func (u *UserGroup) AddRecommended(db *pg.DB, recommendedID uuid.UUID) (error, string) {
   423  	var table string
   424  	tx, err := db.Begin()
   425  	if err != nil {
   426  		return err, table
   427  	}
   428  	defer tx.Rollback()
   429  
   430  	res, pgerr := tx.Exec(`
   431      UPDATE user_groups
   432      SET recommended_artists = (select array_agg(distinct e) from unnest(recommended_artists || ?) e)
   433      WHERE id = ?
   434    `, pg.Array([]uuid.UUID{recommendedID}), u.ID)
   435  	if res.RowsAffected() == 0 {
   436  		return pg.ErrNoRows, "user_group"
   437  	}
   438  	if pgerr != nil {
   439  		return pgerr, "user_group"
   440  	}
   441  
   442  	res, pgerr = tx.Exec(`
   443      UPDATE user_groups
   444      SET recommended_by = (select array_agg(distinct e) from unnest(recommended_by || ?) e)
   445      WHERE id = ?
   446    `, pg.Array([]uuid.UUID{u.ID}), recommendedID)
   447  	if res.RowsAffected() == 0 {
   448  		return pg.ErrNoRows, "recommended"
   449  	}
   450  	if pgerr != nil {
   451  		return pgerr, "user_group"
   452  	}
   453  
   454  	return tx.Commit(), table
   455  }
   456  
   457  func (u *UserGroup) RemoveRecommended(db *pg.DB, recommendedID uuid.UUID) (error, string) {
   458  	var table string
   459  	tx, err := db.Begin()
   460  	if err != nil {
   461  		return err, table
   462  	}
   463  	defer tx.Rollback()
   464  
   465  	res, pgerr := tx.Exec(`
   466      UPDATE user_groups
   467      SET recommended_artists = array_remove(recommended_artists, ?)
   468      WHERE id = ?
   469    `, recommendedID, u.ID)
   470  	if res.RowsAffected() == 0 {
   471  		return pg.ErrNoRows, "user_group"
   472  	}
   473  	if pgerr != nil {
   474  		return pgerr, "user_group"
   475  	}
   476  
   477  	res, pgerr = tx.Exec(`
   478      UPDATE user_groups
   479      SET recommended_by = array_remove(recommended_by, ?)
   480      WHERE id = ?
   481    `, u.ID, recommendedID)
   482  	if res.RowsAffected() == 0 {
   483  		return pg.ErrNoRows, "recommended"
   484  	}
   485  	if pgerr != nil {
   486  		return pgerr, "user_group"
   487  	}
   488  
   489  	return tx.Commit(), table
   490  }
   491  
   492  // Select user groups in db with given 'ids'
   493  // Return slice of UserGroup response
   494  func GetRelatedUserGroups(ids []uuid.UUID, db orm.DB) ([]*pbUser.RelatedUserGroup, error) {
   495  	groupsResponse := make([]*pbUser.RelatedUserGroup, len(ids))
   496  	if len(ids) > 0 {
   497  		var groups []UserGroup
   498  		pgerr := db.Model(&groups).
   499  			Where("id in (?)", pg.In(ids)).
   500  			Select()
   501  		if pgerr != nil {
   502  			return nil, pgerr
   503  		}
   504  		for i, group := range groups {
   505  			groupsResponse[i] = &pbUser.RelatedUserGroup{
   506  				Id:          group.ID.String(),
   507  				DisplayName: group.DisplayName,
   508  				// Avatar:      group.Avatar,
   509  			}
   510  		}
   511  	}
   512  
   513  	return groupsResponse, nil
   514  }
   515  
   516  // Select user groups in db with given ids in 'userGroups'
   517  // Return ids slice
   518  // Used in CreateUserGroup/UpdateUserGroup to add/update ids slice to recommended Artists
   519  func GetRelatedUserGroupIDs(userGroups []*pbUser.RelatedUserGroup, db *pg.Tx) ([]uuid.UUID, error) {
   520  	relatedUserGroups := make([]*UserGroup, len(userGroups))
   521  	relatedUserGroupIDs := make([]uuid.UUID, len(userGroups))
   522  	for i, userGroup := range userGroups {
   523  		id, twerr := uuid.Parse(userGroup.Id)
   524  		if twerr != nil {
   525  			return nil, twerr.(error)
   526  		}
   527  		relatedUserGroups[i] = &UserGroup{IDRecord: IDRecord{ID: id}}
   528  		pgerr := db.Model(relatedUserGroups[i]).
   529  			WherePK().
   530  			Returning("id", "display_name", "avatar").
   531  			Select()
   532  		if pgerr != nil {
   533  			return nil, pgerr
   534  		}
   535  		userGroup.DisplayName = relatedUserGroups[i].DisplayName
   536  		// userGroup.Avatar = relatedUserGroups[i].Avatar
   537  		relatedUserGroupIDs[i] = relatedUserGroups[i].ID
   538  	}
   539  	return relatedUserGroupIDs, nil
   540  }
   541  
   542  func getLinkIDs(l []*pbUser.Link, db *pg.Tx) ([]uuid.UUID, error) {
   543  	links := make([]*Link, len(l))
   544  	linkIDs := make([]uuid.UUID, len(l))
   545  	for i, link := range l {
   546  		if link.ID == "" {
   547  			links[i] = &Link{Platform: link.Platform, URI: link.Uri}
   548  			_, pgerr := db.Model(links[i]).Returning("*").Insert()
   549  			if pgerr != nil {
   550  				return nil, pgerr
   551  			}
   552  			linkIDs[i] = links[i].ID
   553  			link.ID = links[i].ID.String()
   554  		} else {
   555  			linkID, twerr := uuid.Parse(link.ID)
   556  			if twerr != nil {
   557  				return nil, twerr.(error)
   558  			}
   559  			linkIDs[i] = linkID
   560  		}
   561  	}
   562  	return linkIDs, nil
   563  }
   564  
   565  /*type TrackAnalytics struct {
   566    ID uuid.UUID
   567    Title string
   568    PaidPlays int32
   569    FreePlays int32
   570    TotalCredits float32
   571  }
   572  
   573  // DEPRECATED - moved to Payment API
   574  func (u *UserGroup) GetUserGroupTrackAnalytics(db *pg.DB) ([]*pbUser.TrackAnalytics, twirp.Error) {
   575    pgerr := db.Model(u).
   576      Column("OwnerOfTracks").
   577      WherePK().
   578      Select()
   579    if pgerr != nil {
   580      return nil, errorpkg.CheckError(pgerr, "user_group")
   581    }
   582    tracks := make([]TrackAnalytics, len(u.OwnerOfTracks))
   583    trackIDs := make([]uuid.UUID, len(u.OwnerOfTracks))
   584    for i, track := range(u.OwnerOfTracks) {
   585      tracks[i] = TrackAnalytics{
   586        Title: track.Title,
   587      }
   588      trackIDs[i] = track.ID
   589    }
   590    artistTrackAnalytics := make([]*pbUser.TrackAnalytics, len(tracks))
   591  
   592    if len(u.OwnerOfTracks) > 0 {
   593      _, pgerr := db.Query(&tracks, `
   594        SELECT play.track_id AS id,
   595          count(case when play.type = 'paid' then 1 else null end) AS paid_plays,
   596          count(case when play.type = 'free' then 1 else null end) AS free_plays,
   597          SUM(play.credits) AS total_credits
   598        FROM plays AS play
   599        WHERE play.track_id IN (?)
   600        GROUP BY play.track_id
   601      `, pg.In(trackIDs))
   602      if pgerr != nil {
   603        return nil, errorpkg.CheckError(pgerr, "play")
   604      }
   605      for i, track := range(tracks) {
   606        artistTrackAnalytics[i] = &pbUser.TrackAnalytics{
   607          ID: track.ID.String(),
   608          Title: track.Title,
   609          TotalPlays: track.PaidPlays + track.FreePlays,
   610          PaidPlays: track.PaidPlays,
   611          FreePlays: track.FreePlays,
   612          TotalCredits: float32(track.TotalCredits),
   613          UserGroupCredits: 0.7*float32(track.TotalCredits),
   614          ResonateCredits: 0.3*float32(track.TotalCredits),
   615        }
   616      }
   617    }
   618  
   619    return artistTrackAnalytics, nil
   620  }*/