const db = require('../../models'), bcrypt = require('bcrypt-nodejs'), errorMaker = require('../helpers/error.maker'), uuidv4 = require('uuid/v4'); const { Grant, Cause, Region, Organization } = db; // Verify charity exports.verifyCharity = async (req, res, next) => { const charity_id = req.params.charity_id; await db.sequelize.query( 'UPDATE organizations SET verified = 1 WHERE id = :charity_id', { replacements: { charity_id } } ); return res.status(200).json({ message: 'Successfully verified charity' }); }; const log10 = val => { return Math.log(val) / Math.log(10); }; // POST to get suggested organizations based on chosen causes + regions exports.findSuggestedCharities = (req, res, next) => { const { amount, causes, regions } = req.body; if (amount == 0 || causes.length == 0 || regions.length == 0) { return next(errorMaker(400, 'Invalid options for grant suggestions')); } // formula for max amount of organizations to choose const max_orgs = Math.floor(1.5 * log10(amount)); const QUERY = 'SELECT * from organizations ' + 'WHERE primary_cause IN (:causes) or primary_region IN (:regions) ' + 'LIMIT :max_orgs'; db.sequelize .query(QUERY, { replacements: { causes, regions, max_orgs }, type: db.Sequelize.QueryTypes.SELECT }) .then(organizations => { res.status(200).json({ organizations, num_items: organizations.length, max_orgs, distribution: amount / organizations.length }); }) .catch(error => next(error)); }; // GET min and optimal amounts for the chosen causes + regions exports.findOptimalBundleAmounts = (req, res, next) => { const { num_causes, num_regions } = req.body; const feature_factor = parseInt(num_causes) + parseFloat(num_regions / 2); // calculate min amount for chosen causes and regions const min_amount = Math.ceil(feature_factor * 15); // calculate optimal for chosen causes and regions const optimal_amount = Math.ceil(Math.pow(5, feature_factor)); res.status(200).json({ min_amount, optimal_amount }); }; // POST create an organization exports.createOrganization = async (req, res, next) => { let { name, short_description, primary_contact_email, password, address, city, country, state, zip, primary_cause, primary_region, ein, estimate_asset_value, estimate_yearly_operating_cost, is_nonprofit, primary_contact_phone, primary_contact_first_name, primary_contact_last_name } = req.body; // primary_cause = primary_cause.trim().toLowerCase(); // primary_region = primary_region.trim().toLowerCase(); let transaction; try { transaction = await db.sequelize.transaction(); if (password.length < 6) return next(errorMaker(400, 'Password requirements not met!')); const orgs = await Organization.findAll({ where: { primary_contact_email } }); if (orgs.length >= 1) { return next(errorMaker(409, `Email Exists: ${primary_contact_email}`)); } const CAUSE_QUERY = `SELECT name FROM causes WHERE name = :primary_cause`; const causes = await db.sequelize.query(CAUSE_QUERY, { replacements: { primary_cause }, type: db.Sequelize.QueryTypes.SELECT }); const REGION_QUERY = `SELECT name FROM regions WHERE name = :primary_region`; const regions = await db.sequelize.query(REGION_QUERY, { replacements: { primary_region }, type: db.Sequelize.QueryTypes.SELECT }); // cause or region not found if (!causes.length || !regions.length) { throw errorMaker(400, `No valid cause or region`); } const saltRounds = await bcrypt.genSaltSync(10); // hash with salt rounds const hashedPass = await bcrypt.hashSync(password, saltRounds); // Store hash in DB const org = await Organization.create({ id: uuidv4(), name, short_description, primary_contact_email, password: hashedPass, address, city, country, state, zip, primary_cause, primary_region, ein, estimate_asset_value, estimate_yearly_operating_cost, is_nonprofit, primary_contact_phone, primary_contact_first_name, primary_contact_last_name }); await transaction.commit(); return res.status(201).json({ message: 'Organization created', org }); } catch (error) { console.log(error); await transaction.rollback(); next(error); } }; // FETCH all organizations exports.getAllOrganizations = async (req, res, next) => { const QUERY = ` select o.*, p.id as project_id, p.title as project_title, p.description as project_description, p.isComplete from organizations as o left join ( SELECT * from projects where id in ( select max(id) from projects group by organization_id ) ) as p on p.organization_id = o.id where o.is_verified = 1 and o.stripe_account_id IS NOT NULL `; const organizations = await db.sequelize.query(QUERY, { type: db.Sequelize.QueryTypes.SELECT }); return res.status(200).json({ organizations, number_items: organizations.length }); }; // FETCH depending on the given search exports.searchOrganizations = (req, res, next) => { let { search } = req.params; search = '%' + search + '%'; const limit = 10; const QUERY = ` select o.*, p.id as project_id, p.title as project_title, p.description as project_description, p.isComplete from organizations as o left join ( SELECT * from projects where id in ( select max(id) from projects group by organization_id ) ) as p on p.organization_id = o.id WHERE o.is_verified = 1 and o.stripe_account_id IS NOT NULL and (o.name LIKE :search or o.primary_cause LIKE :search or o.primary_region LIKE :search or p.title LIKE :search) LIMIT :limit `; db.sequelize .query(QUERY, { replacements: { search, limit }, type: db.Sequelize.QueryTypes.SELECT }) .then(organizations => { res.status(200).json({ organizations, number_items: organizations.length }); }) .catch(error => next(error)); }; exports.getOrganizationById = async (req, res, next) => { const { charityId } = req.params; const QUERY = ` select o.*, p.id as project_id, p.title as project_title, p.description as project_description, p.isComplete, ta.total_amount as total_amount from organizations as o left join ( SELECT * from projects where id in ( select max(id) from projects group by organization_id ) ) as p on p.organization_id = o.id left join ( SELECT SUM(amount) as total_amount, organization_id FROM grants_organizations GROUP BY organization_id ) as ta on ta.organization_id = o.id WHERE o.id = :charityId `; const org = await db.sequelize.query(QUERY, { replacements: { charityId }, type: db.Sequelize.QueryTypes.SELECT }); return res.status(200).json(org[0]); };