<commit_before>/**
 * Extend module's NODE_PATH
 * HACK: temporary solution
 */

require('node-path')(module);

/**
 * Module dependencies.
 */

var mongoose = require('mongoose');
var Topic = mongoose.model('Topic');
var commentApi = require('./comment');
var tagApi = require('./tag');
var utils = require('lib/utils');
var pluck = utils.pluck;
var log = require('debug')('democracyos:db-api:topic');

/**
 * Get all topics
 *
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topics' list items found or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.all = function all(fn) {
  log('Looking for all topics.')

  Topic
  .find({ deletedAt: null })
  .select('id topicId mediaTitle tag participants votes createdAt updatedAt closingAt publishedAt deletedAt status open closed links author authorUrl')
  .populate('tag', 'id hash name color image')
  .exec(function (err, topics) {
    if (err) {
      log('Found error %j', err);
      return fn(err);
    }

    log('Delivering topics %j', pluck(topics, 'id'));
    fn(null, topics);
  });

  return this;
};

/**
 * Search topics from query
 *
 * @param {Object} query filter
 * @param {Function} fn callback function
 *   - 'err' error found while process or `null`
 *   - 'topics' list of topics objects found or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.search = function search(query, fn) {
  log('Searching for topics matching %j', query);

  Topic
    .find(query, function(err, topics) {
    if (err) {
      log('Found error: %j', err);
      return fn(err);
    }

    log('Found topics %j for %j', pluck(topics, 'id'), query);
    fn(null, topics);
  });

  return this;
};

/**
 * Creates topic
 *
 * @param {Object} data to create topic
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topic' item created or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.create = function create(data, fn) {
  log('Creating new topic %j', data);
  ensureTag(data, function(err, data) {
    if (err) return log('Found error from topic creation: %s', err.message), fn(err);
    createTopic(data, fn);
  });

  return this;
};

function ensureTag(data, fn) {
  tagApi.searchOne(data.hash, function(err, tags) {
    if (err) return log('Found error %s', err.message), fn(err);

    if (!tags.length) {
      if (!data.tag) return fn(new Error('No tag provided'));
      tagApi.create(data.tag, function(err, tag) {
        if (err) return log('Found error from tag creation: %s', err.message), fn(err);
        data.tag = tag;
        fn(null, data);
      });
    } else {
      fn(null, data);
    }
  });
};

function createTopic(data, fn) {
  var topic = new Topic(data);
  topic.save(onsave);

  function onsave(err) {
    if (err) return log('Found error %s', err), fn(err);

    log('Saved topic %s', topic.id);
    fn(null, topic);
  }
};

/**
 * Update topic by `id` and `data`
 *
 * @param {ObjectId|String} data to create topic
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topic' item created or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.update = function update(id, data, fn) {
  log('Updating topic %s with %j', id, data);

  // look for tag for nesting reference
  log('Looking for tag %s in database.', data.tag);
  tagApi.searchOne(data.tag, function (err, tag) {
    if (err) return log('Found error %s', err.message), fn(err);

    // now set `data.tag` to `tag`'s document _id
    data.tag = tag;

    // get topic
    exports.get(id, onget);

    function onget(err, topic) {
      if (err) {
        log('Found error %s', err.message);
        return fn(err);
      };

      var clauses = data.clauses || [];
      delete data.clauses;

      clauses.forEach(function(clause) {
        log('clause: %j', clause);
        if (clause.id) {
          console.dir(topic.clauses)
          var c = topic.clauses.id(clause.id);
          // c.update(clause);
          // clause.centered = !!clause.centered;
          c.set(clause);
        } else {
          topic.clauses.addToSet(clause);
        }
      });

      var links = data.links || [];
      delete data.links;

      links.forEach(function(link) {
        var l = topic.links.id(link.id);
        l.set(link);
      });
<commit_msg>[topic-api] On updating a topic, delete non-submitted clauses<commit_after>/**
 * Extend module's NODE_PATH
 * HACK: temporary solution
 */

require('node-path')(module);

/**
 * Module dependencies.
 */

var mongoose = require('mongoose');
var Topic = mongoose.model('Topic');
var commentApi = require('./comment');
var tagApi = require('./tag');
var utils = require('lib/utils');
var pluck = utils.pluck;
var log = require('debug')('democracyos:db-api:topic');

/**
 * Get all topics
 *
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topics' list items found or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.all = function all(fn) {
  log('Looking for all topics.')

  Topic
  .find({ deletedAt: null })
  .select('id topicId mediaTitle tag participants votes createdAt updatedAt closingAt publishedAt deletedAt status open closed links author authorUrl')
  .populate('tag', 'id hash name color image')
  .exec(function (err, topics) {
    if (err) {
      log('Found error %j', err);
      return fn(err);
    }

    log('Delivering topics %j', pluck(topics, 'id'));
    fn(null, topics);
  });

  return this;
};

/**
 * Search topics from query
 *
 * @param {Object} query filter
 * @param {Function} fn callback function
 *   - 'err' error found while process or `null`
 *   - 'topics' list of topics objects found or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.search = function search(query, fn) {
  log('Searching for topics matching %j', query);

  Topic
    .find(query, function(err, topics) {
    if (err) {
      log('Found error: %j', err);
      return fn(err);
    }

    log('Found topics %j for %j', pluck(topics, 'id'), query);
    fn(null, topics);
  });

  return this;
};

/**
 * Creates topic
 *
 * @param {Object} data to create topic
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topic' item created or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.create = function create(data, fn) {
  log('Creating new topic %j', data);
  ensureTag(data, function(err, data) {
    if (err) return log('Found error from topic creation: %s', err.message), fn(err);
    createTopic(data, fn);
  });

  return this;
};

function ensureTag(data, fn) {
  tagApi.searchOne(data.hash, function(err, tags) {
    if (err) return log('Found error %s', err.message), fn(err);

    if (!tags.length) {
      if (!data.tag) return fn(new Error('No tag provided'));
      tagApi.create(data.tag, function(err, tag) {
        if (err) return log('Found error from tag creation: %s', err.message), fn(err);
        data.tag = tag;
        fn(null, data);
      });
    } else {
      fn(null, data);
    }
  });
};

function createTopic(data, fn) {
  var topic = new Topic(data);
  topic.save(onsave);

  function onsave(err) {
    if (err) return log('Found error %s', err), fn(err);

    log('Saved topic %s', topic.id);
    fn(null, topic);
  }
};

/**
 * Update topic by `id` and `data`
 *
 * @param {ObjectId|String} data to create topic
 * @param {Function} fn callback function
 *   - 'err' error found on query or `null`
 *   - 'topic' item created or `undefined`
 * @return {Module} `topic` module
 * @api public
 */

exports.update = function update(id, data, fn) {
  log('Updating topic %s with %j', id, data);

  // look for tag for nesting reference
  log('Looking for tag %s in database.', data.tag);
  tagApi.searchOne(data.tag, function (err, tag) {
    if (err) return log('Found error %s', err.message), fn(err);

    // now set `data.tag` to `tag`'s document _id
    data.tag = tag;

    // get topic
    exports.get(id, onget);

    function onget(err, topic) {
      if (err) {
        log('Found error %s', err.message);
        return fn(err);
      };

      var clauses = data.clauses || [];
      delete data.clauses;

      // Delete non submitted clauses
      var submittedIds = clauses.map(function(c) { return c.id });
      var persistedIds = topic.clauses.map(function(c) { return c.id });
      var clausesToDelete = persistedIds.filter(function(i) { return !~submittedIds.indexOf(i) });
      log('submittedIds: %j', submittedIds);
      log('persistedIds: %j', persistedIds);
      log('clausesToDelete: %j', clausesToDelete);
      clausesToDelete.forEach(function(id) { topic.clauses.pull({ _id: id }) });

      // Add new clauses or update existing
      clauses.forEach(function(clause) {
        log('clause: %j', clause);
        if (clause.id) {
          var c = topic.clauses.id(clause.id);
          c.set(clause);
        } else {
          topic.clauses.addToSet(clause);
        }
      });

      var links = data.links || [];
      delete data.links;

      links.forEach(function(link) {
        var l = topic.links.id(link.id);
        l.set(link);
      });

{'fraction_non_alphanumeric': 0.15732388316151202, 'fraction_numerical': 0.0, 'mean_word_length': 2.931194596876319, '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': '1505308', 'n_tokens_mistral': 3059, 'n_tokens_neox': 2999, 'n_words': 907}