<commit_before>/* global sails */
"use strict";

var request = require('request-promise');
var _ = require('lodash');
var Promise = require("bluebird");
const ResearchEntity = require('../lib/ResearchEntity');

module.exports = {
    attributes: {},
    getDocuments: function (ResearchEntityModel, researchEntityId, query, skipCopiedCheck) {
        var self = this;
        var connector = query.where.connector;
        if (!connector)
            throw new Error('A Connector parameter is necessary');
        return ResearchEntityModel.findOneById(researchEntityId)
            .then(function (researchEntity) {
                var reqConfig;
                switch (connector) {
                    case'Publications':
                        reqConfig = self.getPublicationsConfig(researchEntity, query);
                        break;
                    case'ORCID':
                        reqConfig = self.getOrcidConfig(researchEntity, query);
                        break;
                    case'Scopus':
                        reqConfig = self.getScopusConfig(researchEntity, query);
                        break;
                    default:
                    //sTODO: error management
                }

                return self.makeRequest(reqConfig)
                    .then(res => {
                        if (!skipCopiedCheck)
                            return ResearchEntityModel.checkCopiedDocuments(ResearchEntityModel, researchEntityId, res.items, ResearchEntity.getAllVerifiedDocuments)
                                .then(documents => ({items: documents, count: res.count}));

                        return res;
                    });
            });
    },
    makeRequest: function (reqConfig) {
        return request.get(reqConfig.reqParams)
            .then(res => {
                const extracted = reqConfig.fieldExtract(res);
                return Promise.all(_.map(extracted.documents, r => reqConfig.transform(r)))
                    .then(documents => ({
                        items: documents,
                        count: extracted.count
                    }));
            });
    },
    getPublicationsConfig: function (researchEntity, configQuery) {
        var researchEntityType = researchEntity.getType();
        var query;
        if (researchEntityType === 'user') {
            var opts = {
                'username': {'author-email': researchEntity.username},
                'surname': {author: researchEntity.surname}
            };

            if (configQuery.where.field in opts)
                query = opts[configQuery.where.field];
            else
                throw "ExternalDocument error: field not selected";
        }
        else {
            query = {"research-structure": researchEntity.publicationsAcronym};
        }

        var qs = {
            limit: configQuery.limit,
            skip: configQuery.skip
        };
        qs = _.merge(qs, query);

        return {
            reqParams: {
                uri: 'http://backend.publications.iit.it/api/publications/getMatchingOnesAsJsonData',
                qs: qs,
                json: true
            },
            fieldExtract: res =>({
                documents: _.get(res, 'data'),
                count: _.get(res, 'items_count')
            }),
            transform: d => {
                var newDoc = {
                    title: d.title,
                    authorsStr: d.authors.replace(/\*/g, ''),
                    year: d.year,
                    doi: d.doi,
                    volume: d.volume,
                    issue: d.issue,
                    pages: /^\d+-\d+$/.test(d.pages) ? d.pages : '',
                    articleNumber: ''
                };
                if (d.conference)
                    newDoc.sourceType = 'conference';
                else if (d.journal)
                    newDoc.sourceType = 'journal';
                else if (d.bookTitle)
                    newDoc.sourceType = 'book';
                else
                    newDoc.sourceType = null;
                var typeMappings = {
                    bookwhole: 'book',
                    bookchapter: 'bookChapter',
                    fullpapervolumeatreferredconference: 'conference_paper',
                    shortpaperabstractatrefereedconference: 'abstract',
                    nationaljournal: 'article',
                    internationaljournal: 'article',
                    correction: 'erraturm',
                    editorial: 'editorial',
                    supplementaryinformation: 'note'
                };
                newDoc.type = d.typeAlias in typeMappings ? typeMappings[d.typeAlias] : null;

                var newSource = {
                    title: d.journal || d.conference || d.bookTitle,
                    publisher: d.publisher,
                    acronym: d.conferenceAcronym,
                    location: d.conferencePlace
                };
                return mergeDocAndSource(newDoc, newSource);
            }
        };
    },
    getOrcidConfig: function (researchEntity, configQuery) {
        return {
            reqParams: {
                uri: 'http://pub.orcid.org/' + researchEntity.orcidId + '/orcid-works',
                headers: {
                    'Accept': 'application/json'
                },
                /*Pagination does not work in this way*/
                qs: {
                    limit: configQuery.limit,
                    skip: configQuery.skip
                },
                json: true

            },
            fieldExtract: res => {
                /* Fake pagination: To be fixed */
                const allDocuments = _.get(res, 'orcid-profile.orcid-activities.orcid-works.orcid-work');
                const sortedDocuments = _.orderBy(allDocuments, ['publication-date.year.value'], ['desc']);
                const documents = _.slice(sortedDocuments, configQuery.skip, configQuery.skip + configQuery.limit);
                const count = allDocuments.length;
                return {documents, count};
            },
            transform: d => {
                function getAttributeFromCitation(d, attribute) {
                    const citationData = _.get(d, 'work-citation.citation', '');
                    const regex = new RegExp(attribute + '\\s=\\s{(.*?)}');
                    return _.get(citationData.match(regex), '[1]');
                }

                function getExternalId(d, attributeName) {
                    return _.get(
                        _.find(_.get(d, 'work-external-identifiers.work-external-identifier'),
                            function (wei) {
                                return wei['work-external-identifier-type'] === attributeName;
                            }),
                        'work-external-identifier-id.value'
                    );
                }

                const sourceTypeMappings = {
                    JOURNAL_ARTICLE: 'journal',
                    CONFERENCE_PAPER: 'conference',
                    BOOK: 'book'
                };
                const sourceType = sourceTypeMappings[d['work-type']];
                const newDoc = {
                    title: _.get(d, 'work-title.title.value'),
                    authorsStr: _.map(_.get(d, 'work-contributors.contributor'), function (c) {
                        var authorStr = _.get(c, 'credit-name.value');
                        return authorStr.replace(/,/g, '');
                    }).join(', '),
                    year: _.get(d, 'publication-date.year.value'),
                    volume: getAttributeFromCitation(d, 'volume'),
                    issue: getAttributeFromCitation(d, 'number'),
                    pages: getAttributeFromCitation(d, 'pages'),
                    doi: getExternalId(d, 'DOI'),
                    scopusId: _.trimStart(getExternalId(d, 'EID'), '2-s2.0-'),
                    wodId: _.trimStart(getExternalId(d, 'OTHER_ID'), 'WOS:'),
                    sourceType: sourceType
                };
                const newSource = {
                    type: sourceType,
                    title: getAttributeFromCitation(d, 'journal') || _.get(d, 'journal-title.value'),
                    issn: getExternalId(d, 'ISSN'),
                    isbn: getExternalId(d, 'ISBN'),
                };

                return mergeDocAndSource(newDoc, newSource);
            }
        };
    },
    getScopusConfig: function (researchEntity, configQuery) {
        var researchEntityType = researchEntity.getType();
        var uri = 'https://api.elsevier.com/content/search/scopus';
        var query = [];

        if (researchEntityType === 'user') {

            var opts = {
                'surname': 'AUTHNAME(' + researchEntity.surname + ')',
                'scopusId': 'au-id(' + researchEntity.scopusId + ')'
            };

            if (configQuery.where.field in opts)
                query.push(opts[configQuery.where.field]);
            else
                throw "ExternalDocument error: field not selected";

        }
        else {
            query.push('AF-ID(' + researchEntity.scopusId + ')');
            uri += 'affiliation';
        }

        var additionalOpts = {
            'year': 'PUBYEAR IS %val'
        };

        if (Array.isArray(configQuery.where.additionalFields))
            query = query.concat(
                configQuery.where.additionalFields
                    .map(f => additionalOpts[f.field].replace(/%val/, f.value))
            );

        return {
            reqParams: {
                uri: uri,
                headers: {
                    'X-ELS-APIKey': sails.config.scientilla.scopus.apiKey,
                    'X-ELS-Insttoken': sails.config.scientilla.scopus.token,
                },
                qs: {
                    start: configQuery.skip,
                    count: configQuery.limit,
                    query: query.join(' AND '),
                    sort: '-pubyear'
                },
                json: true
            },
            fieldExtract: res => {
                const error = _.get(res, 'search-results.entry[0].error');

                if (error)
                    throw new Error(error);

                const count = _.get(res, 'search-results.opensearch:totalResults');
                const documents = _.get(res, 'search-results.entry');
                return {documents, count};
            },
            transform: d1 => {
                function getConferenceLocation(d) {
                    var venuePath = 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.conflocation.venue';
                    var cityPath = 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.conflocation.city';
                    var venue = _.get(d, venuePath);
                    var city = _.get(d, cityPath);
                    var conferenceLocationArray = _.compact([venue, city]);
                    var conferenceLocation = conferenceLocationArray.join(', ');
                    return conferenceLocation;
                }

                function getConferenceAcronym(d) {
                    var confinfo = _.get(d, 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.confname');
                    var confAcronym = _.isNil(confinfo) ? "" : confinfo.split(', ')[1];
                    return confAcronym;
                }

                function getScopusId(d) {
                    var identifier = d1['dc:identifier'];
                    if (_.startsWith(identifier, 'SCOPUS_ID:')) {
                        return _.replace(identifier, 'SCOPUS_ID:', '');
                    }
                    var eid = d['eid'];
                    if (_.startsWith(eid, '2-s2.0-')) {
                        return _.trimStart(eid, '2-s2.0-');
                    }
                    return null;
                }

                var scopusId = getScopusId(d1);
                return singleRequest(0);

                function singleRequest(attempt) {
                    return request
                        .get({
                            uri: 'https://api.elsevier.com/content/abstract/scopus_id/' + scopusId,
                            headers: {
                                'X-ELS-APIKey': sails.config.scientilla.scopus.apiKey,
                                'X-ELS-Insttoken': sails.config.scientilla.scopus.token,
                                Accept: 'application/json'
                            },
                            json: true
                        })
                        .then(function (res) {
                            function getConditionalField(obj, path, type) {
                                const vals = toArray(_.get(obj, path));
                                return _.get(_.find(vals, {'@type': type}), '$');
                            }

                            const d2 = _.get(res, 'abstracts-retrieval-response', {});
                            const scopusSource = _.get(d2, 'item.bibrecord.head.source');

                            const sourceTypeMappings = {
                                'j': 'journal',
                                'p': 'conference',
                                'b': 'book',
                                'r': 'report',
                                'k': 'bookseries'
                            };

                            var typeMappings = {
                                ar: 'article',
                                ab: 'abstract_report',
                                ip: 'article_in_press',
                                bk: 'book',
                                ch: 'book_chapter',
                                cp: 'conference_paper',
                                cr: 'conference_review',
                                ed: 'editorial',
                                er: 'erratum',
                                le: 'letter',
                                no: 'note',
                                re: 'review',
                                sh: 'short_survey'
                            };

                            var sourceType = sourceTypeMappings[scopusSource['@type']];
                            var documentData = {
                                title: _.get(d1, 'dc:title'),
                                authorsStr: _.map(
                                    _.get(d2, 'authors.author'),
                                    function (c) {
                                        return _.get(c, 'ce:indexed-name');
                                    }).join(', '),
                                authorKeywords: _.map(
                                    _.get(d2, 'authkeywords.author-keyword'),
                                    function (c) {
                                        return _.get(c, '$');
                                    }).join(', '),
                                year: _.get(d2, 'item.bibrecord.head.source.publicationdate.year'),
                                doi: _.get(d2, 'coredata.prism:doi'),
                                sourceType: sourceType,
                                scopusId: scopusId,
                                abstract: _.trim(_.get(d2, 'coredata.dc:description')),
                                articleNumber: _.get(d2, 'item.bibrecord.head.source.article-number'),
                                journal: d1['prism:publicationName'],
                                volume: d1['prism:volume'],
                                issue: d1['prism:issueIdentifier'],
                                pages: d1['prism:pageRange'],
                                type: typeMappings[d1['subtype']]
                            };

                            const sourceData = {
                                type: sourceType,
                                scopusId: _.get(scopusSource, '@srcid'),
                                title: _.get(scopusSource, 'sourcetitle.$') || _.get(scopusSource, 'sourcetitle'),
                                issn: getConditionalField(scopusSource, 'issn', 'print'),
                                eissn: getConditionalField(scopusSource, 'issn', 'electronic'),
                                isbn: getConditionalField(scopusSource, 'isbn', 'print'),
                                publisher: _.get(scopusSource, 'publisher.publishername'),
                                year: _.get(scopusSource, 'publicationyear.@first'),
                                website: getConditionalField(scopusSource, 'website.ce:e-address', 'email'),
                                location: getConferenceLocation(d2),
                                acronym: getConferenceAcronym(d2)
                            };

                            const allAffiliations = toArray(d2.affiliation);

                            const scopusInstitutes = _.map(allAffiliations, a => ({
                                name: a.affilname,
                                city: a['affiliation-city'],
                                country: a['affiliation-country'],
                                scopusId: a['@id']
                            }));

                            const institutesCreationFns = _.map(
                                scopusInstitutes,
                                i => Institute.findOrCreate({scopusId: i.scopusId}, i)
                            );

                            return Promise.all([
                                Source.findOrCreate({scopusId: sourceData.scopusId}, sourceData),
                                Promise.all(institutesCreationFns)
                            ])
                                .spread((newSource, newInstitutes) => [d2, documentData, newInstitutes, newSource]);
                        })
                        .spread(function (d2, newDoc, newInstitutes, newSource) {
                            const scopusAuthorships = _.get(d2, 'authors.author');

                            const correspondingAuthorIndexedName = _.get(d2, 'item.bibrecord.head.correspondence.person.ce:indexed-name');
                            const correspondingIndex = _.findIndex(newDoc.authorsStr.split(', '), correspondingAuthorIndexedName);
                            newDoc.authorships = _.map(scopusAuthorships, (a, i) => {
                                const affiliationArray = toArray(a.affiliation);
                                const affiliationInstitutes = _.map(
                                    affiliationArray,
                                    aff => _.find(newInstitutes, {scopusId: aff['@id']}).id
                                );

                                return {
                                    position: i,
                                    corresponding: correspondingIndex === i,
                                    affiliations: affiliationInstitutes
                                };
                            });
                            newDoc.source = newSource;
                            return newDoc;
                        })
                        .catch(function (err) {
                            if (attempt < 3) {
                                sails.log.debug('Error: Scopus Id: ' + scopusId + ', trying again, attempt n. ' + (attempt + 1));
                                return singleRequest(attempt + 1);
                            }

                            sails.log.debug('Scopus request failed. Scopus Id = ' + scopusId);
                            sails.log.debug(err.error);

                            return {};
                        });
                }
            }
        };
    }
};

function toArray(val) {
    if (_.isNil(val))
        return [];
    if (!_.isArray(val))
        return [val];
    return val;
}

function mergeDocAndSource(newDoc, newSource) {
    if (!newSource.title)
        return newDoc;
    return Source.findOneByTitle(newSource.title)
        .then(source => {
            newDoc.source = source ? source : newSource;
            return newDoc;
        });
}<commit_msg>Fixed error.<commit_after>/* global sails */
"use strict";

var request = require('request-promise');
var _ = require('lodash');
var Promise = require("bluebird");
const ResearchEntity = require('../lib/ResearchEntity');

module.exports = {
    attributes: {},
    getDocuments: function (ResearchEntityModel, researchEntityId, query, skipCopiedCheck) {
        var self = this;
        var connector = query.where.connector;
        if (!connector)
            throw new Error('A Connector parameter is necessary');
        return ResearchEntityModel.findOneById(researchEntityId)
            .then(function (researchEntity) {
                var reqConfig;
                switch (connector) {
                    case'Publications':
                        reqConfig = self.getPublicationsConfig(researchEntity, query);
                        break;
                    case'ORCID':
                        reqConfig = self.getOrcidConfig(researchEntity, query);
                        break;
                    case'Scopus':
                        reqConfig = self.getScopusConfig(researchEntity, query);
                        break;
                    default:
                    //sTODO: error management
                }

                return self.makeRequest(reqConfig)
                    .then(res => {
                        if (!skipCopiedCheck)
                            return ResearchEntityModel.checkCopiedDocuments(ResearchEntityModel, researchEntityId, res.items, ResearchEntity.getAllDocuments)
                                .then(documents => ({items: documents, count: res.count}));

                        return res;
                    });
            });
    },
    makeRequest: function (reqConfig) {
        return request.get(reqConfig.reqParams)
            .then(res => {
                const extracted = reqConfig.fieldExtract(res);
                return Promise.all(_.map(extracted.documents, r => reqConfig.transform(r)))
                    .then(documents => ({
                        items: documents,
                        count: extracted.count
                    }));
            });
    },
    getPublicationsConfig: function (researchEntity, configQuery) {
        var researchEntityType = researchEntity.getType();
        var query;
        if (researchEntityType === 'user') {
            var opts = {
                'username': {'author-email': researchEntity.username},
                'surname': {author: researchEntity.surname}
            };

            if (configQuery.where.field in opts)
                query = opts[configQuery.where.field];
            else
                throw "ExternalDocument error: field not selected";
        }
        else {
            query = {"research-structure": researchEntity.publicationsAcronym};
        }

        var qs = {
            limit: configQuery.limit,
            skip: configQuery.skip
        };
        qs = _.merge(qs, query);

        return {
            reqParams: {
                uri: 'http://backend.publications.iit.it/api/publications/getMatchingOnesAsJsonData',
                qs: qs,
                json: true
            },
            fieldExtract: res =>({
                documents: _.get(res, 'data'),
                count: _.get(res, 'items_count')
            }),
            transform: d => {
                var newDoc = {
                    title: d.title,
                    authorsStr: d.authors.replace(/\*/g, ''),
                    year: d.year,
                    doi: d.doi,
                    volume: d.volume,
                    issue: d.issue,
                    pages: /^\d+-\d+$/.test(d.pages) ? d.pages : '',
                    articleNumber: ''
                };
                if (d.conference)
                    newDoc.sourceType = 'conference';
                else if (d.journal)
                    newDoc.sourceType = 'journal';
                else if (d.bookTitle)
                    newDoc.sourceType = 'book';
                else
                    newDoc.sourceType = null;
                var typeMappings = {
                    bookwhole: 'book',
                    bookchapter: 'bookChapter',
                    fullpapervolumeatreferredconference: 'conference_paper',
                    shortpaperabstractatrefereedconference: 'abstract',
                    nationaljournal: 'article',
                    internationaljournal: 'article',
                    correction: 'erraturm',
                    editorial: 'editorial',
                    supplementaryinformation: 'note'
                };
                newDoc.type = d.typeAlias in typeMappings ? typeMappings[d.typeAlias] : null;

                var newSource = {
                    title: d.journal || d.conference || d.bookTitle,
                    publisher: d.publisher,
                    acronym: d.conferenceAcronym,
                    location: d.conferencePlace
                };
                return mergeDocAndSource(newDoc, newSource);
            }
        };
    },
    getOrcidConfig: function (researchEntity, configQuery) {
        return {
            reqParams: {
                uri: 'http://pub.orcid.org/' + researchEntity.orcidId + '/orcid-works',
                headers: {
                    'Accept': 'application/json'
                },
                /*Pagination does not work in this way*/
                qs: {
                    limit: configQuery.limit,
                    skip: configQuery.skip
                },
                json: true

            },
            fieldExtract: res => {
                /* Fake pagination: To be fixed */
                const allDocuments = _.get(res, 'orcid-profile.orcid-activities.orcid-works.orcid-work');
                const sortedDocuments = _.orderBy(allDocuments, ['publication-date.year.value'], ['desc']);
                const documents = _.slice(sortedDocuments, configQuery.skip, configQuery.skip + configQuery.limit);
                const count = allDocuments.length;
                return {documents, count};
            },
            transform: d => {
                function getAttributeFromCitation(d, attribute) {
                    const citationData = _.get(d, 'work-citation.citation', '');
                    const regex = new RegExp(attribute + '\\s=\\s{(.*?)}');
                    return _.get(citationData.match(regex), '[1]');
                }

                function getExternalId(d, attributeName) {
                    return _.get(
                        _.find(_.get(d, 'work-external-identifiers.work-external-identifier'),
                            function (wei) {
                                return wei['work-external-identifier-type'] === attributeName;
                            }),
                        'work-external-identifier-id.value'
                    );
                }

                const sourceTypeMappings = {
                    JOURNAL_ARTICLE: 'journal',
                    CONFERENCE_PAPER: 'conference',
                    BOOK: 'book'
                };
                const sourceType = sourceTypeMappings[d['work-type']];
                const newDoc = {
                    title: _.get(d, 'work-title.title.value'),
                    authorsStr: _.map(_.get(d, 'work-contributors.contributor'), function (c) {
                        var authorStr = _.get(c, 'credit-name.value');
                        return authorStr.replace(/,/g, '');
                    }).join(', '),
                    year: _.get(d, 'publication-date.year.value'),
                    volume: getAttributeFromCitation(d, 'volume'),
                    issue: getAttributeFromCitation(d, 'number'),
                    pages: getAttributeFromCitation(d, 'pages'),
                    doi: getExternalId(d, 'DOI'),
                    scopusId: _.trimStart(getExternalId(d, 'EID'), '2-s2.0-'),
                    wodId: _.trimStart(getExternalId(d, 'OTHER_ID'), 'WOS:'),
                    sourceType: sourceType
                };
                const newSource = {
                    type: sourceType,
                    title: getAttributeFromCitation(d, 'journal') || _.get(d, 'journal-title.value'),
                    issn: getExternalId(d, 'ISSN'),
                    isbn: getExternalId(d, 'ISBN'),
                };

                return mergeDocAndSource(newDoc, newSource);
            }
        };
    },
    getScopusConfig: function (researchEntity, configQuery) {
        var researchEntityType = researchEntity.getType();
        var uri = 'https://api.elsevier.com/content/search/scopus';
        var query = [];

        if (researchEntityType === 'user') {

            var opts = {
                'surname': 'AUTHNAME(' + researchEntity.surname + ')',
                'scopusId': 'au-id(' + researchEntity.scopusId + ')'
            };

            if (configQuery.where.field in opts)
                query.push(opts[configQuery.where.field]);
            else
                throw "ExternalDocument error: field not selected";

        }
        else {
            query.push('AF-ID(' + researchEntity.scopusId + ')');
            uri += 'affiliation';
        }

        var additionalOpts = {
            'year': 'PUBYEAR IS %val'
        };

        if (Array.isArray(configQuery.where.additionalFields))
            query = query.concat(
                configQuery.where.additionalFields
                    .map(f => additionalOpts[f.field].replace(/%val/, f.value))
            );

        return {
            reqParams: {
                uri: uri,
                headers: {
                    'X-ELS-APIKey': sails.config.scientilla.scopus.apiKey,
                    'X-ELS-Insttoken': sails.config.scientilla.scopus.token,
                },
                qs: {
                    start: configQuery.skip,
                    count: configQuery.limit,
                    query: query.join(' AND '),
                    sort: '-pubyear'
                },
                json: true
            },
            fieldExtract: res => {
                const error = _.get(res, 'search-results.entry[0].error');

                if (error)
                    throw new Error(error);

                const count = _.get(res, 'search-results.opensearch:totalResults');
                const documents = _.get(res, 'search-results.entry');
                return {documents, count};
            },
            transform: d1 => {
                function getConferenceLocation(d) {
                    var venuePath = 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.conflocation.venue';
                    var cityPath = 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.conflocation.city';
                    var venue = _.get(d, venuePath);
                    var city = _.get(d, cityPath);
                    var conferenceLocationArray = _.compact([venue, city]);
                    var conferenceLocation = conferenceLocationArray.join(', ');
                    return conferenceLocation;
                }

                function getConferenceAcronym(d) {
                    var confinfo = _.get(d, 'item.bibrecord.head.source.additional-srcinfo.conferenceinfo.confevent.confname');
                    var confAcronym = _.isNil(confinfo) ? "" : confinfo.split(', ')[1];
                    return confAcronym;
                }

                function getScopusId(d) {
                    var identifier = d1['dc:identifier'];
                    if (_.startsWith(identifier, 'SCOPUS_ID:')) {
                        return _.replace(identifier, 'SCOPUS_ID:', '');
                    }
                    var eid = d['eid'];
                    if (_.startsWith(eid, '2-s2.0-')) {
                        return _.trimStart(eid, '2-s2.0-');
                    }
                    return null;
                }

                var scopusId = getScopusId(d1);
                return singleRequest(0);

                function singleRequest(attempt) {
                    return request
                        .get({
                            uri: 'https://api.elsevier.com/content/abstract/scopus_id/' + scopusId,
                            headers: {
                                'X-ELS-APIKey': sails.config.scientilla.scopus.apiKey,
                                'X-ELS-Insttoken': sails.config.scientilla.scopus.token,
                                Accept: 'application/json'
                            },
                            json: true
                        })
                        .then(function (res) {
                            function getConditionalField(obj, path, type) {
                                const vals = toArray(_.get(obj, path));
                                return _.get(_.find(vals, {'@type': type}), '$');
                            }

                            const d2 = _.get(res, 'abstracts-retrieval-response', {});
                            const scopusSource = _.get(d2, 'item.bibrecord.head.source');

                            const sourceTypeMappings = {
                                'j': 'journal',
                                'p': 'conference',
                                'b': 'book',
                                'r': 'report',
                                'k': 'bookseries'
                            };

                            var typeMappings = {
                                ar: 'article',
                                ab: 'abstract_report',
                                ip: 'article_in_press',
                                bk: 'book',
                                ch: 'book_chapter',
                                cp: 'conference_paper',
                                cr: 'conference_review',
                                ed: 'editorial',
                                er: 'erratum',
                                le: 'letter',
                                no: 'note',
                                re: 'review',
                                sh: 'short_survey'
                            };

                            var sourceType = sourceTypeMappings[scopusSource['@type']];
                            var documentData = {
                                title: _.get(d1, 'dc:title'),
                                authorsStr: _.map(
                                    _.get(d2, 'authors.author'),
                                    function (c) {
                                        return _.get(c, 'ce:indexed-name');
                                    }).join(', '),
                                authorKeywords: _.map(
                                    _.get(d2, 'authkeywords.author-keyword'),
                                    function (c) {
                                        return _.get(c, '$');
                                    }).join(', '),
                                year: _.get(d2, 'item.bibrecord.head.source.publicationdate.year'),
                                doi: _.get(d2, 'coredata.prism:doi'),
                                sourceType: sourceType,
                                scopusId: scopusId,
                                abstract: _.trim(_.get(d2, 'coredata.dc:description')),
                                articleNumber: _.get(d2, 'item.bibrecord.head.source.article-number'),
                                journal: d1['prism:publicationName'],
                                volume: d1['prism:volume'],
                                issue: d1['prism:issueIdentifier'],
                                pages: d1['prism:pageRange'],
                                type: typeMappings[d1['subtype']]
                            };

                            const sourceData = {
                                type: sourceType,
                                scopusId: _.get(scopusSource, '@srcid'),
                                title: _.get(scopusSource, 'sourcetitle.$') || _.get(scopusSource, 'sourcetitle'),
                                issn: getConditionalField(scopusSource, 'issn', 'print'),
                                eissn: getConditionalField(scopusSource, 'issn', 'electronic'),
                                isbn: getConditionalField(scopusSource, 'isbn', 'print'),
                                publisher: _.get(scopusSource, 'publisher.publishername'),
                                year: _.get(scopusSource, 'publicationyear.@first'),
                                website: getConditionalField(scopusSource, 'website.ce:e-address', 'email'),
                                location: getConferenceLocation(d2),
                                acronym: getConferenceAcronym(d2)
                            };

                            const allAffiliations = toArray(d2.affiliation);

                            const scopusInstitutes = _.map(allAffiliations, a => ({
                                name: a.affilname,
                                city: a['affiliation-city'],
                                country: a['affiliation-country'],
                                scopusId: a['@id']
                            }));

                            const institutesCreationFns = _.map(
                                scopusInstitutes,
                                i => Institute.findOrCreate({scopusId: i.scopusId}, i)
                            );

                            return Promise.all([
                                Source.findOrCreate({scopusId: sourceData.scopusId}, sourceData),
                                Promise.all(institutesCreationFns)
                            ])
                                .spread((newSource, newInstitutes) => [d2, documentData, newInstitutes, newSource]);
                        })
                        .spread(function (d2, newDoc, newInstitutes, newSource) {
                            const scopusAuthorships = _.get(d2, 'authors.author');

                            const correspondingAuthorIndexedName = _.get(d2, 'item.bibrecord.head.correspondence.person.ce:indexed-name');
                            const correspondingIndex = _.findIndex(newDoc.authorsStr.split(', '), correspondingAuthorIndexedName);
                            newDoc.authorships = _.map(scopusAuthorships, (a, i) => {
                                const affiliationArray = toArray(a.affiliation);
                                const affiliationInstitutes = _.map(
                                    affiliationArray,
                                    aff => _.find(newInstitutes, {scopusId: aff['@id']}).id
                                );

                                return {
                                    position: i,
                                    corresponding: correspondingIndex === i,
                                    affiliations: affiliationInstitutes
                                };
                            });
                            newDoc.source = newSource;
                            return newDoc;
                        })
                        .catch(function (err) {
                            if (attempt < 3) {
                                sails.log.debug('Error: Scopus Id: ' + scopusId + ', trying again, attempt n. ' + (attempt + 1));
                                return singleRequest(attempt + 1);
                            }

                            sails.log.debug('Scopus request failed. Scopus Id = ' + scopusId);
                            sails.log.debug(err.error);

                            return {};
                        });
                }
            }
        };
    }
};

function toArray(val) {
    if (_.isNil(val))
        return [];
    if (!_.isArray(val))
        return [val];
    return val;
}

function mergeDocAndSource(newDoc, newSource) {
    if (!newSource.title)
        return newDoc;
    return Source.findOneByTitle(newSource.title)
        .then(source => {
            newDoc.source = source ? source : newSource;
            return newDoc;
        });
}<|endoftext|>


<commit_before>/**
 * Keyboard Telegram API Services
 *
 * @description :: Telegram API Services for Sails
 * @help        :: See https://github.com/
 */

module.exports.createKeyboard = function () {

    //TODO: HARDCODED
    var buttons = [["A", "B"],["C", "D"]];
    var keyboard = {keyboard: buttons, resize_keyboard: false, one_time_keyboard: true, selective: false };
    return keyboard;

};
<commit_msg>Keyboard support<commit_after>/**
 * Keyboard Telegram API Services
 *
 * @description :: Telegram API Services for Sails
 * @help        :: See https://github.com/
 */

module.exports.createKeyboard = function () {

    //TODO: HARDCODED
    var buttons = [["A", "B"],["C", "D"], ["E", "F"], ["G","H"]];
    var keyboard = {keyboard: buttons, resize_keyboard: false, one_time_keyboard: true, selective: false };
    return keyboard;

};
<|endoftext|>


<commit_before>/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 05/03/12
 * Time: 18:12
 * To change this template use File | Settings | File Templates.
 */

/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 23/02/12
 * Time: 12:03
 * To change this template use File | Settings | File Templates.
 */

var resources = require('jest'),
    util = require('util'),
    models = require('../models'),
    common = require('./common'),
    async = require('async'),
    notifications = require('./notifications');

var SuggestionResource = module.exports = common.GamificationMongooseResource.extend({
    init:function () {
        this._super(models.Suggestion, 'suggestion', /*common.getGamificationTokenPrice('suggestion')*/2);
        this.allowed_methods = ['get', 'post', 'put'];
//        this.authorization = new Authoriztion();
        this.authentication = new common.SessionAuthentication();
        this.filtering = {discussion_id:null, is_approved:null};
        this.default_query = function (query) {
            return query.sort('creation_date', 'descending').populate('creator_id');
        };

        this.fields = {
            creator_id : {
                id:null,
                first_name:null,
                last_name:null,
                avatar_url:null,
                facebook_id:null,
                score: null
            },
            parts:null,
            popularity:null,
            tokens:null,
            creation_date:null,
            agrees:null,
            not_agrees: null,
            evaluate_counter:null,
            grade:null,
            id:null,
            explanation: null,
            updated_user_tokens:null,
            grade_obj: {
                _id: null,
                evalueation_grade: null,
                does_support_the_suggestion: null
            },
            wanted_amount_of_tokens: null,
            curr_amount_of_tokens: null

        };
    },

    get_objects: function (req, filters, sorts, limit, offset, callback) {

        var self = this;
        var discussion_id = req.query.discussion_id;
        var discussion_threshold;

        var iterator = function(suggestion, itr_cbk){
            var curr_grade_obj = {};
            suggestion.grade_obj = curr_grade_obj;
            suggestion.is_agree = false;

            suggestion.curr_amount_of_tokens = suggestion.agrees - suggestion.not_agrees;

            //wanted amount of tokens is either what admin has entered to the specific suggestion, or the discussion threshold...
            if(suggestion.admin_threshold_for_accepting_the_suggestion > 0)
                suggestion.wanted_amount_of_tokens = suggestion.admin_threshold_for_accepting_the_suggestion;
            else
                suggestion.wanted_amount_of_tokens = calculate_sugg_threshold(suggestion.getCharCount(), discussion_threshold);
            if(req.user){
                models.GradeSuggestion.findOne({user_id: req.user._id, suggestion_id: suggestion._id}, ["_id", "evaluation_grade"], function(err, grade_sugg_obj){
                    if(!err && grade_sugg_obj){
                        curr_grade_obj = {
                            _id: grade_sugg_obj._id,
                            evalueation_grade: grade_sugg_obj.evaluation_grade,
                            does_support_the_suggestion: grade_sugg_obj.does_support_the_suggestion
                        }
                        suggestion.grade_obj = curr_grade_obj;
                        itr_cbk(err, suggestion);
                    }else{
                        //check if user is the creator - if so return in grade object the
                        if(!err){
                            models.Discussion.findById(discussion_id, function(err, discussion){
                                if(!err)
                                    if(req.user._id + "" == discussion.creator_id + ""){
                                        suggestion.grade_obj["evalueation_grade"] = discussion.grade;
                                    }
                                itr_cbk(err, suggestion);
                            })
                        }else{
                            itr_cbk(err, suggestion);
                        }
                    }
                });
            }
            else{
                itr_cbk(null, suggestion);
            }
        }

        self._super(req, filters, sorts, limit, offset, function(err, results){

            if(err)
                callback(err, null);
            else
                //arrange objects only if the request is from discussion page
                if(!discussion_id)
                    callback(err, results);
                else
                    //for each object add grade_obj that reflects the user's grade for the suggestion,
                    //if the user is the disvcussion creator - grade_obj contains the discussion evaluate grade
                    //if the user is ofline grade_obj is {}

                    async.waterfall([
                        function(cbk){
                            models.Discussion.findById(discussion_id, cbk);
                        },

                        function(discussion_obj, cbk){
                            discussion_threshold = discussion_obj.threshold_for_accepting_change_suggestions;
                            if(discussion_obj.admin_threshold_for_accepting_change_suggestions > 0)
                                discussion_threshold = discussion_obj.admin_threshold_for_accepting_change_suggestions;

                            async.forEach(results.objects, iterator, function(err, objs){
                                cbk(err, results);
                            });
                        }
                    ], function(err, results){
                        callback(err, results);
                    })
        });
    },

    create_obj:function (req, fields, callback) {
        var user_id = req.session.user_id;
        var self = this;
        var suggestion_object = new self.model();
        var isNewFollower = false;
        var user = req.user;
        var discussion_id;
        var discussion_creator_id;
        var num_of_words;

        var iterator = function(user_schema, itr_cbk){
            if (user_schema.user_id == user_id)
                itr_cbk(null, 0);
            else{
                if (discussion_creator_id == user_schema.user_id){
                    notifications.create_user_notification("change_suggestion_on_discussion_you_created", discussion_id, user_schema.user_id, user_id, null, function(err, results){
                        itr_cbk(err, results);
                    });
                }else{
                    notifications.create_user_notification("change_suggestion_on_discussion_you_are_part_of", discussion_id, user_schema.user_id, user_id, null, function(err, results){
                        itr_cbk(err, results);
                    });
                }
            }
        }

        fields.creator_id = user_id;
        fields.first_name = user.first_name;
        fields.last_name = user.last_name;

        for (var field in fields) {
            suggestion_object.set(field, fields[field]);
        }

        //calculate threshold for the suggestion.. for this i need to get discussion threshold first
        async.waterfall([
            function (cbk) {
                var discussion_thresh;
                models.Discussion.findById(fields.discussion_id, cbk);
            },

            function (discussion_obj, cbk) {

                var word_count = suggestion_object.getWordCount();
                suggestion_object.threshold_for_accepting_the_suggestion = calculate_sugg_threshold(word_count,
                    discussion_obj.admin_threshold_for_accepting_change_suggestions || discussion_obj.threshold_for_accepting_change_suggestions);
                self.authorization.edit_object(req, suggestion_object, cbk);
            },

            function (suggestion_obj, cbk) {
                suggestion_object.save(function(err,data){
                    discussion_id = data.discussion_id;
                    suggestion_obj.wanted_amount_of_tokens = suggestion_obj.threshold_for_accepting_the_suggestion;
                    cbk(err,data);
                });
            },

            function (suggestion_obj, cbk) {
                async.parallel([
                    //add user to praticipants
                    function (cbk2) {
                        models.Discussion.update({_id:suggestion_object.discussion_id, "users.user_id": {$ne: user_id}},
                            {$addToSet: {users: {user_id: user_id, join_date: Date.now}}},
                        cbk2);
                    },

                    //add notification for the dicussion's participants or creator
                    function(cbk2){
                        models.Discussion.findById(suggestion_object.discussion_id, /*["users", "creator_id"],*/ function(err, disc_obj){
                            if (err)
                                cbk2(err, null);
                            else{
                                discussion_creator_id = disc_obj.creator_id;
                                async.forEach(disc_obj.users, iterator, cbk2);
                            }
                        })
                    }
                ], function(err,results)
                {
                    cbk(err, suggestion_obj);
                });
            }
        ], function (err, result) {
            callback(err, result);
        });
    },

    update_obj:function (req, suggestion_object, callback) {
        //if suggestion approved we change the discussion vision
        // + save the ealier version of vison as parts in vison_changes
        var discussion_id = suggestion_object.discussion_id;
        var g_discussion_obj;
        var vision_changes;
        if (suggestion_object.is_approved) {
            callback({message:"this suggestion is already published", code: 401}, null);
        } else {

            async.waterfall([

                function(cbk){
                    var vision_changes_array = [];
                    models.Discussion.findOne({_id:discussion_id}, cbk);
                },

                function(discussion_object, cbk){
                    var vision = discussion_object.vision_text;
                    var new_string = "";
                    var curr_position = 0;
                    var parts = suggestion_object.parts;

                    //changing the vision and save changes that have been so i can reverse it in change_vision
                    for (var i = 0; i < parts.length; i++) {
                        //                changed_text = vision.slice(parts[i].start, parseInt(parts[i].end) + 1);
                        new_string += vision.slice(curr_position, parts[i].start);
                        new_string += parts[i].text;
                        curr_position = parseInt(parts[i].end) + 1;
                        //                vision_changes_array.push({start: parts[i].start, end: parts[i].end, text : changed_text});

                        //                discussion_object.vision_changes.push({start: parts[i].start, end: parts[i].end, text : changed_text});
                    }
                    new_string += vision.slice(curr_position);
                    //            discussion_object.vision_changes.push(vision_changes_array);

                    discussion_object.vision_text_history.push(discussion_object.vision_text);
                    discussion_object.vision_text = new_string;
                    models.Discussion.update({_id:discussion_id}, {$addToSet: {vision_text_history: discussion_object.vision_text}, $set:{vision_text: new_string}}, function(err, counter){
                        cbk(err, discussion_object);
                    });

//                    discussion_object.save(cbk);
                },

                function(disc_obj, cbk){
                    g_discussion_obj = disc_obj;
                    suggestion_object.is_approved = true;
                    suggestion_object.save(cbk);
                }
            ], function(err, suggestion){
                callback(err, g_discussion_obj);
            })
        }
    }
});

module.exports.approveSuggestion = function(id,callback)
{
    //if suggestion approved we change the discussion vision
    // + save the ealier version of vison as parts in vison_changes

    //+ suggestion grade becomes the discussion grade
    var suggestion_object;
    var suggestion_creator;
    var discussion_id;
    var suggestion_grade;

    //set notifications
    //update discussion grade
    var iterator = function(sugg_grade, itr_cbk){

        async.parallel([
            function(cbk1){
                if(suggestion_creator != sugg_grade.user_id){
                    notifications.create_user_notification("approved_change_suggestion_you_graded",
                        discussion_id, suggestion_creator, null, null, cbk1);
                }else{
                    cbk1(null, 0);
                }
            },
            //update discussion grade with the suggestion grade
            function(cbk1){
                models.Grade.update({user_id: sugg_grade.user_id, discussion_id: discussion_id},
                    {$set: {evaluation_grade: sugg_grade.evaluation_grade}},
                    function(err, num){
                        cbk1(err, num);
                    });
            }
        ], function(err, args){
            itr_cbk(err, args);
        })
    }

    async.waterfall([
        function(cbk)
        {
            models.Suggestion.findById(id, cbk);
        },

        function(_suggestion_object,cbk){
            suggestion_object = _suggestion_object;
            suggestion_grade = suggestion_object.grade;
            if (suggestion_object.is_approved) {
                callback({message:"this suggestion is already published", code: 401}, null);
            } else {
                discussion_id = suggestion_object.discussion_id;
                var vision_changes_array = [];
                models.Discussion.findOne({_id:discussion_id}, cbk);
            }
        },

        function(discussion_object, cbk){
            var vision = discussion_object.vision_text;
            var new_string = "";
            var curr_position = 0;
            var parts = suggestion_object.parts;

            //changing the vision and save changes that have been so i can reverse it in change_vision
            for (var i = 0; i < parts.length; i++) {
                //                changed_text = vision.slice(parts[i].start, parseInt(parts[i].end) + 1);
                new_string += vision.slice(curr_position, parts[i].start);
                new_string += parts[i].text;
                curr_position = parseInt(parts[i].end) + 1;
                //                vision_changes_array.push({start: parts[i].start, end: parts[i].end, text : changed_text});

                //                discussion_object.vision_changes.push({start: parts[i].start, end: parts[i].end, text : changed_text});
            }
            new_string += vision.slice(curr_position);
            //            discussion_object.vision_changes.push(vision_changes_array);

            discussion_object.vision_text_history.push(discussion_object.vision_text);
            discussion_object.vision_text = new_string;

            //suggestion grade is the new discussion grade
            models.Discussion.update({_id:discussion_object._id},
            {
                $addToSet: {vision_text_history: discussion_object.vision_text},
                $set:{vision_text: new_string, grade: suggestion_grade}},
                function(err, counter){
                cbk(err, discussion_object);
            });
        },

        function(disc_obj, cbk){
            suggestion_object.is_approved = true;
            suggestion_object.save(cbk);
        },

        function(sug_obj, num, cbk){
            suggestion_creator = sug_obj.creator_id;
            async.parallel([
                //set gamification
                function(par_cbk){
                    models.User.update({_id: sug_obj.creator_id}, {$inc: {"gamification.approved_suggestion": 1}}, function(err, obj){
                        par_cbk(err, obj);
                    });
                },

                //set notifications for creator
                function(par_cbk){
                    notifications.create_user_notification("approved_change_suggestion_you_created",
                            discussion_id, sug_obj.creator_id, null, null, function(err, obj){
                            par_cbk(err, obj);
                        });
                },

                //set notifications for graders
                //for each suggestion grade - copy it to discussion grade
                function(par_cbk){

                    async.waterfall([
                        function(wtr_cbk){
                            models.GradeSuggestion.find({_id: sug_obj._id}, wtr_cbk);
                        },

                        function(sugg_grades, wtr_cbk){
                            async.forEach(sugg_grades, iterator, function(err, result){
                                wtr_cbk(err || null, result || 0);
                            });
                        }
                    ], function(err, args){
                        par_cbk(err, args);
                    });
                }
            ], function(err, args){
                cbk(err, 8);
            })
        }
    ], function(err, arg){
        callback(err, arg);
    });
}

var calculate_sugg_threshold = function(factor, discussion_threshold){
    var log_base_75_of_x =
        Math.log(discussion_threshold) / Math.log(75);
    return Math.pow(log_base_75_of_x, 1.6) * factor;
}
<commit_msg>numner cast<commit_after>/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 05/03/12
 * Time: 18:12
 * To change this template use File | Settings | File Templates.
 */

/**
 * Created by JetBrains WebStorm.
 * User: saar
 * Date: 23/02/12
 * Time: 12:03
 * To change this template use File | Settings | File Templates.
 */

var resources = require('jest'),
    util = require('util'),
    models = require('../models'),
    common = require('./common'),
    async = require('async'),
    notifications = require('./notifications');

var SuggestionResource = module.exports = common.GamificationMongooseResource.extend({
    init:function () {
        this._super(models.Suggestion, 'suggestion', /*common.getGamificationTokenPrice('suggestion')*/2);
        this.allowed_methods = ['get', 'post', 'put'];
//        this.authorization = new Authoriztion();
        this.authentication = new common.SessionAuthentication();
        this.filtering = {discussion_id:null, is_approved:null};
        this.default_query = function (query) {
            return query.sort('creation_date', 'descending').populate('creator_id');
        };

        this.fields = {
            creator_id : {
                id:null,
                first_name:null,
                last_name:null,
                avatar_url:null,
                facebook_id:null,
                score: null
            },
            parts:null,
            popularity:null,
            tokens:null,
            creation_date:null,
            agrees:null,
            not_agrees: null,
            evaluate_counter:null,
            grade:null,
            id:null,
            explanation: null,
            updated_user_tokens:null,
            grade_obj: {
                _id: null,
                evalueation_grade: null,
                does_support_the_suggestion: null
            },
            wanted_amount_of_tokens: null,
            curr_amount_of_tokens: null

        };
    },

    get_objects: function (req, filters, sorts, limit, offset, callback) {

        var self = this;
        var discussion_id = req.query.discussion_id;
        var discussion_threshold;

        var iterator = function(suggestion, itr_cbk){
            var curr_grade_obj = {};
            suggestion.grade_obj = curr_grade_obj;
            suggestion.is_agree = false;

            suggestion.curr_amount_of_tokens = suggestion.agrees - suggestion.not_agrees;

            //wanted amount of tokens is either what admin has entered to the specific suggestion, or the discussion threshold...
            if(suggestion.admin_threshold_for_accepting_the_suggestion > 0)
                suggestion.wanted_amount_of_tokens = suggestion.admin_threshold_for_accepting_the_suggestion;
            else
                suggestion.wanted_amount_of_tokens = calculate_sugg_threshold(suggestion.getCharCount(), discussion_threshold);
            if(req.user){
                models.GradeSuggestion.findOne({user_id: req.user._id, suggestion_id: suggestion._id}, ["_id", "evaluation_grade"], function(err, grade_sugg_obj){
                    if(!err && grade_sugg_obj){
                        curr_grade_obj = {
                            _id: grade_sugg_obj._id,
                            evalueation_grade: grade_sugg_obj.evaluation_grade,
                            does_support_the_suggestion: grade_sugg_obj.does_support_the_suggestion
                        }
                        suggestion.grade_obj = curr_grade_obj;
                        itr_cbk(err, suggestion);
                    }else{
                        //check if user is the creator - if so return in grade object the
                        if(!err){
                            models.Discussion.findById(discussion_id, function(err, discussion){
                                if(!err)
                                    if(req.user._id + "" == discussion.creator_id + ""){
                                        suggestion.grade_obj["evalueation_grade"] = discussion.grade;
                                    }
                                itr_cbk(err, suggestion);
                            })
                        }else{
                            itr_cbk(err, suggestion);
                        }
                    }
                });
            }
            else{
                itr_cbk(null, suggestion);
            }
        }

        self._super(req, filters, sorts, limit, offset, function(err, results){

            if(err)
                callback(err, null);
            else
                //arrange objects only if the request is from discussion page
                if(!discussion_id)
                    callback(err, results);
                else
                    //for each object add grade_obj that reflects the user's grade for the suggestion,
                    //if the user is the disvcussion creator - grade_obj contains the discussion evaluate grade
                    //if the user is ofline grade_obj is {}

                    async.waterfall([
                        function(cbk){
                            models.Discussion.findById(discussion_id, cbk);
                        },

                        function(discussion_obj, cbk){
                            discussion_threshold = discussion_obj.threshold_for_accepting_change_suggestions;
                            if(discussion_obj.admin_threshold_for_accepting_change_suggestions > 0)
                                discussion_threshold = discussion_obj.admin_threshold_for_accepting_change_suggestions;

                            async.forEach(results.objects, iterator, function(err, objs){
                                cbk(err, results);
                            });
                        }
                    ], function(err, results){
                        callback(err, results);
                    })
        });
    },

    create_obj:function (req, fields, callback) {
        var user_id = req.session.user_id;
        var self = this;
        var suggestion_object = new self.model();
        var isNewFollower = false;
        var user = req.user;
        var discussion_id;
        var discussion_creator_id;
        var num_of_words;

        var iterator = function(user_schema, itr_cbk){
            if (user_schema.user_id == user_id)
                itr_cbk(null, 0);
            else{
                if (discussion_creator_id == user_schema.user_id){
                    notifications.create_user_notification("change_suggestion_on_discussion_you_created", discussion_id, user_schema.user_id, user_id, null, function(err, results){
                        itr_cbk(err, results);
                    });
                }else{
                    notifications.create_user_notification("change_suggestion_on_discussion_you_are_part_of", discussion_id, user_schema.user_id, user_id, null, function(err, results){
                        itr_cbk(err, results);
                    });
                }
            }
        }

        fields.creator_id = user_id;
        fields.first_name = user.first_name;
        fields.last_name = user.last_name;

        for (var field in fields) {
            suggestion_object.set(field, fields[field]);
        }

        //calculate threshold for the suggestion.. for this i need to get discussion threshold first
        async.waterfall([
            function (cbk) {
                var discussion_thresh;
                models.Discussion.findById(fields.discussion_id, cbk);
            },

            function (discussion_obj, cbk) {

                var word_count = suggestion_object.getCharCount();
                suggestion_object.threshold_for_accepting_the_suggestion = calculate_sugg_threshold(word_count,
                    Number(discussion_obj.admin_threshold_for_accepting_change_suggestions) || Number(discussion_obj.threshold_for_accepting_change_suggestions));
                self.authorization.edit_object(req, suggestion_object, cbk);
            },

            function (suggestion_obj, cbk) {
                suggestion_object.save(function(err,data){
                    if(data){
                        discussion_id = data.discussion_id;
                        suggestion_obj.wanted_amount_of_tokens = suggestion_obj.threshold_for_accepting_the_suggestion;
                    }
                    cbk(err,data);
                });
            },

            function (suggestion_obj, cbk) {
                async.parallel([
                    //add user to praticipants
                    function (cbk2) {
                        models.Discussion.update({_id:suggestion_object.discussion_id, "users.user_id": {$ne: user_id}},
                            {$addToSet: {users: {user_id: user_id, join_date: Date.now}}},
                        cbk2);
                    },

                    //add notification for the dicussion's participants or creator
                    function(cbk2){
                        models.Discussion.findById(suggestion_object.discussion_id, /*["users", "creator_id"],*/ function(err, disc_obj){
                            if (err)
                                cbk2(err, null);
                            else{
                                discussion_creator_id = disc_obj.creator_id;
                                async.forEach(disc_obj.users, iterator, cbk2);
                            }
                        })
                    }
                ], function(err,results)
                {
                    cbk(err, suggestion_obj);
                });
            }
        ], function (err, result) {
            if(err){
                console.error(err);
                console.trace();
            }
            callback(err, result);
        });
    },

    update_obj:function (req, suggestion_object, callback) {
        //if suggestion approved we change the discussion vision
        // + save the ealier version of vison as parts in vison_changes
        var discussion_id = suggestion_object.discussion_id;
        var g_discussion_obj;
        var vision_changes;
        if (suggestion_object.is_approved) {
            callback({message:"this suggestion is already published", code: 401}, null);
        } else {

            async.waterfall([

                function(cbk){
                    var vision_changes_array = [];
                    models.Discussion.findOne({_id:discussion_id}, cbk);
                },

                function(discussion_object, cbk){
                    var vision = discussion_object.vision_text;
                    var new_string = "";
                    var curr_position = 0;
                    var parts = suggestion_object.parts;

                    //changing the vision and save changes that have been so i can reverse it in change_vision
                    for (var i = 0; i < parts.length; i++) {
                        //                changed_text = vision.slice(parts[i].start, parseInt(parts[i].end) + 1);
                        new_string += vision.slice(curr_position, parts[i].start);
                        new_string += parts[i].text;
                        curr_position = parseInt(parts[i].end) + 1;
                        //                vision_changes_array.push({start: parts[i].start, end: parts[i].end, text : changed_text});

                        //                discussion_object.vision_changes.push({start: parts[i].start, end: parts[i].end, text : changed_text});
                    }
                    new_string += vision.slice(curr_position);
                    //            discussion_object.vision_changes.push(vision_changes_array);

                    discussion_object.vision_text_history.push(discussion_object.vision_text);
                    discussion_object.vision_text = new_string;
                    models.Discussion.update({_id:discussion_id}, {$addToSet: {vision_text_history: discussion_object.vision_text}, $set:{vision_text: new_string}}, function(err, counter){
                        cbk(err, discussion_object);
                    });

//                    discussion_object.save(cbk);
                },

                function(disc_obj, cbk){
                    g_discussion_obj = disc_obj;
                    suggestion_object.is_approved = true;
                    suggestion_object.save(cbk);
                }
            ], function(err, suggestion){
                callback(err, g_discussion_obj);
            })
        }
    }
});

module.exports.approveSuggestion = function(id,callback)
{
    //if suggestion approved we change the discussion vision
    // + save the ealier version of vison as parts in vison_changes

    //+ suggestion grade becomes the discussion grade
    var suggestion_object;
    var suggestion_creator;
    var discussion_id;
    var suggestion_grade;

    //set notifications
    //update discussion grade
    var iterator = function(sugg_grade, itr_cbk){

        async.parallel([
            function(cbk1){
                if(suggestion_creator != sugg_grade.user_id){
                    notifications.create_user_notification("approved_change_suggestion_you_graded",
                        discussion_id, suggestion_creator, null, null, cbk1);
                }else{
                    cbk1(null, 0);
                }
            },
            //update discussion grade with the suggestion grade
            function(cbk1){
                models.Grade.update({user_id: sugg_grade.user_id, discussion_id: discussion_id},
                    {$set: {evaluation_grade: sugg_grade.evaluation_grade}},
                    function(err, num){
                        cbk1(err, num);
                    });
            }
        ], function(err, args){
            itr_cbk(err, args);
        })
    }

    async.waterfall([
        function(cbk)
        {
            models.Suggestion.findById(id, cbk);
        },

        function(_suggestion_object,cbk){
            suggestion_object = _suggestion_object;
            suggestion_grade = suggestion_object.grade;
            if (suggestion_object.is_approved) {
                callback({message:"this suggestion is already published", code: 401}, null);
            } else {
                discussion_id = suggestion_object.discussion_id;
                var vision_changes_array = [];
                models.Discussion.findOne({_id:discussion_id}, cbk);
            }
        },

        function(discussion_object, cbk){
            var vision = discussion_object.vision_text;
            var new_string = "";
            var curr_position = 0;
            var parts = suggestion_object.parts;

            //changing the vision and save changes that have been so i can reverse it in change_vision
            for (var i = 0; i < parts.length; i++) {
                //                changed_text = vision.slice(parts[i].start, parseInt(parts[i].end) + 1);
                new_string += vision.slice(curr_position, parts[i].start);
                new_string += parts[i].text;
                curr_position = parseInt(parts[i].end) + 1;
                //                vision_changes_array.push({start: parts[i].start, end: parts[i].end, text : changed_text});

                //                discussion_object.vision_changes.push({start: parts[i].start, end: parts[i].end, text : changed_text});
            }
            new_string += vision.slice(curr_position);
            //            discussion_object.vision_changes.push(vision_changes_array);

            discussion_object.vision_text_history.push(discussion_object.vision_text);
            discussion_object.vision_text = new_string;

            //suggestion grade is the new discussion grade
            models.Discussion.update({_id:discussion_object._id},
            {
                $addToSet: {vision_text_history: discussion_object.vision_text},
                $set:{vision_text: new_string, grade: suggestion_grade}},
                function(err, counter){
                cbk(err, discussion_object);
            });
        },

        function(disc_obj, cbk){
            suggestion_object.is_approved = true;
            suggestion_object.save(cbk);
        },

        function(sug_obj, num, cbk){
            suggestion_creator = sug_obj.creator_id;
            async.parallel([
                //set gamification
                function(par_cbk){
                    models.User.update({_id: sug_obj.creator_id}, {$inc: {"gamification.approved_suggestion": 1}}, function(err, obj){
                        par_cbk(err, obj);
                    });
                },

                //set notifications for creator
                function(par_cbk){
                    notifications.create_user_notification("approved_change_suggestion_you_created",
                            discussion_id, sug_obj.creator_id, null, null, function(err, obj){
                            par_cbk(err, obj);
                        });
                },

                //set notifications for graders
                //for each suggestion grade - copy it to discussion grade
                function(par_cbk){

                    async.waterfall([
                        function(wtr_cbk){
                            models.GradeSuggestion.find({_id: sug_obj._id}, wtr_cbk);
                        },

                        function(sugg_grades, wtr_cbk){
                            async.forEach(sugg_grades, iterator, function(err, result){
                                wtr_cbk(err || null, result || 0);
                            });
                        }
                    ], function(err, args){
                        par_cbk(err, args);
                    });
                }
            ], function(err, args){
                cbk(err, 8);
            })
        }
    ], function(err, arg){
        callback(err, arg);
    });
}

var calculate_sugg_threshold = function(factor, discussion_threshold){
    var log_base_75_of_x =
        Math.log(discussion_threshold) / Math.log(75);
    return Math.pow(log_base_75_of_x, 1.6) * factor;
}
<|endoftext|>


<commit_before>var React = require('react-native')
var MapView = require('react-native-maps')

var {
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  TextInput
} = React;

var styles = StyleSheet.create({
  mainContainer: {
    flex: 1,
    padding: 30,
    marginTop: 65,
    flexDirection: 'column',
    justifyContent: 'center',
    backgroundColor: '#48BBEC'
  },
  title: {
    marginBottom: 20,
    fontSize: 25,
    textAlign: 'center',
    color: '#fff'
  },
  searchInput: {
    height: 50,
    padding: 4,
    marginRight: 5,
    fontSize: 23,
    borderWidth: 1,
    borderColor: 'white',
    borderRadius: 8,
    color: 'white'
  },
  buttonText: {
    fontSize: 18,
    color: '#111',
    alignSelf: 'center'
  },
  button: {
    height: 45,
    flexDirection: 'row',
    backgroundColor: 'white',
    borderColor: 'white',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    marginTop: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  }
});

class MapPage extends React.Component{

  render(){
    return (
      <View style={styles.mainContainer}>
       <MapView
        style={styles.map}
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0,
          longitudeDelta: 0
        }}
          <Mapview.Marker
            coordinate={marker.latlng}
          />
        />
      </View>
    )
  }
}

module.exports = MapPage;<commit_msg>Add working breadcrumb with callout<commit_after>var React = require('react-native')
var MapView = require('react-native-maps')

var {
  StyleSheet,
  Text,
  View,
  TouchableHighlight,
  ActivityIndicatorIOS,
  TextInput,
  Image
} = React;

var styles = StyleSheet.create({
  mainContainer: {
    flex: 1,
    padding: 30,
    marginTop: 65,
    flexDirection: 'column',
    justifyContent: 'center',
    backgroundColor: '#48BBEC'
  },
  title: {
    marginBottom: 20,
    fontSize: 25,
    textAlign: 'center',
    color: '#fff'
  },
  searchInput: {
    height: 50,
    padding: 4,
    marginRight: 5,
    fontSize: 23,
    borderWidth: 1,
    borderColor: 'white',
    borderRadius: 8,
    color: 'white'
  },
  buttonText: {
    fontSize: 18,
    color: '#111',
    alignSelf: 'center'
  },
  button: {
    height: 45,
    flexDirection: 'row',
    backgroundColor: 'white',
    borderColor: 'white',
    borderWidth: 1,
    borderRadius: 8,
    marginBottom: 10,
    marginTop: 10,
    alignSelf: 'stretch',
    justifyContent: 'center'
  },
  map: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0
  }
});

// debugger
var marker = {
  latlng: {
    latitude: 37.78825,
    longitude: -122.4324
  },
  title: 'keep slayin boi',
  description: 'cant outstunt me when it comes to these fuckin cars'
};

class MapPage extends React.Component{
  render(){
    return (
      <View style={styles.mainContainer}>
       <MapView
        style={styles.map}
        initialRegion={{
          latitude: 37.78825,
          longitude: -122.4324,
          latitudeDelta: 0,
          longitudeDelta: 0
        }}>
          <MapView.Marker
            coordinate={marker.latlng}
            title={marker.title}
            description={marker.description}
          />
        </MapView>
      </View>
    )
  }
}

module.exports = MapPage;<|endoftext|>


<commit_before>import runloop from 'src/global/runloop';
import { warnOnceIfDebug } from 'utils/log';
import noop from 'utils/noop';
import findElement from '../../shared/findElement';

export default class Binding {
  constructor(element, name = 'value') {
    this.element = element;
    this.ractive = element.ractive;
    this.attribute = element.attributeByName[name];

    const interpolator = this.attribute.interpolator;
    interpolator.twowayBinding = this;

    const model = interpolator.model;

    if (model.isReadonly && !model.setRoot) {
      const keypath = model.getKeypath().replace(/^@/, '');
      warnOnceIfDebug(
        `Cannot use two-way binding on <${
          element.name
        }> element: ${keypath} is read-only. To suppress this warning use <${
          element.name
        } twoway='false'...>`,
        { ractive: this.ractive }
      );
      return false;
    }

    this.attribute.isTwoway = true;
    this.model = model;

    // initialise value, if it's undefined
    let value = model.get();
    this.wasUndefined = value === undefined;

    if (value === undefined && this.getInitialValue) {
      value = this.getInitialValue();
      model.set(value);
    }
    this.lastVal(true, value);

    const parentForm = findElement(this.element, false, 'form');
    if (parentForm) {
      this.resetValue = value;
      parentForm.formBindings.push(this);
    }
  }

  bind() {
    this.model.registerTwowayBinding(this);
  }

  handleChange() {
    const value = this.getValue();
    if (this.lastVal() === value) return;

    runloop.start();
    this.attribute.locked = true;
    this.model.set(value);
    this.lastVal(true, value);

    // if the value changes before observers fire, unlock to be updatable cause something weird and potentially freezy is up
    if (this.model.get() !== value) this.attribute.locked = false;
    else runloop.scheduleTask(() => (this.attribute.locked = false));

    runloop.end();
  }

  lastVal(setting, value) {
    if (setting) this.lastValue = value;
    else return this.lastValue;
  }

  rebind(next, previous) {
    if (this.model && this.model === previous) previous.unregisterTwowayBinding(this);
    if (next) {
      this.model = next;
      runloop.scheduleTask(() => next.registerTwowayBinding(this));
    }
  }

  rebound() {
    if (this.model) this.model.unregisterTwowayBinding(this);
    this.model = this.attribute.interpolator.model;
    this.model.registerTwowayBinding(this);
  }

  render() {
    this.node = this.element.node;
    this.node._ractive.binding = this;
    this.rendered = true; // TODO is this used anywhere?
  }

  setFromNode(node) {
    this.model.set(node.value);
  }

  unbind() {
    this.model.unregisterTwowayBinding(this);
  }
}

Binding.prototype.unrender = noop;
<commit_msg>shuffling binding mystery bug there's a mystery bug with fast repeated shuffles that causes a bindings to lose their model during shuffle briefly before being unbound checking for a model before registering or unregistering is a workaround<commit_after>import runloop from 'src/global/runloop';
import { warnOnceIfDebug } from 'utils/log';
import noop from 'utils/noop';
import findElement from '../../shared/findElement';

export default class Binding {
  constructor(element, name = 'value') {
    this.element = element;
    this.ractive = element.ractive;
    this.attribute = element.attributeByName[name];

    const interpolator = this.attribute.interpolator;
    interpolator.twowayBinding = this;

    const model = interpolator.model;

    if (model.isReadonly && !model.setRoot) {
      const keypath = model.getKeypath().replace(/^@/, '');
      warnOnceIfDebug(
        `Cannot use two-way binding on <${
          element.name
        }> element: ${keypath} is read-only. To suppress this warning use <${
          element.name
        } twoway='false'...>`,
        { ractive: this.ractive }
      );
      return false;
    }

    this.attribute.isTwoway = true;
    this.model = model;

    // initialise value, if it's undefined
    let value = model.get();
    this.wasUndefined = value === undefined;

    if (value === undefined && this.getInitialValue) {
      value = this.getInitialValue();
      model.set(value);
    }
    this.lastVal(true, value);

    const parentForm = findElement(this.element, false, 'form');
    if (parentForm) {
      this.resetValue = value;
      parentForm.formBindings.push(this);
    }
  }

  bind() {
    this.model.registerTwowayBinding(this);
  }

  handleChange() {
    const value = this.getValue();
    if (this.lastVal() === value) return;

    runloop.start();
    this.attribute.locked = true;
    this.model.set(value);
    this.lastVal(true, value);

    // if the value changes before observers fire, unlock to be updatable cause something weird and potentially freezy is up
    if (this.model.get() !== value) this.attribute.locked = false;
    else runloop.scheduleTask(() => (this.attribute.locked = false));

    runloop.end();
  }

  lastVal(setting, value) {
    if (setting) this.lastValue = value;
    else return this.lastValue;
  }

  rebind(next, previous) {
    if (this.model && this.model === previous) previous.unregisterTwowayBinding(this);
    if (next) {
      this.model = next;
      runloop.scheduleTask(() => next.registerTwowayBinding(this));
    }
  }

  rebound() {
    if (this.model) this.model.unregisterTwowayBinding(this);
    this.model = this.attribute.interpolator.model;
    this.model && this.model.registerTwowayBinding(this);
  }

  render() {
    this.node = this.element.node;
    this.node._ractive.binding = this;
    this.rendered = true; // TODO is this used anywhere?
  }

  setFromNode(node) {
    this.model.set(node.value);
  }

  unbind() {
    this.model && this.model.unregisterTwowayBinding(this);
  }
}

Binding.prototype.unrender = noop;
<|endoftext|>


<commit_before>import Hexagon from './hexagon'
import Avatar from '../../../components/ui/avatar'
import Link from '../../../components/ui/link'

const ListItem = ({item, toggleModal}) =>

      <Link to={`/sockets/${item.name}`}>
        <Hexagon
          title={item.name}
          fill={item.icon.background}
          icon={item.icon.url}
        />
      </Link>

        <Link to={`/sockets/${item.name}`}>
          {item.name}
        </Link>

        {item.description.length > 140
          ? `${item.description.substr(0, 140)}...`
          : item.description}

        <Avatar size={16} email={item.author.display_name} />

          {item.author.display_name}

export default ListItem
<commit_msg>fix(hexagon): when missing details<commit_after>import get from 'lodash.get'
import Hexagon from './hexagon'
import Avatar from '../../../components/ui/avatar'
import Link from '../../../components/ui/link'

const ListItem = ({item, toggleModal}) =>

      <Link to={`/sockets/${item.name}`}>
        <Hexagon
          title={item.name}
          fill={get(item, 'icon.background')}
          icon={get(item, 'icon.url')}
        />
      </Link>

        <Link to={`/sockets/${item.name}`}>
          {item.name}
        </Link>

        {item.description.length > 140
          ? `${item.description.substr(0, 140)}...`
          : item.description}

        <Avatar size={16} email={item.author.display_name} />

          {item.author.display_name}

export default ListItem
<|endoftext|>


<commit_before>
var _ = require("underscore");

class RestCollection {

    constructor(endpoint, Schema) {
        this._value = [];
        this._endpoint = endpoint;
        this._modelRef = new RestObject(this._endpoint + '/' + Schema.getModel(endpoint), Schema);
    }

    at(index) { return this._value.splice(index, 1); }
    id(postId) { return this._value.filter(p => p.id == postId); }

    val() { return this._value }
    get(args) { } // for getting posts
    post(payload) { } // for adding new data of type _modelRef;
}

class RestObject {
    constructor(endpoint, Schema) {
        this._endpoint = endpoint;
        this._args = {}; // the lite collection
        this._args.deep = {}; // the complete collection.

        // get the args for the different methods and append them

        // add collections that extend from there
        Schema.getCollections(endpoint).forEach(e => {
            this[e] = new RestCollection(this._endpoint + '/' + e, Schema);
        });
    }

    $(route) {
        if (!this[route])
            throw this._endpoint + '/' + route + " - route not reachable from model"
        return this[route];
    }

    val() { return this._model; }
    get(args) { } // fetch the post
    post(args) { }
    put(args) { }
    delete(args) { }
    asPostType() { }
    args() { } // returns the arguments for the different post types
    extend(a, b) {
        for (var key in b)
            if (b.hasOwnProperty(key))
                a[key] = b[key];
        return a;
    }

}

class Schema {
    constructor() {
        this.schema = {
            "routes": {
                "/wp/v2": {

                },
                "/wp/v2/posts": {

                },
                "/wp/v2/posts/(?P<id>[\\d]+)": {

                },
                "/wp/v2/posts/(?P<id>[\\d]+)/revisions": {},
                "/wp/v2/posts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {},
                "/wp/v2/pages": {},
                "/wp/v2/pages/(?P<id>[\\d]+)": {},
            }
        }
        this.routes = Object.keys(this.schema.routes).map(r => r.replace("parent", "id"));
    }

    getModel(endpoint) {
        let routes = this.routes;
        routes = routes.filter(r => this.isImmediate(endpoint, r) && !this.isCollection(r))
        return _.first(routes).replace(endpoint + "/", "");
    }

    getCollections(endpoint) {
        let routes = this.routes;
        return routes.filter(r => this.isImmediate(endpoint, r) && this.isCollection(r)).map(r => r.replace(endpoint + "/", ""));
    }

    isCollection(endpoint) {
        return !endpoint.endsWith("[\\d]+)"); // todo: improve targetting
    }

    isImmediate(endpoint, newEndpoint) {
        let diff = newEndpoint.replace(endpoint, "");
        return (newEndpoint.startsWith(endpoint)) && (diff.match(/\//g) || []).length == 1
    }

    onlyUnique(value, index, self) {
        return self.indexOf(value) === index;
    }
}

var schema = new Schema();
var ro = new RestObject("/wp/v2", schema);
const util = require('util');
console.log(util.inspect(ro, false, null));

<commit_msg>still on this schema thing<commit_after>
var _ = require("underscore");

class RestCollection {

    constructor(endpoint, parent, Schema) {
        this._schema = Schema;
        this._value = [];
        this._parent = parent;
        this._route = this._parent + '/' + endpoint;
        this._modelRef = new RestObject(Schema.getModel(this._route), this._route,  Schema);

        // process args
    }

    at(index) {
        // fetch object at index
        let value = this._value.splice(index, 1); // check bounds
        return new RestObject(value.id, this._route, this._schema, value);
    }

    id(postId) {
        let postArr = this._value.filter(p => p.id == postId);
        let value = postArr.size() == 0 ? null: postArr[0]
        return new RestObject(postId, this._route, this.Schema, value);
    }

    val() { return this._value }

    valOrGet() {
        if(this._value)
            return this._value;
        this.get();
        return this._value;
    }

    get(args) {
        // process args and append to route
        return this.Ajax.get(this._route).then(
            success => {
                success.data.forEach(e => this._value.push(this.toRestObj(e)));
                return this._value;
            }
        )
     }

     toRestObj(e) {
         return new RestObject(); // complete here
     }

    post(payload) {
        return this.Ajax.post(this._route, payload).then(
            success => {
                return success.data;
            }
        )
    }
}

class RestObject {
    constructor(endpoint, parent, Schema, model) {
        this._parent = parent;
        this._route = this._parent + '/' + endpoint;
        this._model = model;

        // get the args for the different methods and append them
        let args = Schema.getArgs(this._route);
        this._args = this.trimArgs(args);
        this._args.deep = args;

        // add collections that extend from there
        if(Schema) {
        Schema.getCollections(this._route).forEach(e => {
            this[e] = new RestCollection(e, this._route, Schema);
        });
        }
    }

    trimArgs(args) {
        // todo: non-critical
        return args;
    }

    $(route) {
        if (!this[route])
            throw this._route + '/' + route + " - route not reachable from model"
        return this[route];
    }

    val() { return this._model; }
    get(args) { } // fetch the post
    post(args) { }
    put(args) { }
    delete(args) { }
    asPostType() { }
    args() { } // returns the arguments for the different post types
    extend(a, b) {
        for (var key in b)
            if (b.hasOwnProperty(key))
                a[key] = b[key];
        return a;
    }

}

class Schema {
    constructor() {
        this.schema = {
            "routes": {
                "/wp/v2": {

                },
                "/wp/v2/posts": {

                },
                "/wp/v2/posts/(?P<id>[\\d]+)": {

                },
                "/wp/v2/posts/(?P<id>[\\d]+)/revisions": {},
                "/wp/v2/posts/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {},
                "/wp/v2/pages": {},
                "/wp/v2/pages/(?P<id>[\\d]+)": {},
            }
        }
        this.routes = Object.keys(this.schema.routes).map(r => r.replace("parent", "id"));
    }

    getArgs(endpoint) {
        // todo: non-critical
        return {};
    }

    getModel(endpoint) {
        let routes = this.routes;
        routes = routes.filter(r => this.isImmediate(endpoint, r) && !this.isCollection(r))
        return _.first(routes).replace(endpoint + "/", "");
    }

    getCollections(endpoint) {
        let routes = this.routes;
        return routes.filter(r => this.isImmediate(endpoint, r) && this.isCollection(r)).map(r => r.replace(endpoint + "/", ""));
    }

    isCollection(endpoint) {
        return !endpoint.endsWith("[\\d]+)"); // todo: improve targetting
    }

    isImmediate(endpoint, newEndpoint) {
        let diff = newEndpoint.replace(endpoint, "");
        return (newEndpoint.startsWith(endpoint)) && (diff.match(/\//g) || []).length == 1
    }

    onlyUnique(value, index, self) {
        return self.indexOf(value) === index;
    }
}

var schema = new Schema();
var ro = new RestObject("/wp/v2", schema);
const util = require('util');
console.log(util.inspect(ro, false, null));

<|endoftext|>