<commit_before>var commentAttributes = function(comment) {
  var attrs = _.pick(comment.toJSON(), 'id', 'comment_text', 'created_at', 'user');
  return _.extend({
    isThanked: _.isEmpty(comment.relations.thanks)
  }, attrs);
};

var createComment = function(commenterId, text, post) {
  var text = RichText.sanitize(text),
    attrs = {
      comment_text: text,
      created_at: new Date(),
      post_id: post.id,
      user_id: commenterId,
      active: true
    };

  return bookshelf.transaction(function(trx) {
    return new Comment(attrs).save(null, {transacting: trx})
    .tap(function(comment) {
      // update number of comments on post
      return Aggregate.count(post.comments(), {transacting: trx})
      .then(function(numComments) {
        return post.save({
          num_comments: numComments,
          updated_at: new Date()
        }, {patch: true, transacting: trx});
      });
    })
    .tap(function(comment) {
      return comment.load(['user', 'post', 'post.communities', 'post.creator'], {transacting: trx})
    })
    .tap(function(comment) {

      return post.load('followers', {transacting: trx}).then(function(post) {
        // find all existing followers and all mentioned users
        // (there may be some users in both groups)
        return [
          post.relations.followers.pluck('user_id'),
          RichText.getUserMentions(text)
        ];

      }).spread(function(existing, mentioned) {

        return Promise.join(
          // create activity and send mention notification to all mentioned users
          Promise.map(mentioned, function(userId) {
            return Promise.join(
              Queue.classMethod('Comment', 'sendNotificationEmail', {
                recipientId: userId,
                commentId: comment.id,
                version: 'mention'
              }),
              Activity.forComment(comment, userId, Activity.Action.Mention).save({}, {transacting: trx}),
              Comment.sendPushNotification(userId, comment, 'mention', {transacting: trx}),
              User.incNewNotificationCount(userId, trx)
            );
          }),

          // create activity and send comment notification to all followers,
          // except the commenter and mentioned users
          Promise.map(_.difference(_.without(existing, commenterId), mentioned), function(userId) {
            return Promise.join(
              Queue.classMethod('Comment', 'sendNotificationEmail', {
                recipientId: userId,
                commentId: comment.id,
                version: 'default'
              }),
              Activity.forComment(comment, userId, Activity.Action.Comment).save({}, {transacting: trx}),
              Comment.sendPushNotification(userId, comment, 'default', {transacting: trx}),
              User.query().where({id: userId}).increment('new_notification_count', 1).transacting(trx)
            );
          }),

          // add all mentioned users and the commenter as followers, if not already following
          post.addFollowers(_.difference(mentioned.concat(commenterId), existing), commenterId, {transacting: trx})
        );
      });

    });
  }); // transaction

};

module.exports = {

  findForPost: function(req, res) {
    Comment.query(function(qb) {
      qb.where({post_id: res.locals.post.id, active: true});
      qb.orderBy('id', 'asc');
    }).fetchAll({withRelated: [
      {user: function(qb) {
        qb.column('id', 'name', 'avatar_url');
      }},
      {thanks: function(qb) {
        qb.where('thanked_by_id', req.session.userId);
      }}
    ]}).then(function(comments) {
      res.ok(comments.map(commentAttributes));
    })
    .catch(res.serverError.bind(res));
  },

  create: function(req, res) {
    return createComment(req.session.userId, req.param('text'), res.locals.post)<commit_msg>handle edge case with commenting<commit_after>var commentAttributes = function(comment) {
  var attrs = _.pick(comment.toJSON(), 'id', 'comment_text', 'created_at', 'user');
  return _.extend({
    isThanked: _.isEmpty(comment.relations.thanks)
  }, attrs);
};

var createComment = function(commenterId, text, post) {
  var text = RichText.sanitize(text),
    attrs = {
      comment_text: text,
      created_at: new Date(),
      post_id: post.id,
      user_id: commenterId,
      active: true
    };

  return bookshelf.transaction(function(trx) {
    return new Comment(attrs).save(null, {transacting: trx})
    .tap(function(comment) {
      // update number of comments on post
      return Aggregate.count(post.comments(), {transacting: trx})
      .then(function(numComments) {
        return post.save({
          num_comments: numComments,
          updated_at: new Date()
        }, {patch: true, transacting: trx});
      });
    })
    .tap(function(comment) {
      return comment.load(['user', 'post', 'post.communities', 'post.creator'], {transacting: trx})
    })
    .tap(function(comment) {

      return post.load('followers', {transacting: trx}).then(function(post) {
        // find all existing followers and all mentioned users
        // (there may be some users in both groups)
        return [
          post.relations.followers.pluck('user_id'),
          RichText.getUserMentions(text)
        ];

      }).spread(function(existing, mentioned) {

        var newFollowers = _.difference(_.uniq(mentioned.concat(commenterId)), existing),
          unmentionedOldFollowers = _.difference(_.without(existing, commenterId), mentioned);

        return Promise.join(
          // create activity and send mention notification to all mentioned users
          Promise.map(mentioned, function(userId) {
            return Promise.join(
              Queue.classMethod('Comment', 'sendNotificationEmail', {
                recipientId: userId,
                commentId: comment.id,
                version: 'mention'
              }),
              Activity.forComment(comment, userId, Activity.Action.Mention).save({}, {transacting: trx}),
              Comment.sendPushNotification(userId, comment, 'mention', {transacting: trx}),
              User.incNewNotificationCount(userId, trx)
            );
          }),

          // create activity and send comment notification to all followers,
          // except the commenter and mentioned users
          Promise.map(unmentionedOldFollowers, function(userId) {
            return Promise.join(
              Queue.classMethod('Comment', 'sendNotificationEmail', {
                recipientId: userId,
                commentId: comment.id,
                version: 'default'
              }),
              Activity.forComment(comment, userId, Activity.Action.Comment).save({}, {transacting: trx}),
              Comment.sendPushNotification(userId, comment, 'default', {transacting: trx}),
              User.query().where({id: userId}).increment('new_notification_count', 1).transacting(trx)
            );
          }),

          // add all mentioned users and the commenter as followers, if not already following
          post.addFollowers(newFollowers, commenterId, {transacting: trx})
        );
      });

    });
  }); // transaction

};

module.exports = {

  findForPost: function(req, res) {
    Comment.query(function(qb) {
      qb.where({post_id: res.locals.post.id, active: true});
      qb.orderBy('id', 'asc');
    }).fetchAll({withRelated: [
      {user: function(qb) {
        qb.column('id', 'name', 'avatar_url');
      }},
      {thanks: function(qb) {
        qb.where('thanked_by_id', req.session.userId);
      }}
    ]}).then(function(comments) {
      res.ok(comments.map(commentAttributes));
    })
    .catch(res.serverError.bind(res));
  },

  create: function(req, res) {
    return createComment(req.session.userId, req.param('text'), res.locals.post)
{'fraction_non_alphanumeric': 0.13455618265777322, 'fraction_numerical': 0.0002565418163160595, 'mean_word_length': 2.528054298642534, 'pattern_counts': {'":': 0, '<': 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': '3851342', 'n_tokens_mistral': 2244, 'n_tokens_neox': 2180, 'n_words': 462}