<commit_before>'use strict';
var mongoose 		    = require('mongoose'),
    assert              = require('assert'),
    fs                  = require('fs'),
    linkModel           = require('../models/Links'),
    aliasModel          = require('../models/Aliases'),
    commodityModel      = require('../models/Commodities'),
    companyGroupModel   = require('../models/CompanyGroups'),
    countryModel        = require('../models/Countries'),
    sourceModel 	    = require('../models/Sources'),
    userModel 		    = require('../models/Users'),
    actionModel 		= require('../models/Actions'),
    datasetModel 		= require('../models/Datasets'),
    companyModel        = require('../models/Companies'),
    concessionModel 	= require('../models/Concessions'),
    contractModel 	    = require('../models/Contracts'),
    projectModel        = require('../models/Projects'),
    //contributorModel 	= require('../models/Contributors'),
    //licenseModel      = require('../models/Licenses'),
    //productionModel   = require('../models/Production'),
    //reservesModel     = require('../models/Reserves'),
    transferModel     = require('../models/Transfers'),
    productionModel     = require('../models/Production'),
    model_load          = ['Facts'];

model_load.forEach(function(model_name) {
    require('../models/' + model_name);
});

module.exports 	= function(config, user, pass, env) {
    if (env === 'local') {
        mongoose.connect(config.db);
    } else {
        var ca = [fs.readFileSync(__dirname + "/certs/servercert.crt")];
        var options = {
            mongos: {
                ssl: true,
                sslValidate: true,
                sslCA:ca,
            }
        };
        mongoose.connect('mongodb://' + user + ':' + pass + config.db, options);
    }

    var db = mongoose.connection;
    db.on('error', console.error.bind(console, 'connection error...'));
    db.once('open', function callback() {
        console.log('rgi db opened');
    });

    userModel.createDefaultUsers();
    sourceModel.createDefaultSources();
    companyModel.createDefaultCompanies();
    commodityModel.createDefaultCommodities();
    companyGroupModel.createDefaultCompanyGroups();
    concessionModel.createDefaultConcessions();
    contractModel.createDefaultContracts();
    projectModel.createDefaultProjects();
    linkModel.createDefaultLinks();
    aliasModel.createDefaultAliases();
    countryModel.createDefaultCountries();
    transferModel.createDefaultTransfers();
    productionModel.createDefaultProductions();
    //contributorModel.createDefaultContributors();
    datasetModel.createDefaultDatasets();

};<commit_msg>SAVEPOINT<commit_after>'use strict';
var mongoose 		    = require('mongoose'),
    assert              = require('assert'),
    fs                  = require('fs'),
    linkModel           = require('../models/Links'),
    aliasModel          = require('../models/Aliases'),
    commodityModel      = require('../models/Commodities'),
    companyGroupModel   = require('../models/CompanyGroups'),
    countryModel        = require('../models/Countries'),
    sourceModel 	    = require('../models/Sources'),
    userModel 		    = require('../models/Users'),
    actionModel 		= require('../models/Actions'),
    datasetModel 		= require('../models/Datasets'),
    companyModel        = require('../models/Companies'),
    concessionModel 	= require('../models/Concessions'),
    contractModel 	    = require('../models/Contracts'),
    projectModel        = require('../models/Projects'),
    //contributorModel 	= require('../models/Contributors'),
    //licenseModel      = require('../models/Licenses'),
    //productionModel   = require('../models/Production'),
    //reservesModel     = require('../models/Reserves'),
    transferModel     = require('../models/Transfers'),
    productionModel     = require('../models/Production'),
    model_load          = ['Facts'];

model_load.forEach(function(model_name) {
    require('../models/' + model_name);
});

module.exports 	= function(config, user, pass, env) {

    if (env === 'local') {
        mongoose.connect(config.db);
    } else {
        var ca = [fs.readFileSync(__dirname + "/certs/servercert.crt")];
        var options = {
            mongos: {
                ssl: true,
                sslValidate: true,
                sslCA:ca,
            }
        };
        mongoose.connect('mongodb://' + user + ':' + pass + config.db, options);

    }
    var db = mongoose.connection;
    db.on('error', console.error.bind(console, 'connection error...'));
    db.once('open', function callback() {
        console.log('rgi db opened');
    });

    userModel.createDefaultUsers();
    sourceModel.createDefaultSources();
    companyModel.createDefaultCompanies();
    commodityModel.createDefaultCommodities();
    companyGroupModel.createDefaultCompanyGroups();
    concessionModel.createDefaultConcessions();
    contractModel.createDefaultContracts();
    projectModel.createDefaultProjects();
    linkModel.createDefaultLinks();
    aliasModel.createDefaultAliases();
    countryModel.createDefaultCountries();
    transferModel.createDefaultTransfers();
    productionModel.createDefaultProductions();
    //contributorModel.createDefaultContributors();
    datasetModel.createDefaultDatasets();

};<|endoftext|>


<commit_before>
/*
 * GET home page.
 */

//DB設定
var mysql = require('../config/db');
var connection = mysql.dbConect.blog();

exports.blogController = {

    'top' : function(req, res){

            var queryStr = 'select * from blog';
            connection.query(queryStr, [ new Date() ], function(err, rows) {
                if(err) {
                    console.log(err);
                } else {
                    console.log('db中身' + debug(rows));
                    res.render('index', { articles: rows });
                }
            });

    },
    'postBlog' : function(req , res){
        var body = req.body;
        insertBlogQuery = 'insert into blog (title, content) values("' + body.title + '","' + body.content + '")';
        connection.query(insertBlogQuery , function(err, rows){
            if(err) {
                console.log(err);
            } else {
                res.redirect('/');
            }
        });

    }

};
<commit_msg>post時にdatetimeを追加する<commit_after>
/*
 * GET home page.
 */

//DB設定
var mysql = require('../config/db');
var connection = mysql.dbConect.blog();

exports.blogController = {

    'top' : function(req, res){

            var queryStr = 'select * from blog';
            connection.query(queryStr, [ new Date() ], function(err, rows) {
                if(err) {
                    console.log(err);
                } else {
                    console.log('db中身' + debug(rows));
                    res.render('index', { articles: rows });
                }
            });

    },
    'postBlog' : function(req , res){
        var body = req.body;
        var DateObj = new Date();
        var datetimeAry = [DateObj.getFullYear() , DateObj.getMonth() + 1, DateObj.getDate() , DateObj.getHours() , DateObj.getMinutes() , DateObj.getSeconds()];
        var dateTime = datetimeAry.join('-');
        console.log(debug(dateTime));
        insertBlogQuery = 'insert into blog (title, content , create_time ,update_time) values("' + body.title + '","' + body.content + '")';
        connection.query(insertBlogQuery , function(err, rows){
            if(err) {
                console.log(err);
            } else {
                res.redirect('/');
            }
        });

    }

};
<|endoftext|>


<commit_before>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats SELECT query with complex WHERE", function() {
        const result = standardSqlFormatter.format(
            "SELECT count(*),'Column1','Testing', 'Testing Three' FROM Table1 WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) );"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  'Column1',\n" +
            "  'Testing',\n" +
            "  'Testing Three'\n" +
            "FROM\n" +
            "  Table1\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  );\n"
        );
    });

    it("formats SELECT with toplevel reserved words", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo WHERE name = 'John' GROUP BY some_column " +
            "HAVING column > 10 ORDER BY other_column LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "WHERE\n" +
            "  name = 'John'\n" +
            "GROUP BY\n" +
            "  some_column\n" +
            "HAVING\n" +
            "  column > 10\n" +
            "ORDER BY\n" +
            "  other_column\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("preserves case of keywords", function() {
        const result = standardSqlFormatter.format(
            "select distinct * frOM foo left join bar WHERe a > 1 and b = 3"
        );
        expect(result).toBe(
            "select\n" +
            "  distinct *\n" +
            "frOM\n" +
            "  foo\n" +
            "  left join bar\n" +
            "WHERe\n" +
            "  a > 1\n" +
            "  and b = 3\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "WHERE\n" +
            "  a > b\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  Customers\n" +
            "SET\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) AS ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) AS ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<commit_msg>XR-3710 split SELECT and WHERE tests<commit_after>import standardSqlFormatter from "xr/sqlFormatter/standardSqlFormatter";

describe("standardSqlFormatter", function() {
    it("formats simple SELECT query", function() {
        const result = standardSqlFormatter.format("SELECT count(*),Column1 FROM Table1;");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*),\n" +
            "  Column1\n" +
            "FROM\n" +
            "  Table1;\n"
        );
    });

    it("formats complex SELECT", function() {
        const result = standardSqlFormatter.format(
            "SELECT DISTINCT name, ROUND(age/7) field1, 18 + 20 AS field2, 'some string' FROM foo;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  DISTINCT name,\n" +
            "  ROUND(age / 7) field1,\n" +
            "  18 + 20 AS field2,\n" +
            "  'some string'\n" +
            "FROM\n" +
            "  foo;\n"
        );
    });

    it("formats SELECT with complex WHERE", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo WHERE Column1 = 'testing' " +
            "AND ( (Column2 = Column3 OR Column4 >= NOW()) );"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "WHERE\n" +
            "  Column1 = 'testing'\n" +
            "  AND (\n" +
            "    (\n" +
            "      Column2 = Column3\n" +
            "      OR Column4 >= NOW()\n" +
            "    )\n" +
            "  );\n"
        );
    });

    it("formats SELECT with toplevel reserved words", function() {
        const result = standardSqlFormatter.format(
            "SELECT * FROM foo WHERE name = 'John' GROUP BY some_column " +
            "HAVING column > 10 ORDER BY other_column LIMIT 5,10 OFFSET 20;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "WHERE\n" +
            "  name = 'John'\n" +
            "GROUP BY\n" +
            "  some_column\n" +
            "HAVING\n" +
            "  column > 10\n" +
            "ORDER BY\n" +
            "  other_column\n" +
            "LIMIT\n" +
            "  5, 10 OFFSET 20;\n"
        );
    });

    it("preserves case of keywords", function() {
        const result = standardSqlFormatter.format(
            "select distinct * frOM foo left join bar WHERe a > 1 and b = 3"
        );
        expect(result).toBe(
            "select\n" +
            "  distinct *\n" +
            "frOM\n" +
            "  foo\n" +
            "  left join bar\n" +
            "WHERe\n" +
            "  a > 1\n" +
            "  and b = 3\n"
        );
    });

    it("formats SELECT query with SELECT query inside it", function() {
        const result = standardSqlFormatter.format(
            "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  *,\n" +
            "  SUM(*) AS sum\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      Posts\n" +
            "    LIMIT\n" +
            "      30\n" +
            "  )\n" +
            "WHERE\n" +
            "  a > b\n"
        );
    });

    it("formats SELECT query with INNER JOIN", function() {
        const result = standardSqlFormatter.format(
            "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " +
            "INNER JOIN orders ON customers.customer_id = orders.customer_id;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  customer_id.from,\n" +
            "  COUNT(order_id) AS total\n" +
            "FROM\n" +
            "  customers\n" +
            "  INNER JOIN orders ON customers.customer_id = orders.customer_id;\n"
        );
    });

    it("formats SELECT query with different comments", function() {
        const result = standardSqlFormatter.format(
            "SELECT\n" +
            "/* This is a block comment\n" +
            "*/\n" +
            "* FROM\n" +
            "-- This is another comment\n" +
            "MyTable # One final comment\n" +
            "WHERE 1 = 2;"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  /* This is a block comment\n" +
            "  */\n" +
            "  *\n" +
            "FROM\n" +
            "  -- This is another comment\n" +
            "  MyTable # One final comment\n" +
            "WHERE\n" +
            "  1 = 2;\n"
        );
    });

    it("formats SELECT query with different variable types", function() {
        const result = standardSqlFormatter.format(
            "SELECT @variable, @'variable name';"
        );
        expect(result).toBe(
            "SELECT\n" +
            "  @variable,\n" +
            "  @'variable name';\n"
        );
    });

    it("formats simple INSERT query", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stavanger');"
        );
        expect(result).toBe(
            "INSERT INTO Customers (ID, MoneyBalance, Address, City)\n" +
            "VALUES\n" +
            "  (\n" +
            "    12, -123.4, 'Skagen 2111', 'Stavanger'\n" +
            "  );\n"
        );
    });

    it("formats long INSERT query including SELECT, IF and INNER JOIN parts", function() {
        const result = standardSqlFormatter.format(
            "INSERT INTO `PREFIX_specific_price` (`id_product`, `id_shop`, `id_currency`, `id_country`, `id_group`, `priority`, " +
            "`price`, `from_quantity`, `reduction`,   `reduction_type`, `from`, `to`) (" +
            "SELECT dq.`id_product`, 1, 1,0,1, 0, 0.00, dq.`quantity`, IF(dq.`id_discount_type` = 2, dq.`value`, dq.`value` / 100)," +
            "IF (dq.`id_discount_type` = 2, 'amount', 'percentage'), '0000-00-00 00:00:00', '0000-00-00 00:00:00' " +
            "FROM `PREFIX_discount_quantity` dq INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`));"
        );
        expect(result).toBe(
            "INSERT INTO `PREFIX_specific_price` (\n" +
            "  `id_product`, `id_shop`, `id_currency`,\n" +
            "  `id_country`, `id_group`, `priority`,\n" +
            "  `price`, `from_quantity`, `reduction`,\n" +
            "  `reduction_type`, `from`, `to`\n" +
            ") (\n" +
            "  SELECT\n" +
            "    dq.`id_product`,\n" +
            "    1,\n" +
            "    1,\n" +
            "    0,\n" +
            "    1,\n" +
            "    0,\n" +
            "    0.00,\n" +
            "    dq.`quantity`,\n" +
            "    IF(\n" +
            "      dq.`id_discount_type` = 2, dq.`value`,\n" +
            "      dq.`value` / 100\n" +
            "    ),\n" +
            "    IF (\n" +
            "      dq.`id_discount_type` = 2, 'amount',\n" +
            "      'percentage'\n" +
            "    ),\n" +
            "    '0000-00-00 00:00:00',\n" +
            "    '0000-00-00 00:00:00'\n" +
            "  FROM\n" +
            "    `PREFIX_discount_quantity` dq\n" +
            "    INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)\n" +
            ");\n"
        );
    });

    it("formats simple UPDATE query", function() {
        const result = standardSqlFormatter.format(
            "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  Customers\n" +
            "SET\n" +
            "  ContactName = 'Alfred Schmidt',\n" +
            "  City = 'Hamburg'\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfreds Futterkiste';\n"
        );
    });

    it("formats simple DELETE query", function() {
        const result = standardSqlFormatter.format(
            "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;"
        );
        expect(result).toBe(
            "DELETE FROM\n" +
            "  Customers\n" +
            "WHERE\n" +
            "  CustomerName = 'Alfred'\n" +
            "  AND Phone = 5002132;\n"
        );
    });

    it("formats simple DROP query", function() {
        const result = standardSqlFormatter.format(
            "DROP TABLE IF EXISTS admin_role;"
        );
        expect(result).toBe(
            "DROP\n" +
            "  TABLE IF EXISTS admin_role;\n"
        );
    });

    it("formats uncomplete query", function() {
        const result = standardSqlFormatter.format("SELECT count(");
        expect(result).toBe(
            "SELECT\n" +
            "  count(\n"
        );
    });

    it("formats query that ends with open comment", function() {
        const result = standardSqlFormatter.format("SELECT count(*)\n/*Comment");
        expect(result).toBe(
            "SELECT\n" +
            "  count(*)\n" +
            "  /*Comment\n"
        );
    });

    it("formats UPDATE query with AS part", function() {
        const result = standardSqlFormatter.format(
            "UPDATE customers SET totalorders = ordersummary.total  FROM ( SELECT * FROM bank) AS ordersummary"
        );
        expect(result).toBe(
            "UPDATE\n" +
            "  customers\n" +
            "SET\n" +
            "  totalorders = ordersummary.total\n" +
            "FROM\n" +
            "  (\n" +
            "    SELECT\n" +
            "      *\n" +
            "    FROM\n" +
            "      bank\n" +
            "  ) AS ordersummary\n"
        );
    });

    it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() {
        const result = standardSqlFormatter.format("SELECT * FROM foo LEFT \t OUTER  \n JOIN bar ORDER \n BY blah");
        expect(result).toBe(
            "SELECT\n" +
            "  *\n" +
            "FROM\n" +
            "  foo\n" +
            "  LEFT OUTER JOIN bar\n" +
            "ORDER BY\n" +
            "  blah\n"
        );
    });

    it("formats single-char operators", function() {
        expect(standardSqlFormatter.format("foo = bar")).toBe("foo = bar\n");
        expect(standardSqlFormatter.format("foo < bar")).toBe("foo < bar\n");
        expect(standardSqlFormatter.format("foo > bar")).toBe("foo > bar\n");
        expect(standardSqlFormatter.format("foo + bar")).toBe("foo + bar\n");
        expect(standardSqlFormatter.format("foo - bar")).toBe("foo - bar\n");
        expect(standardSqlFormatter.format("foo * bar")).toBe("foo * bar\n");
        expect(standardSqlFormatter.format("foo / bar")).toBe("foo / bar\n");
        expect(standardSqlFormatter.format("foo % bar")).toBe("foo % bar\n");
    });

    it("formats multi-char operators", function() {
        expect(standardSqlFormatter.format("foo != bar")).toBe("foo != bar\n");
        expect(standardSqlFormatter.format("foo <> bar")).toBe("foo <> bar\n");
        expect(standardSqlFormatter.format("foo == bar")).toBe("foo == bar\n"); // N1QL
        expect(standardSqlFormatter.format("foo || bar")).toBe("foo || bar\n"); // Oracle, Postgres, N1QL string concat

        expect(standardSqlFormatter.format("foo <= bar")).toBe("foo <= bar\n");
        expect(standardSqlFormatter.format("foo >= bar")).toBe("foo >= bar\n");

        expect(standardSqlFormatter.format("foo !< bar")).toBe("foo !< bar\n");
        expect(standardSqlFormatter.format("foo !> bar")).toBe("foo !> bar\n");
    });

    it("formats logical operators", function() {
        expect(standardSqlFormatter.format("foo ALL bar")).toBe("foo ALL bar\n");
        expect(standardSqlFormatter.format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)\n");
        expect(standardSqlFormatter.format("EXISTS bar")).toBe("EXISTS bar\n");
        expect(standardSqlFormatter.format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)\n");
        expect(standardSqlFormatter.format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'\n");
        expect(standardSqlFormatter.format("foo IS NULL")).toBe("foo IS NULL\n");
        expect(standardSqlFormatter.format("UNIQUE foo")).toBe("UNIQUE foo\n");
    });

    it("formats AND/OR operators", function() {
        expect(standardSqlFormatter.format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz\n");
        expect(standardSqlFormatter.format("foo AND bar")).toBe("foo\nAND bar\n");
        expect(standardSqlFormatter.format("foo OR bar")).toBe("foo\nOR bar\n");
    });

    it("recognizes strings", function() {
        expect(standardSqlFormatter.format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo JOIN bar'")).toBe("'foo JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo JOIN bar`")).toBe("`foo JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]\n");
    });

    it("recognizes escaped strings", function() {
        expect(standardSqlFormatter.format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\"\n");
        expect(standardSqlFormatter.format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'\n");
        expect(standardSqlFormatter.format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`\n");
        expect(standardSqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]\n");
    });
});
<|endoftext|>


<commit_before>var https = require('https');
var fileSystem = require('fs');
var exitHook = require('exit-hook');
const regex = require('./regexModule');
const descriptionParser = require('./descriptionParser');

var batchSize = 10;

var statePath = "state.json";
var state = {
    after: ""
};

var updateOnExit = false;

function getReddits(after) {
    return new Promise(function(resolve, reject) {
        function get_json(url, callback) {
            console.log(`Querying ${url}`);
            https.get(url, function(res) {
                var body = '';
                res.on('data', function(chunk) {
                    body += chunk;
                });

                res.on('end', function() {
                    var response = JSON.parse(body);
                    callback(response);
                });

                res.on('error', function(error) {
                    reject(error);
                });
            });
        }

        function buildURL(after) {
            var url = "https://www.reddit.com/reddits.json";
            if (!!batchSize) {
                url += "?limit=" + batchSize;
            }
            if (!!after) {
                url += "&after=" + after;
            }
            return url;
        }

        function parseSubreddit(subreddit) {
            console.log(`Scanning ${subreddit.url}`);

            var subredditData;
            var fileName = regex.getNameFromURL(subreddit.url)
            if (fileSystem.existsSync(`./parsed_subreddits/${fileName}.json`)) {
                subredditData = JSON.parse(fileSystem.readFileSync(`./parsed_subreddits/${fileName}.json`));
            } else {
                subredditData = {
                    tags: []
                }
            }

            subredditData.url = subreddit.url;
            subredditData.name = subreddit.name;
            subredditData.total_subscribers = subreddit.subscribers;

            const csvMatcher = /\b[\w\s]+\b/gi;
            var tags = regex.getListOfMatches(subreddit.audience_target, csvMatcher);
            var i;
            for (i in tags) {
                updateTags(subredditData, {
                    tag: tags[i]
                }, 0);
            }

            subredditData.relatedSubreddits = descriptionParser.getMentionedSubreddits(subreddit);

            writeSubreddit(fileName, subredditData);

            for (i in subredditData.relatedSubreddits) {
                //console.log("spreading tags to " + subredditData.relatedSubreddits[i]);
                propagateSubredditData(subredditData.relatedSubreddits[i], subredditData, 1, []);
            }

            console.log(`Finished ${subreddit.url}`);
        }

        get_json(buildURL(after, batchSize), function(response) {
            var subreddit;
            for (subreddit in response.data.children) {
                parseSubreddit(response.data.children[subreddit].data);
            }

            resolve(response.data.after);
        });
    });
}

function writeSubreddit(fileName, subredditData) {
    //console.log("writing " + fileName + " to file system");
    var subredditPath = `./parsed_subreddits/${fileName}.json`;
    fileSystem.writeFileSync(subredditPath, JSON.stringify(subredditData));
}

function propagateSubredditData(subredditURL, parentSubredditData, depth, searched) {
    // This is really inefficient but that is because the db isnt ready yet

    var fileName = regex.getNameFromURL(subredditURL);
    // handle self reference
    //console.log("Searching for " + depth);
    if (searched.indexOf(fileName) !== -1) {
        console.log("it caught " + fileName);
        return;
    }

    var subredditData;
    if (fileSystem.existsSync(`./parsed_subreddits/${fileName}.json`)) {
        subredditData = JSON.parse(fileSystem.readFileSync(`./parsed_subreddits/${fileName}.json`));
        //console.log("Found " + subredditData.url + " in file system");
    } else {
        subredditData = {
            url: subredditURL,
            tags: [],
            relatedSubreddits: [parentSubredditData.url.replace(/^\/|\/$/g, '')]
        };
        //console.log("Going to create " + subredditURL + " in file system");
    }
    var updatedTags = false;
    var i;
    for (i in parentSubredditData.tags) {
        updatedTags = updatedTags || updateTags(subredditData, parentSubredditData.tags[i], depth);
    }
    if (updatedTags) {
        writeSubreddit(regex.getNameFromURL(subredditURL), subredditData);
        if (!!subredditData.relatedSubreddits) {
            console.log("spreading tags recursively");
            for (i in subredditData.relatedSubreddits) {
                // we want to update any possible tags that weren't originally referenced.
                var nextFileName = regex.getNameFromURL(subredditData.relatedSubreddits[i]);
                if (searched.indexOf(nextFileName) > -1) {
                    searched.splice(index, 1);
                }
                propagateSubredditData(subredditData.relatedSubreddits[i], subredditData.tags, depth + 1, searched);
            }
        }
    } else {
        searched.push(fileName);
    }
}

// Tags are {tag:"tagName", mentionDistance:X}
function updateTags(subredditData, newTag, mentionDistance) {
    var i;
    for (i in subredditData.tags) {
        var existingTag = subredditData.tags[i];
        if (existingTag.tag === newTag.tag) {
            if (existingTag.mentionDistance > mentionDistance) {
                //console.log("updating closer mention: " + newTag.tag + ": " + mentionDistance);
                existingTag.mentionDistance = mentionDistance;
                return true;
            }
            return false;
        }
    }
    //console.log("tag was not found, adding: " + newTag.tag + ": " + mentionDistance);
    subredditData.tags.push({
        tag: newTag.tag,
        mentionDistance: mentionDistance
    });
    return true;
}

function continueSearch(after) {
    getReddits(after).then(
        function(after) {
            state.after = after;
            setTimeout(function() {
                continueSearch(after)
            }, 1000);
        },
        function(error) {
            console.log(error);
            process.exit(1);
        });
}

function loadStateJSON(callback) {
    fileSystem.readFile(statePath, (err, data) => {
        if (err) {
            console.log("state.json file not initialized");
        } else {
            if (data.byteLength === 0) {
                console.log("state.json file is empty");
            } else {
                state = JSON.parse(data);
                console.log("state initialized to " + state.after);
            }
        }
        callback(state.after);
    });
}

exitHook(function() {
    if (updateOnExit) {
        fileSystem.writeFileSync(statePath, JSON.stringify(state));
        console.log("    Crawler terminated, current state saved as " + state.after);
    }
});

module.exports = {
    crawl: function() {
        loadStateJSON(function(after) {
            updateOnExit = true;
            continueSearch(after)
        });
    },
    getAllTags: function() {
        var tags = [];
        var parsedSubreddits = fileSystem.readdirSync("./parsed_subreddits/");
        var index;
        for (index in parsedSubreddits) {
            var subreddit = JSON.parse(fileSystem.readFileSync("./parsed_subreddits/" + parsedSubreddits[index]));
            var i;
            for (i in subreddit.tags) {
                var tag = subreddit.tags[i];
                if (tags.indexOf(tag) === -1) {
                    tags.push(tag);
                }
            }
        }
        console.log("Total: " + tags.length);
        console.log(tags);
    },
    getSubredditForTag: function(tag) {
        var parsedSubreddits = fileSystem.readdirSync("./parsed_subreddits/");
        var index;
        for (index in parsedSubreddits) {
            var subreddit = JSON.parse(fileSystem.readFileSync("./parsed_subreddits/" + parsedSubreddits[index]));
            if (subreddit.tags.indexOf(tag) !== -1) {
                console.log(subreddit.url);
            }
        }
    }
};

require('make-runnable');
<commit_msg>Crawler fully functioning<commit_after>var https = require('https');
var fileSystem = require('fs');
var exitHook = require('exit-hook');
const regex = require('./regexModule');
const descriptionParser = require('./descriptionParser');

var batchSize = 1;

var statePath = "state.json";
var state = {
    after: "",
    maxDepthReached: 0,
    maxDepthSubreddit: ""
};

var updateOnExit = false;

function getReddits(after) {
    return new Promise(function(resolve, reject) {
        function get_json(url, callback) {
            console.log(`Querying ${url}`);
            https.get(url, function(res) {
                var body = '';
                res.on('data', function(chunk) {
                    body += chunk;
                });

                res.on('end', function() {
                    var response = JSON.parse(body);
                    callback(response);
                });

                res.on('error', function(error) {
                    reject(error);
                });
            });
        }

        function buildURL(after) {
            var url = "https://www.reddit.com/reddits.json";
            if (!!batchSize) {
                url += "?limit=" + batchSize;
            }
            if (!!after) {
                url += "&after=" + after;
            }
            return url;
        }

        function parseSubreddit(subreddit) {
            var subredditData;
            var fileName = regex.getNameFromURL(subreddit.url)
            if (fileSystem.existsSync(`./parsed_subreddits/${fileName}.json`)) {
                subredditData = JSON.parse(fileSystem.readFileSync(`./parsed_subreddits/${fileName}.json`));
                console.log(`Updating ${subreddit.url}...`);
            } else {
                console.log(`Discovered ${subreddit.url}!`);
                subredditData = {
                    tags: []
                }
            }

            subredditData.url = subreddit.url;
            subredditData.name = subreddit.name;
            subredditData.total_subscribers = subreddit.subscribers;

            const csvMatcher = /\b[\w\s]+\b/gi;
            var tags = regex.getListOfMatches(subreddit.audience_target, csvMatcher);
            var i;
            for (i in tags) {
                updateTags(subredditData, {
                    tag: tags[i],
                    mentionDistance: 0
                }, 0);
            }

            subredditData.relatedSubreddits = descriptionParser.getMentionedSubreddits(subreddit);

            writeSubreddit(fileName, subredditData);

            for (i in subredditData.relatedSubreddits) {
                console.log("Recursively Updating Immediate Relation: " + subredditData.relatedSubreddits[i]);
                propagateSubredditData(subredditData.relatedSubreddits[i], subredditData, 1, []);
            }

            console.log(`Finished ${subreddit.url}`);
        }

        get_json(buildURL(after, batchSize), function(response) {
            var subreddit;
            for (subreddit in response.data.children) {
                parseSubreddit(response.data.children[subreddit].data);
            }

            resolve(response.data.after);
        });
    });
}

function writeSubreddit(fileName, subredditData) {
    var subredditPath = `./parsed_subreddits/${fileName}.json`;
    fileSystem.writeFileSync(subredditPath, JSON.stringify(subredditData));
}

function propagateSubredditData(subredditURL, parentSubredditData, depth, searched) {
    // Statistical analysis
    if (state.maxDepthReached < depth) {
        state.maxDepthReached = depth;
        state.maxDepthSubreddit = subredditURL;
    }
    // This is really inefficient but that is because the db isnt ready yet
    var fileName = regex.getNameFromURL(subredditURL);

    // handle self reference
    if (searched.indexOf(fileName) !== -1) {
        return;
    }

    var subredditData;
    var relatedURL = parentSubredditData.url.replace(/^\/|\/$/g, '');
    if (fileSystem.existsSync(`./parsed_subreddits/${fileName}.json`)) {
        subredditData = JSON.parse(fileSystem.readFileSync(`./parsed_subreddits/${fileName}.json`));
        if (subredditData.relatedSubreddits.indexOf(relatedURL) === -1) {
            subredditData.relatedSubreddits.push(relatedURL);
        }
    } else {
        subredditData = {
            url: subredditURL,
            tags: [],
            relatedSubreddits: [relatedURL]
        };
    }
    var updatedTags = false;
    var i;
    for (i in parentSubredditData.tags) {
        updatedTags = updatedTags || updateTags(subredditData, parentSubredditData.tags[i], depth);
    }
    if (updatedTags) {
        writeSubreddit(regex.getNameFromURL(subredditURL), subredditData);
        if (!!subredditData.relatedSubreddits) {
            for (i in subredditData.relatedSubreddits) {
                // we want to update any possible tags that weren't originally referenced.
                var nextFileName = regex.getNameFromURL(subredditData.relatedSubreddits[i]);
                var index = searched.indexOf(nextFileName);
                if (index > -1) {
                    searched.splice(index, 1);
                }
                propagateSubredditData(subredditData.relatedSubreddits[i], subredditData, depth + 1, searched);
            }
        }
    } else {
        searched.push(fileName);
    }
}

// Tags are {tag:"tagName", mentionDistance:X}
function updateTags(subredditData, newTag, depth) {
    var i;
    for (i in subredditData.tags) {
        var existingTag = subredditData.tags[i];
        if (existingTag.tag === newTag.tag) {
            if (existingTag.mentionDistance > (newTag.mentionDistance + depth)) {
                // console.log(subredditData.url + ": updating {" + existingTag.tag + ": " + existingTag.mentionDistance + " => " + newTag.mentionDistance + depth + "}");
                existingTag.mentionDistance = (newTag.mentionDistance + depth);
                return true;
            }
            return false;
        }
    }
    //console.log("tag was not found, adding: " + newTag.tag + ": " + depth);
    subredditData.tags.push({
        tag: newTag.tag,
        mentionDistance: newTag.mentionDistance + depth
    });
    return true;
}

function continueSearch(after) {
    getReddits(after).then(
        function(after) {
            state.after = after;
            setTimeout(function() {
                console.log("Max Depth Reached: " + state.maxDepthSubreddit + " at " + state.maxDepthReached + " references.");
                continueSearch(after)
            }, 1000);
        },
        function(error) {
            console.log(error);
            process.exit(1);
        });
}

function loadStateJSON(callback) {
    fileSystem.readFile(statePath, (err, data) => {
        if (err) {
            console.log("state.json file not initialized");
        } else {
            if (data.byteLength === 0) {
                console.log("state.json file is empty");
            } else {
                state = JSON.parse(data);
            }
        }
        callback(state.after);
    });
}

exitHook(function() {
    if (updateOnExit) {
        fileSystem.writeFileSync(statePath, JSON.stringify(state));
        console.log("    Crawler terminated, current state saved as " + state.after);
    }
});

module.exports = {
    crawl: function(size) {
        if (size > 100) {
            console.log("Max batch size is 100");
            size = 100;
        } else if (size < 0) {
            console.log("Min batch size is 1");
            size = 1;
        }
        batchSize = size;
        loadStateJSON(function(after) {
            updateOnExit = true;
            console.log("Starting search from " + state.after);
            continueSearch(after)
        });
    },
    getAllTags: function() {
        var tags = [];
        var parsedSubreddits = fileSystem.readdirSync("./parsed_subreddits/");
        var index;
        for (index in parsedSubreddits) {
            var subreddit = JSON.parse(fileSystem.readFileSync("./parsed_subreddits/" + parsedSubreddits[index]));
            var i;
            for (i in subreddit.tags) {
                var tag = subreddit.tags[i];
                if (tags.indexOf(tag) === -1) {
                    tags.push(tag);
                }
            }
        }
        console.log("Total: " + tags.length);
        console.log(tags);
    },
    getRankedSubredditsForTags: function() {
        var parsedSubreddits = fileSystem.readdirSync("./parsed_subreddits/");
        var index;
        for (index in parsedSubreddits) {
            var subreddit = JSON.parse(fileSystem.readFileSync("./parsed_subreddits/" + parsedSubreddits[index]));
            if (subreddit.tags.indexOf(tag) !== -1) {
                console.log(subreddit.url);
            }
        }
    }
};

require('make-runnable');
<|endoftext|>


<commit_before>/**
 * Chatroom model.  Handles adding and removing users from the chatroom, and
 * keeps track of who is in the room.
 */
module.exports = function() {
	/**
	 * Constructor.
	 * @param {string} roomname
	 */
	var Chatroom = function(roomname) {
		this.name = roomname;
		this.users = {};
	};

	/**
	 * Get a list of usernames currently in the room.
	 * @return {Array} users
	 */
	Chatroom.prototype.getUsers = function() {
		return Object.keys(this.users);
	};

	/**
	 * Add a user to the room.
	 * @param {User} user
	 */
	Chatroom.prototype.add = function(user) {
		this.users[user.who] = user;
		user.join(this.name);
	};

	/**
	 * Remove a user from the room.
	 * @param {User} user
	 */
	Chatroom.prototype.remove = function(user) {
		delete this.users[user.who];
		user.leave();
	};

	/**
	 * Test if a specific username is in the room.
	 * @return {boolean}
	 */
	Chatroom.prototype.here = function(username) {
		return !!this.users[username];
	};

	return Chatroom;
};<commit_msg>getName()<commit_after>/**
 * Chatroom model.  Handles adding and removing users from the chatroom, and
 * keeps track of who is in the room.
 */
module.exports = function() {
	/**
	 * Constructor.
	 * @param {string} roomname
	 */
	var Chatroom = function(roomname) {
		this.name = roomname;
		this.users = {};
	};

	/**
	 * Get the name of this chatroom
	 * @return {string} roomname
	 */
	 Chatroom.prototype.getName = function() {
	 	return this.name;
	 };

	/**
	 * Get a list of usernames currently in the room.
	 * @return {Array} users
	 */
	Chatroom.prototype.getUsers = function() {
		return Object.keys(this.users);
	};

	/**
	 * Add a user to the room.
	 * @param {User} user
	 */
	Chatroom.prototype.add = function(user) {
		this.users[user.who] = user;
		user.join(this.name);
	};

	/**
	 * Remove a user from the room.
	 * @param {User} user
	 */
	Chatroom.prototype.remove = function(user) {
		delete this.users[user.who];
		user.leave();
	};

	/**
	 * Test if a specific username is in the room.
	 * @return {boolean}
	 */
	Chatroom.prototype.here = function(username) {
		return !!this.users[username];
	};

	return Chatroom;
};<|endoftext|>


<commit_before>$(function(){
	//notice
	$('

').html($('.notice-list>li').eq(0).html()).appendTo($('.notice-list'));
	var h = $('.notice-box').height();
	var notice_order = 0;
	var notice_len = $('.notice-list').children().length;
	setTimeout(noticeRoll,3000);

	function noticeRoll(){
		notice_order++;
		$('.notice-list').css({
			'transition-duration':'.5s',
			'transform': 'translate3d(0px, -'+notice_order*h+'px, 0px)'
		});
		if (notice_order == notice_len-1){
			notice_order = 0;
			setTimeout(function(){
				$('.notice-list').css({
					'transition-duration':'0s',
					'transform': 'translate3d(0px, -'+notice_order*h+'px, 0px)'
				});
			},500);
		}
		setTimeout(noticeRoll,3000);
	}

	//slide
	$('

').html($('.slide-list>li').eq(0).html()).appendTo($('.slide-list'));
	var len = $('.slide-list').children().length;
	var wid = $('.news-slide').width();
	var order = 0;
	setTimeout(slide,3000);
	function slide(){
		order++;
		$('.slide-list').css({
			'transition-duration':'.5s',
			'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
		});
		if (order == len-1){
			order = 0;
			setTimeout(function(){
				$('.slide-list').css({
					'transition-duration':'0s',
					'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
				});
			},500);
		}
		$('.slide-paging').find('.active').removeClass('active');
		$('.slide-paging>li').eq(order).addClass('active');

		setTimeout(slide,3000);
	}
	$('.slide-paging>li>a').each(function(index){
		$(this).on('click',function(){
			if (index == order-1){
				return false;
			}
			order = index;
			$('.slide-list').css({
				'transition-duration':'.5s',
				'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
			});
			$('.slide-paging').find('.active').removeClass('active');
			$('.slide-paging>li').eq(order).addClass('active');
		});
	});

	//side-bar
	var colH = new Array();
	colH[0] = 0;
	$('.column-box').each(function(){
		colH.push($(this).offset().top);
	});
	$(window).on('scroll',function(){
		if ($(window).width() < 1200){
			return false;
		}
		var scrollH = $(document).scrollTop() - $(window).height()/2;
		if (scrollH > 0){
			$('.side-bar').css('display','block');
		}else{
			$('.side-bar').css('display','none');
		}
		for (var i=1;i<colH.length;i++){
			if (scrollH>colH[i-1] && scrollH<colH[i]){
				$('.side-bar').find('.active').removeClass('active');
				$('.side-bar>ul>li').eq(i-1).addClass('active');
			}
		}
	});

	//Game
	var vm = new Vue({
		el: '#game',
        data:{
        	games:'',
        	liWid:[],
        	hrStyle:[],
        	mWid:[],
        	mLeft:[],
        	styleObject:{
        		width: '1200%',
        		transform:'translateX(0px)'
        	}
        },
        created:function(){
            var url="http://cms.test.com/api/match";
            this.$http.get(url).then(function(res){
                this.games=res.body.data;
                for (var i=0;i<this.games.length; i++){
                	var game = this.games[i];
                	var count = game.groupCount;
                	if (parseInt(count) == 0){
                		var wid = 0;
                	}else{
                		var wid = 1170 / count;
                	}
                	this.liWid[game.id] = wid;
                	this.hrStyle[game.id] = {
                		wid : wid * (count - 1),
                		left : wid / 2 + 15
                	}
                	this.mWid[game.id] = new Array();
                	this.mLeft[game.id] = new Array();
                	for (var j=0;j<game.groups.length; j++){
                		var group = game.groups[j];
                		this.mWid[game.id][group.id] = 277 * group.matchCount;
                		var temp = 0;
                		for (var k=0;k<group.matchs.length; k++){
                			if (parseInt(group.matchs[k].default) == 1){
                				temp = k;
                				break;
                			}
                		}
                		this.mLeft[game.id][group.id] = -277 * temp;
                	}
                }
            },function(response){
                console.info(response);
            });
        },
        methods:{
			changeGame:function (gameid) {
				var groupid = 0;
				for (var i=0;i<this.games.length; i++){
					if (this.games[i].id == gameid){
						this.games[i].default = 1;
						for (var j=0;j<this.games[i].groups.length; j++){
							if (this.games[i].groups[j].default == 1){
								groupid = this.games[i].groups[j].id;
								break;
							}
						}
					}else{
						this.games[i].default = 0;
					}
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
				this.styleObject.width = this.mWid[gameid][groupid]+'px';
			},
			changeGroup:function (gameid,groupid) {
				for (var i=0;i<this.games.length; i++){
					if (gameid == this.games[i].id){
						for (var j=0;j<this.games[i].groups.length; j++){
							if (groupid == this.games[i].groups[j].id){
								this.games[i].groups[j].default = 1;
							}else{
								this.games[i].groups[j].default = 0;
							}
						}
					}
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
				this.styleObject.width = this.mWid[gameid][groupid]+'px';
			},
			translateLeft:function (gameid,groupid) {
				if (this.mLeft[gameid][groupid] <= -277){
					this.mLeft[gameid][groupid] += 277;
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
			},
			translateRight:function (gameid,groupid) {
				if (this.mLeft[gameid][groupid] > -(this.mWid[gameid][groupid] - 277 * 4)){
					this.mLeft[gameid][groupid] -= 277;
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
			}

        }
	});
});

function getLeft(str){
	var arr = str.replace(/[^0-9^,^-]/ig,"").split(',');
	return parseInt(arr[4]);
}<commit_msg>API接口地址更改<commit_after>$(function(){
	//notice
	$('

').html($('.notice-list>li').eq(0).html()).appendTo($('.notice-list'));
	var h = $('.notice-box').height();
	var notice_order = 0;
	var notice_len = $('.notice-list').children().length;
	setTimeout(noticeRoll,3000);

	function noticeRoll(){
		notice_order++;
		$('.notice-list').css({
			'transition-duration':'.5s',
			'transform': 'translate3d(0px, -'+notice_order*h+'px, 0px)'
		});
		if (notice_order == notice_len-1){
			notice_order = 0;
			setTimeout(function(){
				$('.notice-list').css({
					'transition-duration':'0s',
					'transform': 'translate3d(0px, -'+notice_order*h+'px, 0px)'
				});
			},500);
		}
		setTimeout(noticeRoll,3000);
	}

	//slide
	$('

').html($('.slide-list>li').eq(0).html()).appendTo($('.slide-list'));
	var len = $('.slide-list').children().length;
	var wid = $('.news-slide').width();
	var order = 0;
	setTimeout(slide,3000);
	function slide(){
		order++;
		$('.slide-list').css({
			'transition-duration':'.5s',
			'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
		});
		if (order == len-1){
			order = 0;
			setTimeout(function(){
				$('.slide-list').css({
					'transition-duration':'0s',
					'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
				});
			},500);
		}
		$('.slide-paging').find('.active').removeClass('active');
		$('.slide-paging>li').eq(order).addClass('active');

		setTimeout(slide,3000);
	}
	$('.slide-paging>li>a').each(function(index){
		$(this).on('click',function(){
			if (index == order-1){
				return false;
			}
			order = index;
			$('.slide-list').css({
				'transition-duration':'.5s',
				'transform': 'translate3d(-'+order*wid+'px, 0px, 0px)'
			});
			$('.slide-paging').find('.active').removeClass('active');
			$('.slide-paging>li').eq(order).addClass('active');
		});
	});

	//side-bar
	var colH = new Array();
	colH[0] = 0;
	$('.column-box').each(function(){
		colH.push($(this).offset().top);
	});
	$(window).on('scroll',function(){
		if ($(window).width() < 1200){
			return false;
		}
		var scrollH = $(document).scrollTop() - $(window).height()/2;
		if (scrollH > 0){
			$('.side-bar').css('display','block');
		}else{
			$('.side-bar').css('display','none');
		}
		for (var i=1;i<colH.length;i++){
			if (scrollH>colH[i-1] && scrollH<colH[i]){
				$('.side-bar').find('.active').removeClass('active');
				$('.side-bar>ul>li').eq(i-1).addClass('active');
			}
		}
	});

	//Game
	var vm = new Vue({
		el: '#game',
        data:{
        	games:'',
        	liWid:[],
        	hrStyle:[],
        	mWid:[],
        	mLeft:[],
        	styleObject:{
        		width: '1200%',
        		transform:'translateX(0px)'
        	}
        },
        created:function(){
            var url="http://cms.yearn.cc/api/match";
            this.$http.get(url).then(function(res){
                this.games=res.body.data;
                for (var i=0;i<this.games.length; i++){
                	var game = this.games[i];
                	var count = game.groupCount;
                	if (parseInt(count) == 0){
                		var wid = 0;
                	}else{
                		var wid = 1170 / count;
                	}
                	this.liWid[game.id] = wid;
                	this.hrStyle[game.id] = {
                		wid : wid * (count - 1),
                		left : wid / 2 + 15
                	}
                	this.mWid[game.id] = new Array();
                	this.mLeft[game.id] = new Array();
                	for (var j=0;j<game.groups.length; j++){
                		var group = game.groups[j];
                		this.mWid[game.id][group.id] = 277 * group.matchCount;
                		var temp = 0;
                		for (var k=0;k<group.matchs.length; k++){
                			if (parseInt(group.matchs[k].default) == 1){
                				temp = k;
                				break;
                			}
                		}
                		this.mLeft[game.id][group.id] = -277 * temp;
                	}
                }
            },function(response){
                console.info(response);
            });
        },
        methods:{
			changeGame:function (gameid) {
				var groupid = 0;
				for (var i=0;i<this.games.length; i++){
					if (this.games[i].id == gameid){
						this.games[i].default = 1;
						for (var j=0;j<this.games[i].groups.length; j++){
							if (this.games[i].groups[j].default == 1){
								groupid = this.games[i].groups[j].id;
								break;
							}
						}
					}else{
						this.games[i].default = 0;
					}
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
				this.styleObject.width = this.mWid[gameid][groupid]+'px';
			},
			changeGroup:function (gameid,groupid) {
				for (var i=0;i<this.games.length; i++){
					if (gameid == this.games[i].id){
						for (var j=0;j<this.games[i].groups.length; j++){
							if (groupid == this.games[i].groups[j].id){
								this.games[i].groups[j].default = 1;
							}else{
								this.games[i].groups[j].default = 0;
							}
						}
					}
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
				this.styleObject.width = this.mWid[gameid][groupid]+'px';
			},
			translateLeft:function (gameid,groupid) {
				if (this.mLeft[gameid][groupid] <= -277){
					this.mLeft[gameid][groupid] += 277;
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
			},
			translateRight:function (gameid,groupid) {
				if (this.mLeft[gameid][groupid] > -(this.mWid[gameid][groupid] - 277 * 4)){
					this.mLeft[gameid][groupid] -= 277;
				}
				this.styleObject.transform = 'translateX('+this.mLeft[gameid][groupid]+'px)';
			}

        }
	});
});

function getLeft(str){
	var arr = str.replace(/[^0-9^,^-]/ig,"").split(',');
	return parseInt(arr[4]);
}<|endoftext|>


<commit_before>import fs from 'fs';
import denodeify from 'denodeify';
import path from 'path';
import ffmpeg from 'fluent-ffmpeg';
import pick from 'lodash/pick';
import { dolarizeQueryParams, prepareAll, choke, releaseChoke } from '_server/utils';
import { getConfig } from '_server/config';

import ID3 from 'id3-parser';
import union from 'lodash/union';

const readDir = denodeify(fs.readdir);
const readFile = denodeify(fs.readFile);

const stat = denodeify(fs.stat);

let audioExtensions = [];
let initialDir = '';
let prepared = {};

export default function init() {
  initialDir = getConfig('musicDir');
  audioExtensions = getConfig('audioExtensions').split(',').map(e => e.trim());
  return prepareAll({
    fetchByLocation: 'select * from AllTracks where location = $location',
    selectGenreId: 'select idGenre from Genres where genre = $genre',
    insertGenre: 'insert into Genres (genre) values ($genre)',
    selectPersonId: 'select idPerson from People where artist = $artist',
    insertPerson: 'insert into People (artist) values ($artist)',
    selectAlbumId: 'select idAlbum from Albums where album = $album',
    insertAlbum: 'insert into Albums (album) values ($album)',
    hasAlbumArtistMap: 'select count(*) as `count` from AlbumArtistMap where idAlbum = $idAlbum and idArtist = $idArtist',
    insertAlbumArtistMap: 'insert into AlbumArtistMap (idAlbum, idArtist) values ($idAlbum, $idArtist)',
    insertTrack: `
      insert into Tracks(
        title, idArtist, idComposer, idAlbumArtist, idAlbum, track, date, length, idGenre, location, fileModified, size, hasIssues
      ) values (
        $title, $idArtist, $idComposer, $idAlbumArtist, $idAlbum, $track, $date, $length, $idGenre, $location, $fileModified, $size, $hasIssues
      )
    `,
  })
  .then((p) => {
    prepared = p;
  });
}

const useTags = [
  'album',
  'title',
  'track',
  'artist',
  'album',
  'album_artist',
  'composer',
  'date',
  'genre',
  'TLEN',
];

const genreRxp = /\((\d+)\)/;

export function getPersonId(name) {
  const who = { $artist: name.trim() };
  const resId = `artist:${name}`;
  return choke(resId)
  .then(() => prepared.selectPersonId.get(who))
  .then(row => (
    row
    ? row.idPerson
    : prepared.insertPerson.run(who)
    .then(res => res.lastID)
  ))
  .then(id => releaseChoke(resId, id));
}

export function insertTrack(tags) {
  const t = pick(tags, [
    'title',
    'location',
    'size',
    'date',
  ]);
  const loc = path.basename(t.location, path.extname(t.location)).split('-').map(s => s.trim());
  if (!t.title) {
    t.hasIssues = 1;
    t.title = loc[loc.length - 1];
  }
  if (tags.track) t.track = parseInt(tags.track, 10);
  if (t.track <= 0) t.track = null;
  if (t.date <= 0) t.date = null;
  if (tags.TLEN) t.length = parseInt(tags.TLEN, 10);
  if (t.length <= 0) t.length = null;
  t.fileModified = tags.fileModified.toISOString();
  return Promise.resolve()
  .then(() => {
    let artist = tags.artist;
    if (!artist) {
      t.hasIssues = 1;
      artist = loc.length === 3 ? loc[1] : loc[0];
    }
    if (artist) {
      return getPersonId(artist)
      .then((id) => {
        t.idArtist = id;
      });
    }
    return null;
  })
  .then(() => {
    if ('album_artist' in tags) {
      return getPersonId(tags.album_artist)
      .then((id) => {
        t.idAlbumArtist = id;
      });
    }
    return null;
  })
  .then(() => {
    if ('composer' in tags) {
      return getPersonId(tags.composer)
      .then((id) => {
        t.idComposer = id;
      });
    }
    return null;
  })
  .then(() => {
    if ('genre' in tags) {
      const m = genreRxp.exec(tags.genre);
      if (m) return parseInt(m[1], 10);
      const which = { $genre: tags.genre };
      const resId = `genre:${tags.genre}`;
      return choke(resId)
      .then(() => prepared.selectGenreId.get(which))
      .then(row => (
        row
        ? row.idGenre
        : prepared.insertGenre.run(which)
        .then(res => res.lastID)
      ))
      .then((id) => {
        t.idGenre = id;
      })
      .then(() => releaseChoke(resId));
    }
    return null;
  })
  .then(() => {
    let album = tags.album;
    if (!album && loc.length === 3) {
      t.hasIssues = 1;
      album = loc[0];
    }
    if (album) {
      const which = { $album: album };
      const resId = `album:${album}`;
      return choke(resId)
      .then(() => prepared.selectAlbumId.get(which))
      .then(row => (
        row
        ? row.idAlbum
        : prepared.insertAlbum.run(which)
        .then(res => res.lastID)
      ))
      .then((id) => {
        t.idAlbum = id;
      })
      .then(() => releaseChoke(resId));
    }
    return null;
  })
  .then(() => {
    if (t.idAlbumArtist && t.idAlbum) {
      const map = {
        $idAlbum: t.idAlbum,
        $idArtist: t.idAlbumArtist,
      };
      return prepared.hasAlbumArtistMap.get(map)
      .then(row => (
        row.count
        ? null
        : prepared.insertAlbumArtistMap.run(map)
      ));
    }
    return null;
  })
  .then(() => prepared.insertTrack.run(dolarizeQueryParams(t)))
  .then(res => res.lastID);
}

function readTags(fileName) {
  return new Promise((resolve, reject) => {
    ffmpeg(fileName)
    .ffprobe((err, data) => {
      if (err) {
        reject(err);
      } else {
        const ts = data.format.tags;
        readFile(fileName)
        .then(fileBuffer => ID3.parse(fileBuffer))
        .then((tags) => {
          // /* eslint-disable */
          // tags.date = tags.year;
          // delete tags.year;
          // tags.album_artist = tags.band;
          // delete tags.band;
          // tags.length = tags.TLEN;
          // delete tags.TLEN;
          // delete tags.version;
          // /* eslint-enable */
          // console.log('-------------\n', fileName);
          // if (data.format && typeof data.format.tags !== 'object') {
          //   console.log('********** data format with no tags', data.format);
          // }
          // const mixed = union(
          //   Object.keys(ts),
          //   Object.keys(tags)
          // );
          // mixed.forEach((tag) => {
          //   const tagFF = ts[tag];
          //   const tagID3 = tags[tag];
          //   if ((tagFF || tagID3) && tagFF !== tagID3) {
          //     console.log(tag);
          //     console.log('    ', tagFF);
          //     console.log('    ', tagID3);
          //   }
          // });
          resolve(pick(ts, useTags));
        });
      }
    });
  });
}

const pending = [];
let stopRequested = false;
const errors = [];
let steps = 100;

export function scan(dir) {
  return readDir(dir)
  .then(files => Promise.all(files.map((file) => {
    const fullName = path.join(dir, file);
    const relName = path.relative(initialDir, fullName);
    return stat(fullName)
    .then((st) => {
      if (st.isDirectory()) return pending.push(fullName);
      if (st.isFile()) {
        const ext = path.extname(file).substr(1);
        if (audioExtensions.indexOf(ext) !== -1) {
          return prepared.fetchByLocation.all({
            $location: relName,
          })
          .then((rows) => {
            if (rows.length) {
              if (rows.length > 1) {
                return Promise.reject(`refreshDb: Multiple entries for ${file}, idTracks: ${rows.map(row => row.idTrack).join(',')}`);
              }
            } else {
              return readTags(fullName)
              .then(t => insertTrack(
                Object.assign(t, {
                  location: relName,
                  fileModified: st.mtime,
                  size: st.size,
                })
              ));
            }
            return null;
          });
        }
      }
      return null;
    });
  })))
  .then(() => {
    steps -= 1;
    if (stopRequested || steps < 0) {
      pending.length = 0;
      stopRequested = false;
      db.exec('vacuum');
    } else if (pending.length) {
      setImmediate(scan, pending.shift());
    }
  })
  .catch((err) => {
    console.error(err);
    errors.push(err);
    pending.length = 0;
    stopRequested = false;
  });
}

export function refreshStatus() {
  if (pending.length) {
    if (stopRequested) {
      return {
        status: 'stop pending',
        pending: pending.length,
        errors,
      };
    }
    return {
      status: 'working',
      pending: pending.length,
      errors,
    };
  }
  return {
    status: 'stopped',
    pending: pending.length,
    errors,
  };
}

export function startRefresh() {
  stopRequested = false;
  setImmediate(scan, initialDir);
  return refreshStatus;
}

export function stopRefresh() {
  stopRequested = true;
  return refreshStatus();
}
<commit_msg>got it working with id3-parse<commit_after>import fs from 'fs';
import denodeify from 'denodeify';
import path from 'path';
import { dolarizeQueryParams, prepareAll, choke, releaseChoke } from '_server/utils';
import { getConfig } from '_server/config';

import ID3 from 'id3-parser';

const readDir = denodeify(fs.readdir);
const readFile = denodeify(fs.readFile);

const stat = denodeify(fs.stat);

let audioExtensions = [];
let initialDir = '';
let prepared = {};

export default function init() {
  initialDir = getConfig('musicDir');
  audioExtensions = getConfig('audioExtensions').split(',').map(e => e.trim());
  return prepareAll({
    fetchByLocation: 'select * from AllTracks where location = $location',
    selectGenreId: 'select idGenre from Genres where genre = $genre',
    insertGenre: 'insert into Genres (genre) values ($genre)',
    selectPersonId: 'select idPerson from People where artist = $artist',
    insertPerson: 'insert into People (artist) values ($artist)',
    selectAlbumId: 'select idAlbum from Albums where album = $album',
    insertAlbum: 'insert into Albums (album) values ($album)',
    hasAlbumArtistMap: 'select count(*) as `count` from AlbumArtistMap where idAlbum = $idAlbum and idArtist = $idArtist',
    insertAlbumArtistMap: 'insert into AlbumArtistMap (idAlbum, idArtist) values ($idAlbum, $idArtist)',
    /*
    `idTrack` INTEGER PRIMARY KEY AUTOINCREMENT,
    `title` TEXT,
    `idArtist` INTEGER,
    `idComposer` INTEGER,
    `idAlbumArtist` INTEGER,
    `idAlbum` INTEGER,
    `track` INTEGER,
    `year` INTEGER,
    `length` INTEGER,
    `idGenre` INTEGER,
    `location` TEXT,
    `fileModified` TEXT,
    `size` INTEGER,
    `hasIssues` INTEGER,
    */
    insertTrack: `
      insert into Tracks(
        "title",
        "idArtist",
        "idComposer",
        "idAlbumArtist",
        "idAlbum",
        "track",
        "year",
        "length",
        "idGenre",
        "location",
        "fileModified",
        "size",
        "hasIssues"
      ) values (
        $title,
        $idArtist,
        $idComposer,
        $idAlbumArtist,
        $idAlbum,
        $track,
        $year,
        $length,
        $idGenre,
        $location,
        $fileModified,
        $size,
        $hasIssues
      )
    `,
  })
  .then((p) => {
    prepared = p;
  });
}

const genreRxp = /\((\d+)\)/;

export function getPersonId(name) {
  const who = { $artist: name.trim() };
  const resId = `artist:${name}`;
  return choke(resId)
  .then(() => prepared.selectPersonId.get(who))
  .then(row => (
    row
    ? row.idPerson
    : prepared.insertPerson.run(who)
    .then(res => res.lastID)
  ))
  .then(id => releaseChoke(resId, id));
}

export function insertTrack(tags) {
  return Promise.resolve()
  .then(() => {
    const artist = tags.artist;
    if (artist) {
      return getPersonId(artist)
      .then((id) => {
        delete tags.artist;
        tags.idArtist = id;
      });
    }
    return null;
  })
  .then(() => {
    if (tags.album_artist) {
      return getPersonId(tags.album_artist)
      .then((id) => {
        delete tags.album_artist;
        tags.idAlbumArtist = id;
      });
    }
    return null;
  })
  .then(() => {
    if (tags.composer) {
      return getPersonId(tags.composer)
      .then((id) => {
        delete tags.composer;
        tags.idComposer = id;
      });
    }
    return null;
  })
  .then(() => {
    if (tags.genre) {
      const m = genreRxp.exec(tags.genre);
      if (m) {
        delete tags.genre;
        return (tags.idGenre = parseInt(m[1], 10));
      }
      const which = { $genre: tags.genre };
      const resId = `genre:${tags.genre}`;
      return choke(resId)
      .then(() => prepared.selectGenreId.get(which))
      .then(row => (
        row
        ? row.idGenre
        : prepared.insertGenre.run(which)
        .then(res => res.lastID)
      ))
      .then((id) => {
        delete tags.genre;
        tags.idGenre = id;
      })
      .then(() => releaseChoke(resId));
    }
    return null;
  })
  .then(() => {
    const album = tags.album;
    if (album) {
      const which = { $album: album };
      const resId = `album:${album}`;
      return choke(resId)
      .then(() => prepared.selectAlbumId.get(which))
      .then(row => (
        row
        ? row.idAlbum
        : prepared.insertAlbum.run(which)
        .then(res => res.lastID)
      ))
      .then((id) => {
        delete tags.album;
        tags.idAlbum = id;
      })
      .then(() => releaseChoke(resId));
    }
    return null;
  })
  .then(() => {
    if (tags.idAlbumArtist && tags.idAlbum) {
      const map = {
        $idAlbum: tags.idAlbum,
        $idArtist: tags.idAlbumArtist,
      };
      return prepared.hasAlbumArtistMap.get(map)
      .then(row => (
        row.count
        ? null
        : prepared.insertAlbumArtistMap.run(map)
      ));
    }
    return null;
  })
  .then(() => prepared.insertTrack.run(dolarizeQueryParams(tags)))
  .then(res => res.lastID)
  .catch(console.error);
}

function readTags(fileName) {
  // https://github.com/ddsol/mp3-duration
  // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/duration
  return readFile(fileName)
    .then(fileBuffer => ID3.parse(fileBuffer))
    .then((tags) => {
      const t = {};
      [
        'album',
        'title',
        'artist',
        'composer',
        'genre',
      ].forEach((key) => {
        const val = tags[key];
        if (val && val.trim().length) t[key] = val;
      });
      t.year = parseInt(tags.year, 10);
      if (t.year <= 0) t.year = null;
      t.album_artist = (tags.band && tags.band.trim().length ? tags.band : undefined);
      const loc = path.basename(fileName, path.extname(fileName)).split('-').map(s => s.trim());
      if (!t.title) {
        t.hasIssues = 1;
        t.title = loc[loc.length - 1];
      }
      if (!t.artist) {
        t.hasIssues = 1;
        t.artist = loc.length === 3 ? loc[1] : loc[0];
      }
      if (!t.album && loc.length === 3) {
        t.hasIssues = 1;
        t.album = loc[0];
      }

      if (tags.track) t.track = parseInt(tags.track, 10);
      if (t.track <= 0) t.track = null;
      if (tags.length) t.length = parseInt(tags.length, 10);
      if (t.length <= 0) t.length = null;
      return t;
    });
}

const pending = [];
let stopRequested = false;
const errors = [];
let steps = 100;

export function scan(dir) {
  return readDir(dir)
  .then(files => Promise.all(files.map((file) => {
    const fullName = path.join(dir, file);
    const relName = path.relative(initialDir, fullName);
    return stat(fullName)
    .then((st) => {
      if (st.isDirectory()) return pending.push(fullName);
      if (st.isFile()) {
        const ext = path.extname(file).substr(1);
        if (audioExtensions.indexOf(ext) !== -1) {
          return prepared.fetchByLocation.all({
            $location: relName,
          })
          .then((rows) => {
            if (rows.length) {
              if (rows.length > 1) {
                return Promise.reject(`refreshDb: Multiple entries for ${file}, idTracks: ${rows.map(row => row.idTrack).join(',')}`);
              }
            } else {
              return readTags(fullName)
              .then(t => insertTrack(
                Object.assign(t, {
                  location: relName,
                  fileModified: st.mtime.toISOString(),
                  size: st.size,
                })
              ));
            }
            return null;
          });
        }
      }
      return null;
    });
  })))
  .then(() => {
    steps -= 1;
    if (stopRequested || steps < 0) {
      pending.length = 0;
      stopRequested = false;
      db.exec('vacuum');
    } else if (pending.length) {
      setImmediate(scan, pending.shift());
    }
  })
  .catch((err) => {
    console.error(err);
    errors.push(err);
    pending.length = 0;
    stopRequested = false;
  });
}

export function refreshStatus() {
  if (pending.length) {
    if (stopRequested) {
      return {
        status: 'stop pending',
        pending: pending.length,
        errors,
      };
    }
    return {
      status: 'working',
      pending: pending.length,
      errors,
    };
  }
  return {
    status: 'stopped',
    pending: pending.length,
    errors,
  };
}

export function startRefresh() {
  stopRequested = false;
  setImmediate(scan, initialDir);
  return refreshStatus;
}

export function stopRefresh() {
  stopRequested = true;
  return refreshStatus();
}
<|endoftext|>