From 3a698903f6bf01ec29ff23769533a4a372042c9d Mon Sep 17 00:00:00 2001 From: les Date: Tue, 21 Jan 2020 01:24:32 +0100 Subject: [PATCH] new events fetch methods --- server/api/controller/event.js | 322 ++++++++++++++++++--------------- store/index.js | 26 ++- 2 files changed, 197 insertions(+), 151 deletions(-) diff --git a/server/api/controller/event.js b/server/api/controller/event.js index 4e3a2316..e5782444 100644 --- a/server/api/controller/event.js +++ b/server/api/controller/event.js @@ -1,7 +1,7 @@ const crypto = require('crypto') const moment = require('moment-timezone') const { Op } = require('sequelize') -const lodash = require('lodash') +const _ = require('lodash') const { event: Event, resource: Resource, tag: Tag, place: Place, notification: Notification } = require('../models') const Sequelize = require('sequelize') const exportController = require('./export') @@ -30,7 +30,7 @@ const eventController = { order: [[Sequelize.literal('weigth'), 'DESC']], attributes: { include: [[Sequelize.fn('count', Sequelize.col('events.placeId')), 'weigth']], - exclude: ['weigth', 'createdAt', 'updatedAt'] + exclude: ['createdAt', 'updatedAt'] }, include: [{ model: Event, attributes: [] }], group: ['place.id'] @@ -40,11 +40,11 @@ const eventController = { raw: true, order: [['weigth', 'DESC']], attributes: { - exclude: ['createdAt', 'updatedAt', 'weigth'] + exclude: ['createdAt', 'updatedAt'] } }) - res.json({ tags: tags.map(t => t.tag), places }) + res.json({ tags, places }) }, async getNotifications (event, action) { @@ -59,7 +59,7 @@ const eventController = { if (!filters.tags && !filters.places) { return true } if (!filters.tags.length && !filters.places.length) { return true } if (filters.tags.length) { - const m = lodash.intersection(event.tags.map(t => t.tag), filters.tags) + const m = _.intersection(event.tags.map(t => t.tag), filters.tags) if (m.length > 0) { return true } } if (filters.places.length) { @@ -69,7 +69,7 @@ const eventController = { } } - const notifications = await Notification.findAll({ where: { action }, include: [ Event ] }) + const notifications = await Notification.findAll({ where: { action }, include: [Event] }) // get notification that matches with selected event const ret = notifications.filter(notification => match(event, notification.filters)) @@ -197,28 +197,121 @@ const eventController = { res.sendStatus(200) }, + async addRecurrent (start, places, where_tags, limit) { + const where = { + is_visible: true, + recurrent: { [Op.ne]: null } + // placeId: places + } + + 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'] } + ] + }) + + debug(`Found ${events.length} recurrent events`) + let allEvents = [] + _.forEach(events, e => { + allEvents = allEvents.concat(eventController.createEventsFromRecurrent(e.get(), start)) + }) + + debug(`Created ${allEvents.length} events`) + return allEvents + }, + + // 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 = moment.unix(start).add(2, 'month') + } + let cursor = moment.unix(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()) + } + + cursor.set('hour', start_date.hour()).set('minute', start_date.minutes()) + + // each month or 2 + if (frequency === '1m' || frequency === '2m') { + // find first match + toAdd.n = 1 + toAdd.unit = 'month' + if (type === 'weekday') { + + } else if (type === 'ordinal') { + + } + } + + // 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()) + } + + return events + }, + + /** + * Select events based on params + */ async select (req, res) { - const start = req.query.start || 0 - const limit = req.query.limit - const show_recurrent = req.query.show_recurrent || false + const start = req.query.start || moment().unix() + const limit = req.query.limit || 100 + const show_recurrent = req.query.show_recurrent || true const filter_tags = req.query.tags || '' const filter_places = req.query.places || '' - let where = {} + debug(`select limit:${limit} rec:${show_recurrent} tags:${filter_tags} places:${filter_places}`) let where_tags = {} - if (show_recurrent) { - where = { - [Op.or]: [ - { recurrent: { [Op.ne]: null } }, - { start_datetime: { [Op.gt]: start }} - ] - } - } else { - where = { - start_datetime: { [Op.gt]: start } - } + const where = { + // confirmed event only + is_visible: true, + start_datetime: { [Op.gt]: start }, + recurrent: null } - where.is_visible = true if (filter_tags) { where_tags = { where: { tag: filter_tags.split(',') } } @@ -232,139 +325,86 @@ const eventController = { where, limit, attributes: { - exclude: ['slug', 'likes', 'boost', 'userId', 'is_visible', 'description', 'createdAt', 'updatedAt', 'placeId'], + 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'] } - ], - }) - events = events.map(e => e.get()).map(e => { - e.tags = e.tags.map(t => t.tag) - return e - }) - - res.json(events) - }, - async getAll (req, res) { - // this is due how v-calendar shows dates - const start = moment() - .year(req.params.year) - .month(req.params.month) - .startOf('month') - .startOf('week') - - let end = moment() - .year(req.params.year) - .month(req.params.month) - .endOf('month') - - const shownDays = end.diff(start, 'days') - if (shownDays <= 35) { end = end.add(1, 'week') } - end = end.endOf('week') - - let events = await Event.findAll({ - where: { - // return only confirmed events - is_visible: true, - [Op.or]: [ - // return all recurrent events regardless start_datetime - { recurrent: { [Op.ne]: null } }, - - // and events in specified range - { start_datetime: { [Op.between]: [start.unix(), end.unix()] } } - ] - }, - attributes: { exclude: [ 'createdAt', 'updatedAt', 'placeId' ] }, - order: [[Tag, 'weigth', 'DESC']], - include: [ - { model: Resource, required: false, attributes: ['id'] }, - { model: Tag, required: false }, + { model: Tag, ...where_tags, attributes: ['tag'], through: { attributes: [] } }, { model: Place, required: false, attributes: ['id', 'name', 'address'] } ] }) - events = events.map(e => e.get()).map(e => { + + let recurrentEvents = [] + events = _.map(events, e => e.get()) + if (show_recurrent) { + recurrentEvents = await eventController.addRecurrent(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 }) - - // build singular events from a recurrent pattern - function createEventsFromRecurrent (e, dueTo = null) { - const events = [] - const recurrent = JSON.parse(e.recurrent) - if (!recurrent.frequency) { return false } - - let cursor = moment(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()) - } - - cursor.set('hour', start_date.hour()).set('minute', start_date.minutes()) - - // each month or 2 - if (frequency === '1m' || frequency === '2m') { - // find first match - toAdd.n = 1 - toAdd.unit = 'month' - if (type === 'weekday') { - - } else if (type === 'ordinal') { - - } - } - - // 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()) - } - - return events - } - - let allEvents = events.filter(e => !e.recurrent || e.recurrent.length === 0) - events.filter(e => e.recurrent && e.recurrent.length).forEach(e => { - const events = createEventsFromRecurrent(e, end) - if (events) { allEvents = allEvents.concat(events) } - }) - // allEvents.sort((a,b) => a.start_datetime-b.start_datetime) - res.json(allEvents.sort((a, b) => a.start_datetime - b.start_datetime)) + res.json(events.sort((a, b) => a.start_datetime - b.start_datetime)) + // res.json(recurrentEvents) } + // async getAll (req, res) { + // // this is due how v-calendar shows dates + // const start = moment() + // .year(req.params.year) + // .month(req.params.month) + // .startOf('month') + // .startOf('week') + + // let end = moment() + // .year(req.params.year) + // .month(req.params.month) + // .endOf('month') + + // const shownDays = end.diff(start, 'days') + // if (shownDays <= 35) { end = end.add(1, 'week') } + // end = end.endOf('week') + + // let events = await Event.findAll({ + // where: { + // // return only confirmed events + // is_visible: true, + // [Op.or]: [ + // // return all recurrent events regardless start_datetime + // { recurrent: { [Op.ne]: null } }, + + // // and events in specified range + // { start_datetime: { [Op.between]: [start.unix(), end.unix()] } } + // ] + // }, + // attributes: { exclude: ['createdAt', 'updatedAt', 'placeId'] }, + // order: [[Tag, 'weigth', 'DESC']], + // include: [ + // { model: Resource, required: false, attributes: ['id'] }, + // { model: Tag, required: false }, + // { model: Place, required: false, attributes: ['id', 'name', 'address'] } + // ] + // }) + // events = events.map(e => e.get()).map(e => { + // e.tags = e.tags.map(t => t.tag) + // return e + // }) + + // let allEvents = events.filter(e => !e.recurrent || e.recurrent.length === 0) + // events.filter(e => e.recurrent && e.recurrent.length).forEach(e => { + // const events = createEventsFromRecurrent(e, end) + // if (events) { allEvents = allEvents.concat(events) } + // }) + + // // allEvents.sort((a,b) => a.start_datetime-b.start_datetime) + // res.json(allEvents.sort((a, b) => a.start_datetime - b.start_datetime)) + // } + } module.exports = eventController diff --git a/store/index.js b/store/index.js index b83c542e..dab05af0 100644 --- a/store/index.js +++ b/store/index.js @@ -36,6 +36,9 @@ export const getters = { const search_for_tags = !!state.filters.tags.length const search_for_places = !!state.filters.places.length + const search_place_ids = state.filters.places.map(p => p.id) + const search_tags_tags = state.filters.tags.map(t => t.id) + return state.events.filter(e => { // filter past events if (!state.filters.show_past_events && e.past) { return false } @@ -44,13 +47,13 @@ export const getters = { if (!state.filters.show_recurrent_events && e.recurrent) { return false } if (search_for_places) { - if (find(state.filters.places, p => p.id === e.place.id)) { + if (search_place_ids.includes(e.place.id)) { return true } } if (search_for_tags) { - const common_tags = intersection(e.tags, state.filters.tags) + const common_tags = intersection(e.tags, search_tags_tags) if (common_tags.length > 0) { return true } } @@ -158,7 +161,11 @@ export const actions = { commit('setSettings', settings) const start_datetime = moment().startOf('month').startOf('week').unix() - const events = await this.$axios.$get(`/event?start=${start_datetime}`) + let query = `start=${start_datetime}` + if (settings.recurrent_event_visible) { + query += '&show_recurrent' + } + const events = await this.$axios.$get(`/event?${query}`) commit('setEvents', events) const { tags, places } = await this.$axios.$get('/event/meta') @@ -167,16 +174,15 @@ export const actions = { // apply settings commit('showRecurrentEvents', settings.allow_recurrent_event && settings.recurrent_event_visible) }, - async updateEvents ({ commit, state }, page) { - const month = moment().month() - const year = moment().year() - commit('setPast', page.year < year || (page.year === year && page.month <= month)) - // const events = await this.$axios.$get(`/event/${page.month - 1}/${page.year}`) - const start_datetime = moment().year(page.year).month(page.month - 1).unix() + async updateEvents ({ commit }, page) { + const [month, year] = [moment().month(), moment().year()] + const in_past = page.year < year || (page.year === year && page.month <= month) + // commit('setPast', in_past) + const start_datetime = moment().year(page.year).month(page.month - 1).startOf('month').startOf('week').unix() const query = `start=${start_datetime}` - const events = await this.$axios.$get(`/event?${query}`) commit('setEvents', events) + commit('showPastEvents', in_past) }, async updateMeta ({ commit }) { const { tags, places } = await this.$axios.$get('/event/meta')