Files
gancio/server/api/controller/event.js

378 lines
12 KiB
JavaScript
Raw Normal View History

2019-04-03 00:25:12 +02:00
const crypto = require('crypto')
2019-10-20 14:22:55 +02:00
const moment = require('moment-timezone')
2019-04-03 00:25:12 +02:00
const { Op } = require('sequelize')
2020-01-21 01:24:32 +01:00
const _ = require('lodash')
2019-12-04 01:18:05 +01:00
const { event: Event, resource: Resource, tag: Tag, place: Place, notification: Notification } = require('../models')
2019-05-30 12:04:14 +02:00
const Sequelize = require('sequelize')
2019-10-20 14:22:55 +02:00
const exportController = require('./export')
2019-09-11 23:17:12 +02:00
const debug = require('debug')('controller:event')
2019-04-03 00:25:12 +02:00
const eventController = {
2020-01-27 00:47:03 +01:00
async _getMeta () {
2019-05-30 12:04:14 +02:00
const places = await Place.findAll({
2019-05-30 12:12:51 +02:00
order: [[Sequelize.literal('weigth'), 'DESC']],
2019-05-30 12:04:14 +02:00
attributes: {
2019-09-11 19:12:24 +02:00
include: [[Sequelize.fn('count', Sequelize.col('events.placeId')), 'weigth']],
2020-01-21 01:24:32 +01:00
exclude: ['createdAt', 'updatedAt']
2019-05-30 12:04:14 +02:00
},
2019-05-30 12:12:51 +02:00
include: [{ model: Event, attributes: [] }],
group: ['place.id']
2019-05-30 12:04:14 +02:00
})
const tags = await Tag.findAll({
2020-01-15 23:51:57 +01:00
raw: true,
2019-06-07 17:02:33 +02:00
order: [['weigth', 'DESC']],
2019-05-30 12:04:14 +02:00
attributes: {
2020-01-21 01:24:32 +01:00
exclude: ['createdAt', 'updatedAt']
2019-09-11 19:12:24 +02:00
}
2019-05-30 12:12:51 +02:00
})
2019-05-30 12:04:14 +02:00
2020-01-27 00:47:03 +01:00
return { places, tags }
},
async getMeta (req, res) {
res.json(await eventController._getMeta())
2019-04-03 00:25:12 +02:00
},
async getNotifications (event, action) {
debug('getNotifications "%s" (%s)', event.title, action)
2019-09-11 19:12:24 +02:00
function match (event, filters) {
2019-04-03 00:25:12 +02:00
// matches if no filter specified
2019-09-11 19:12:24 +02:00
if (!filters) { return true }
2019-04-03 00:25:12 +02:00
// check for visibility
2019-09-11 19:12:24 +02:00
if (typeof filters.is_visible !== 'undefined' && filters.is_visible !== event.is_visible) { return false }
2019-04-03 00:25:12 +02:00
2019-09-11 19:12:24 +02:00
if (!filters.tags && !filters.places) { return true }
if (!filters.tags.length && !filters.places.length) { return true }
2019-04-03 00:25:12 +02:00
if (filters.tags.length) {
2020-01-21 01:24:32 +01:00
const m = _.intersection(event.tags.map(t => t.tag), filters.tags)
2019-09-11 19:12:24 +02:00
if (m.length > 0) { return true }
2019-04-03 00:25:12 +02:00
}
if (filters.places.length) {
if (filters.places.find(p => p === event.place.name)) {
return true
}
}
}
2020-01-21 01:24:32 +01:00
const notifications = await Notification.findAll({ where: { action }, include: [Event] })
2019-04-03 00:25:12 +02:00
// get notification that matches with selected event
const ret = notifications.filter(notification => match(event, notification.filters))
return ret
2019-04-03 00:25:12 +02:00
},
2019-09-11 19:12:24 +02:00
async updatePlace (req, res) {
2019-04-03 00:25:12 +02:00
const place = await Place.findByPk(req.body.id)
await place.update(req.body)
res.json(place)
},
2019-06-14 23:26:13 +02:00
// TODO retrieve next/prev event also
// select id, start_datetime, title from events where start_datetime > (select start_datetime from events where id=89) order by start_datetime limit 20;
2019-09-11 19:12:24 +02:00
async get (req, res) {
2019-10-20 14:22:55 +02:00
const format = req.params.format || 'json'
2019-07-04 01:09:35 +02:00
const is_admin = req.user && req.user.is_admin
2020-01-15 23:51:57 +01:00
const id = Number(req.params.event_id)
2019-10-26 00:27:12 +02:00
let event = await Event.findByPk(id, {
2019-09-11 19:12:24 +02:00
attributes: {
2019-07-23 16:26:54 +02:00
exclude: ['createdAt', 'updatedAt']
2019-07-23 01:31:43 +02:00
},
2019-06-14 23:26:13 +02:00
include: [
2019-06-26 14:44:21 +02:00
{ model: Tag, attributes: ['tag', 'weigth'], through: { attributes: [] } },
{ model: Place, attributes: ['name', 'address'] },
2019-12-04 01:18:05 +01:00
{ model: Resource, where: !is_admin && { hidden: false }, required: false }
2019-06-07 17:02:33 +02:00
],
2020-01-15 23:51:57 +01:00
order: [[Resource, 'id', 'DESC']]
2019-05-30 12:04:14 +02:00
})
2019-06-25 01:05:38 +02:00
2019-07-04 01:02:45 +02:00
if (event && (event.is_visible || is_admin)) {
2019-10-26 00:27:12 +02:00
event = event.toJSON()
event.tags = event.tags.map(t => t.tag)
2019-10-20 14:22:55 +02:00
if (format === 'json') {
res.json(event)
} else if (format === 'ics') {
// last arg is alarms/reminder, ref: https://github.com/adamgibbons/ics#attributes (alarms)
exportController.ics(req, res, [event], [{
action: 'display',
trigger: { hours: 1, before: true }
}])
2019-10-20 14:22:55 +02:00
}
} else {
res.sendStatus(404)
}
2019-04-03 00:25:12 +02:00
},
2019-11-06 11:31:56 +01:00
/** confirm an anonymous event
* and send its relative notifications
*/
2019-09-11 19:12:24 +02:00
async confirm (req, res) {
2019-07-04 00:29:50 +02:00
const id = Number(req.params.event_id)
2019-04-03 00:25:12 +02:00
const event = await Event.findByPk(id)
2019-09-11 19:12:24 +02:00
if (!event) { return res.sendStatus(404) }
if (!req.user.is_admin && req.user.id !== event.userId) {
return res.sendStatus(403)
}
2019-04-03 00:25:12 +02:00
try {
2019-07-04 01:02:45 +02:00
event.is_visible = true
await event.save()
2019-04-03 00:25:12 +02:00
res.sendStatus(200)
2019-09-11 19:12:24 +02:00
// send notification
const notifier = require('../../notifier')
notifier.notifyEvent('Create', event.id)
2019-04-03 00:25:12 +02:00
} catch (e) {
res.sendStatus(404)
}
},
2019-09-11 19:12:24 +02:00
async unconfirm (req, res) {
2019-07-04 00:29:50 +02:00
const id = Number(req.params.event_id)
2019-04-03 00:25:12 +02:00
const event = await Event.findByPk(id)
2019-10-28 17:33:20 +01:00
if (!event) { return req.sendStatus(404) }
if (!req.user.is_admin && req.user.id !== event.userId) {
return res.sendStatus(403)
}
2019-04-03 00:25:12 +02:00
try {
2019-07-04 01:02:45 +02:00
event.is_visible = false
await event.save()
2019-04-03 00:25:12 +02:00
res.sendStatus(200)
} catch (e) {
res.sendStatus(404)
}
},
2019-11-06 11:31:56 +01:00
/** get all unconfirmed events */
2019-09-11 19:12:24 +02:00
async getUnconfirmed (req, res) {
2019-04-03 00:25:12 +02:00
const events = await Event.findAll({
where: {
is_visible: false
},
order: [['start_datetime', 'ASC']],
include: [Tag, Place]
})
res.json(events)
},
2019-09-11 19:12:24 +02:00
async addNotification (req, res) {
2019-04-03 00:25:12 +02:00
try {
const notification = {
filters: { is_visible: true },
email: req.body.email,
type: 'mail',
remove_code: crypto.randomBytes(16).toString('hex')
}
await Notification.create(notification)
res.sendStatus(200)
} catch (e) {
res.sendStatus(404)
}
},
2019-09-11 19:12:24 +02:00
async delNotification (req, res) {
2019-04-03 00:25:12 +02:00
const remove_code = req.params.code
try {
const notification = await Notification.findOne({ where: { remove_code: { [Op.eq]: remove_code } } })
await notification.destroy()
} catch (e) {
return res.sendStatus(404)
}
res.sendStatus(200)
},
2020-01-27 00:47:03 +01:00
// async addRecurrent (start, places, where_tags, limit) {
// const where = {
// is_visible: true,
// recurrent: { [Op.ne]: null }
// // placeId: places
// }
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// const events = await Event.findAll({
// where,
// limit,
// attributes: {
// exclude: ['slug', 'likes', 'boost', 'userId', 'is_visible', 'description', 'createdAt', 'updatedAt', 'placeId']
// },
// order: ['start_datetime', [Tag, 'weigth', 'DESC']],
// include: [
// { model: Resource, required: false, attributes: ['id'] },
// { model: Tag, ...where_tags, attributes: ['tag'], through: { attributes: [] } },
// { model: Place, required: false, attributes: ['id', 'name', 'address'] }
// ]
// })
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// let allEvents = []
// _.forEach(events, e => {
// allEvents = allEvents.concat(eventController.createEventsFromRecurrent(e.get(), start))
// })
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// return allEvents
// },
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// // build singular events from a recurrent pattern
// createEventsFromRecurrent (e, start, dueTo = null) {
// const events = []
// const recurrent = JSON.parse(e.recurrent)
// if (!recurrent.frequency) { return false }
// if (!dueTo) {
// dueTo = start.add(2, 'month')
// }
// let cursor = start.startOf('week')
// const start_date = moment.unix(e.start_datetime)
// const duration = moment.unix(e.end_datetime).diff(start_date, 's')
// const frequency = recurrent.frequency
// const days = recurrent.days
// const type = recurrent.type
// // default frequency is '1d' => each day
// const toAdd = { n: 1, unit: 'day' }
// // each week or 2 (search for the first specified day)
// if (frequency === '1w' || frequency === '2w') {
// cursor.add(days[0] - 1, 'day')
// if (frequency === '2w') {
// const nWeeks = cursor.diff(e.start_datetime, 'w') % 2
// if (!nWeeks) { cursor.add(1, 'week') }
// }
// toAdd.n = Number(frequency[0])
// toAdd.unit = 'week'
// // cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
// }
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// // each month or 2
// if (frequency === '1m' || frequency === '2m') {
// // find first match
// toAdd.n = 1
// toAdd.unit = 'month'
// if (type === 'weekday') {
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// } else if (type === 'ordinal') {
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// }
// }
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// // add event at specified frequency
// while (true) {
// const first_event_of_week = cursor.clone()
// days.forEach(d => {
// if (type === 'ordinal') {
// cursor.date(d)
// } else {
// cursor.day(d - 1)
// }
// if (cursor.isAfter(dueTo) || cursor.isBefore(start)) { return }
// e.start_datetime = cursor.unix()
// e.end_datetime = e.start_datetime + duration
// events.push(Object.assign({}, e))
// })
// if (cursor.isAfter(dueTo)) { break }
// cursor = first_event_of_week.add(toAdd.n, toAdd.unit)
// cursor.set('hour', start_date.hour()).set('minute', start_date.minutes())
// }
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
// return events
// },
2020-01-15 23:51:57 +01:00
2020-01-27 00:47:03 +01:00
async _select (start = moment.unix(), limit = 100, show_recurrent = true) {
2020-01-21 01:24:32 +01:00
const where = {
// confirmed event only
is_visible: true,
2020-01-27 00:47:03 +01:00
start_datetime: { [Op.gt]: start }
2020-01-15 23:51:57 +01:00
}
2020-01-27 00:47:03 +01:00
if (!show_recurrent) {
where.recurrent = null
2020-01-15 23:51:57 +01:00
}
2020-01-27 00:47:03 +01:00
const events = await Event.findAll({
2020-01-15 23:51:57 +01:00
where,
limit,
attributes: {
2020-01-21 01:24:32 +01:00
exclude: ['slug', 'likes', 'boost', 'userId', 'is_visible', 'description', 'createdAt', 'updatedAt', 'placeId']
2020-01-15 23:51:57 +01:00
// include: [[Sequelize.fn('COUNT', Sequelize.col('activitypub_id')), 'ressources']]
},
order: ['start_datetime', [Tag, 'weigth', 'DESC']],
include: [
{ model: Resource, required: false, attributes: ['id'] },
2020-01-27 00:47:03 +01:00
{ model: Tag, attributes: ['tag'], required: false, through: { attributes: [] } },
2019-05-30 12:12:51 +02:00
{ model: Place, required: false, attributes: ['id', 'name', 'address'] }
]
2019-04-03 00:25:12 +02:00
})
2019-07-11 23:31:37 +02:00
2020-01-27 00:47:03 +01:00
return _(events).map(e => {
e = e.get()
e.tags = e.tags ? e.tags.map(t => t && t.tag) : []
2020-01-21 01:24:32 +01:00
return e
2019-07-13 01:02:11 +02:00
})
2020-01-27 00:47:03 +01:00
},
2020-01-21 01:24:32 +01:00
2020-01-27 00:47:03 +01:00
/**
* Select events based on params
*/
async select (req, res) {
const start = req.query.start || moment().unix()
const limit = req.query.limit || 100
const show_recurrent = req.query.show_recurrent || true
res.json(await eventController._select(start, limit, show_recurrent))
// const filter_tags = req.query.tags || ''
// const filter_places = req.query.places || ''
// debug(`select limit:${limit} rec:${show_recurrent} tags:${filter_tags} places:${filter_places}`)
// let where_tags = {}
// const where = {
// // confirmed event only
// is_visible: true,
// start_datetime: { [Op.gt]: start },
// recurrent: null
// }
// if (filter_tags) {
// where_tags = { where: { tag: filter_tags.split(',') } }
// }
// if (filter_places) {
// where.placeId = filter_places.split(',')
// }
// let events = await Event.findAll({
// where,
// limit,
// attributes: {
// exclude: ['slug', 'likes', 'boost', 'userId', 'is_visible', 'description', 'createdAt', 'updatedAt', 'placeId']
// // include: [[Sequelize.fn('COUNT', Sequelize.col('activitypub_id')), 'ressources']]
// },
// order: ['start_datetime', [Tag, 'weigth', 'DESC']],
// include: [
// { model: Resource, required: false, attributes: ['id'] },
// { model: Tag, ...where_tags, attributes: ['tag'], through: { attributes: [] } },
// { model: Place, required: false, attributes: ['id', 'name', 'address'] }
// ]
// })
// let recurrentEvents = []
// events = _.map(events, e => e.get())
// if (show_recurrent) {
// recurrentEvents = await eventController.addRecurrent(moment.unix(start), where.placeId, where_tags, limit)
// events = _.concat(events, recurrentEvents)
// }
// // flat tags
// events = _(events).map(e => {
// e.tags = e.tags.map(t => t.tag)
// return e
// })
// res.json(events.sort((a, b) => a.start_datetime - b.start_datetime))
}
2020-01-21 01:24:32 +01:00
2019-04-03 00:25:12 +02:00
}
module.exports = eventController