<commit_before>package models

import (
	// "errors"
	"fmt"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"math"
	"time"
)

var (
	FETCH_LIMIT = 50
)

type Notification struct {
	Id int64 `json:"id"`
	// notification recipient
	AccountId int64 `json:"accountId" sql:"NOT NULL"`
	// notification content foreign key
	NotificationContentId int64 `json:"notificationContentId" sql:"NOT NULL"`
	// glanced information
	Glanced bool `json:"glanced" sql:"NOT NULL"`
	// last notifier addition time
	UpdatedAt time.Time `json:"updatedAt" sql:"NOT NULL"`
}

func (n *Notification) GetId() int64 {
	return n.Id
}

func (n Notification) TableName() string {
	return "api.notification"
}

func NewNotification() *Notification {
	return &Notification{}
}

func (n *Notification) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *Notification) Create() error {
	s := map[string]interface{}{
		"account_id":              n.AccountId,
		"notification_content_id": n.NotificationContentId,
	}
	q := bongo.NewQS(s)
	if err := n.One(q); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}

		return bongo.B.Create(n)
	}

	n.Glanced = false

	return bongo.B.Update(n)
}

func (n *Notification) List(q *Query) (*NotificationResponse, error) {
	if err := n.isAccountValid(q.AccountId); err != nil {
		return nil, err
	}

	limit := math.Min(float64(q.Limit), 8.0)
	q.Limit = int(limit)

	response := &NotificationResponse{}
	result, err := n.getDecoratedList(q)
	if err != nil {
		return response, err
	}

	result, err = populateActors(q.AccountId, result)
	if err != nil {
		return response, err
	}

	response.Notifications = result
	response.UnreadCount = getUnreadNotificationCount(result)

	return response, nil
}

func (n *Notification) Some(data interface{}, q *bongo.Query) error {

	return bongo.B.Some(n, data, q)
}

func (n *Notification) fetchByAccountId(q *Query) ([]Notification, error) {
	var notifications []Notification
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"account_id": q.AccountId,
		},
		Sort: map[string]string{
			"updated_at": "desc",
		},
		Pagination: bongo.Pagination{
			Limit: FETCH_LIMIT,
			Skip:  q.Skip,
		},
	}
	if err := bongo.B.Some(n, &notifications, query); err != nil {
		return nil, err
	}

	return notifications, nil
}

// prepareNotifications
func (n *Notification) getDecoratedList(q *Query) ([]NotificationContainer, error) {
	result := make([]NotificationContainer, 0)
	resultMap := make(map[string]struct{}, 0)

	var err error
	result, err = n.decorateContents(result, &resultMap, q)
	if err != nil {
		return nil, err
	}

	return result, nil
}

// decorateContents recursively fetches notification data till it reaches the FETCH_LIMIT
func (n *Notification) decorateContents(result []NotificationContainer, resultMap *map[string]struct{}, q *Query) ([]NotificationContainer, error) {

	nList, err := n.fetchByAccountId(q)
	if nList == nil {
		return result, nil
	}

	// fetch all notification content relationships
	ncMap, err := fetchRelatedContents(nList)
	if err != nil {
		return nil, err
	}

	for _, n := range nList {
		nc := ncMap[n.NotificationContentId]
		key := prepareResultKey(&nc)
		if _, ok := (*resultMap)[key]; !ok {
			container := buildNotificationContainer(&nc)
			container.UpdatedAt = n.UpdatedAt // fetch latest notification timestamp
			container.Glanced = n.Glanced
			(*resultMap)[key] = struct{}{}
			result = append(result, container)
			if len(result) == q.Limit {
				return result, nil
			}
		}
	}

	if len(nList) == FETCH_LIMIT {
		q.Skip += FETCH_LIMIT
		return n.decorateContents(result, resultMap, q)
	}

	return result, nil
}

func buildNotificationContainer(nc *NotificationContent) NotificationContainer {
	return NotificationContainer{
		TargetId:     nc.TargetId,
		TypeConstant: nc.TypeConstant,
	}
}

func prepareResultKey(nc *NotificationContent) string {
	return fmt.Sprintf("%s_%d", nc.TypeConstant, nc.TargetId)
}

func fetchRelatedContents(nl []Notification) (map[int64]NotificationContent, error) {
	notificationContentIds := make([]int64, 0)
	for _, n := range nl {
		notificationContentIds = append(notificationContentIds, n.NotificationContentId)
	}
	nc := NewNotificationContent()
	return nc.FetchMapByIds(notificationContentIds)
}

func (n *Notification) FetchContent() (*NotificationContent, error) {
	nc := NewNotificationContent()
	nc.Id = n.NotificationContentId
	if err := nc.Fetch(); err != nil {
		return nil, err
	}

	return nc, nil
}

func (n *Notification) Follow(followerId int64) error {
	fn := NewFollowNotification()
	fn.FollowerId = followerId
	fn.FolloweeId = n.AccountId

	return CreateNotification(fn)
}
<commit_msg>Social: JoinGroup/LeaveGroup methods are added to notification<commit_after>package models

import (
	// "errors"
	"fmt"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"math"
	"time"
)

var (
	FETCH_LIMIT = 50
)

type Notification struct {
	Id int64 `json:"id"`
	// notification recipient
	AccountId int64 `json:"accountId" sql:"NOT NULL"`
	// notification content foreign key
	NotificationContentId int64 `json:"notificationContentId" sql:"NOT NULL"`
	// glanced information
	Glanced bool `json:"glanced" sql:"NOT NULL"`
	// last notifier addition time
	UpdatedAt time.Time `json:"updatedAt" sql:"NOT NULL"`
}

func (n *Notification) GetId() int64 {
	return n.Id
}

func (n Notification) TableName() string {
	return "api.notification"
}

func NewNotification() *Notification {
	return &Notification{}
}

func (n *Notification) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *Notification) Create() error {
	s := map[string]interface{}{
		"account_id":              n.AccountId,
		"notification_content_id": n.NotificationContentId,
	}
	q := bongo.NewQS(s)
	if err := n.One(q); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}

		return bongo.B.Create(n)
	}

	n.Glanced = false

	return bongo.B.Update(n)
}

func (n *Notification) List(q *Query) (*NotificationResponse, error) {
	if err := n.isAccountValid(q.AccountId); err != nil {
		return nil, err
	}

	limit := math.Min(float64(q.Limit), 8.0)
	q.Limit = int(limit)

	response := &NotificationResponse{}
	result, err := n.getDecoratedList(q)
	if err != nil {
		return response, err
	}

	result, err = populateActors(q.AccountId, result)
	if err != nil {
		return response, err
	}

	response.Notifications = result
	response.UnreadCount = getUnreadNotificationCount(result)

	return response, nil
}

func (n *Notification) Some(data interface{}, q *bongo.Query) error {

	return bongo.B.Some(n, data, q)
}

func (n *Notification) fetchByAccountId(q *Query) ([]Notification, error) {
	var notifications []Notification
	query := &bongo.Query{
		Selector: map[string]interface{}{
			"account_id": q.AccountId,
		},
		Sort: map[string]string{
			"updated_at": "desc",
		},
		Pagination: bongo.Pagination{
			Limit: FETCH_LIMIT,
			Skip:  q.Skip,
		},
	}
	if err := bongo.B.Some(n, &notifications, query); err != nil {
		return nil, err
	}

	return notifications, nil
}

// prepareNotifications
func (n *Notification) getDecoratedList(q *Query) ([]NotificationContainer, error) {
	result := make([]NotificationContainer, 0)
	resultMap := make(map[string]struct{}, 0)

	var err error
	result, err = n.decorateContents(result, &resultMap, q)
	if err != nil {
		return nil, err
	}

	return result, nil
}

// decorateContents recursively fetches notification data till it reaches the FETCH_LIMIT
func (n *Notification) decorateContents(result []NotificationContainer, resultMap *map[string]struct{}, q *Query) ([]NotificationContainer, error) {

	nList, err := n.fetchByAccountId(q)
	if nList == nil {
		return result, nil
	}

	// fetch all notification content relationships
	ncMap, err := fetchRelatedContents(nList)
	if err != nil {
		return nil, err
	}

	for _, n := range nList {
		nc := ncMap[n.NotificationContentId]
		key := prepareResultKey(&nc)
		if _, ok := (*resultMap)[key]; !ok {
			container := buildNotificationContainer(&nc)
			container.UpdatedAt = n.UpdatedAt // fetch latest notification timestamp
			container.Glanced = n.Glanced
			(*resultMap)[key] = struct{}{}
			result = append(result, container)
			if len(result) == q.Limit {
				return result, nil
			}
		}
	}

	if len(nList) == FETCH_LIMIT {
		q.Skip += FETCH_LIMIT
		return n.decorateContents(result, resultMap, q)
	}

	return result, nil
}

func buildNotificationContainer(nc *NotificationContent) NotificationContainer {
	return NotificationContainer{
		TargetId:     nc.TargetId,
		TypeConstant: nc.TypeConstant,
	}
}

func prepareResultKey(nc *NotificationContent) string {
	return fmt.Sprintf("%s_%d", nc.TypeConstant, nc.TargetId)
}

func fetchRelatedContents(nl []Notification) (map[int64]NotificationContent, error) {
	notificationContentIds := make([]int64, 0)
	for _, n := range nl {
		notificationContentIds = append(notificationContentIds, n.NotificationContentId)
	}
	nc := NewNotificationContent()
	return nc.FetchMapByIds(notificationContentIds)
}

func (n *Notification) FetchContent() (*NotificationContent, error) {
	nc := NewNotificationContent()
	nc.Id = n.NotificationContentId
	if err := nc.Fetch(); err != nil {
		return nil, err
	}

	return nc, nil
}

func (n *Notification) Follow(followerId int64) error {
	fn := NewFollowNotification()
	fn.FollowerId = followerId
	fn.FolloweeId = n.AccountId

	return CreateNotification(fn)
}

func (n *Notification) JoinGroup(a *Activity, admins []int64) error {
	a.TypeConstant = NotificationContent_TYPE_JOIN

	return n.interactGroup(a, admins)
}

func (n *Notification) LeaveGroup(a *Activity, admins []int64) error {
	a.TypeConstant = NotificationContent_TYPE_LEAVE

	return n.interactGroup(a, admins)
}

func (n *Notification) interactGroup(a *Activity, admins []int64) error {
	gn := NewGroupNotification(a.TypeConstant)
	gn.NotifierId = a.ActorId
	gn.TargetId = a.TargetId
	gn.Admins = admins

	if err := a.Create(); err != nil {
		return err
	}

	return CreateNotification(gn)
}

{'fraction_non_alphanumeric': 0.13007804682809687, 'fraction_numerical': 0.005203121873123874, 'mean_word_length': 4.5220994475138125, 'pattern_counts': {'":': 8, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3616209', 'n_tokens_mistral': 3525, 'n_tokens_neox': 3320, 'n_words': 855}