From 74889a71fe687dda74f2a687653122327807af36 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 16 Jun 2017 09:45:46 +0200 Subject: Reorganize model files --- server/models/request/index.ts | 4 + server/models/request/request-interface.ts | 47 ++++++ server/models/request/request-to-pod-interface.ts | 21 +++ server/models/request/request-to-pod.ts | 54 ++++++ .../request/request-video-event-interface.ts | 48 ++++++ server/models/request/request-video-event.ts | 185 +++++++++++++++++++++ .../models/request/request-video-qadu-interface.ts | 46 +++++ server/models/request/request-video-qadu.ts | 164 ++++++++++++++++++ server/models/request/request.ts | 150 +++++++++++++++++ 9 files changed, 719 insertions(+) create mode 100644 server/models/request/index.ts create mode 100644 server/models/request/request-interface.ts create mode 100644 server/models/request/request-to-pod-interface.ts create mode 100644 server/models/request/request-to-pod.ts create mode 100644 server/models/request/request-video-event-interface.ts create mode 100644 server/models/request/request-video-event.ts create mode 100644 server/models/request/request-video-qadu-interface.ts create mode 100644 server/models/request/request-video-qadu.ts create mode 100644 server/models/request/request.ts (limited to 'server/models/request') diff --git a/server/models/request/index.ts b/server/models/request/index.ts new file mode 100644 index 000000000..824c140f5 --- /dev/null +++ b/server/models/request/index.ts @@ -0,0 +1,4 @@ +export * from './request-interface' +export * from './request-to-pod-interface' +export * from './request-video-event-interface' +export * from './request-video-qadu-interface' diff --git a/server/models/request/request-interface.ts b/server/models/request/request-interface.ts new file mode 100644 index 000000000..70fd734e1 --- /dev/null +++ b/server/models/request/request-interface.ts @@ -0,0 +1,47 @@ +import * as Sequelize from 'sequelize' + +import { PodInstance, PodAttributes } from '../pod' + +export type RequestsGrouped = { + [ podId: number ]: { + request: RequestInstance, + pod: PodInstance + }[] +} + +export namespace RequestMethods { + export type CountTotalRequestsCallback = (err: Error, total: number) => void + export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void + + export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsGrouped) => void + export type ListWithLimitAndRandom = (limitPods, limitRequestsPerPod, callback: ListWithLimitAndRandomCallback) => void + + export type RemoveWithEmptyToCallback = (err: Error) => void + export type RemoveWithEmptyTo = (callback: RemoveWithEmptyToCallback) => void + + export type RemoveAllCallback = (err: Error) => void + export type RemoveAll = (callback: RemoveAllCallback) => void +} + +export interface RequestClass { + countTotalRequests: RequestMethods.CountTotalRequests + listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom + removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo + removeAll: RequestMethods.RemoveAll +} + +export interface RequestAttributes { + request: object + endpoint: string +} + +export interface RequestInstance extends RequestClass, RequestAttributes, Sequelize.Instance { + id: number + createdAt: Date + updatedAt: Date + + setPods: Sequelize.HasManySetAssociationsMixin + Pods: PodInstance[] +} + +export interface RequestModel extends RequestClass, Sequelize.Model {} diff --git a/server/models/request/request-to-pod-interface.ts b/server/models/request/request-to-pod-interface.ts new file mode 100644 index 000000000..6d75ca6e5 --- /dev/null +++ b/server/models/request/request-to-pod-interface.ts @@ -0,0 +1,21 @@ +import * as Sequelize from 'sequelize' + +export namespace RequestToPodMethods { + export type RemoveByRequestIdsAndPodCallback = (err: Error) => void + export type RemoveByRequestIdsAndPod = (requestsIds: number[], podId: number, callback?: RemoveByRequestIdsAndPodCallback) => void +} + +export interface RequestToPodClass { + removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod +} + +export interface RequestToPodAttributes { +} + +export interface RequestToPodInstance extends RequestToPodClass, RequestToPodAttributes, Sequelize.Instance { + id: number + createdAt: Date + updatedAt: Date +} + +export interface RequestToPodModel extends RequestToPodClass, Sequelize.Model {} diff --git a/server/models/request/request-to-pod.ts b/server/models/request/request-to-pod.ts new file mode 100644 index 000000000..67331be1d --- /dev/null +++ b/server/models/request/request-to-pod.ts @@ -0,0 +1,54 @@ +import * as Sequelize from 'sequelize' + +import { addMethodsToModel } from '../utils' +import { + RequestToPodClass, + RequestToPodInstance, + RequestToPodAttributes, + + RequestToPodMethods +} from './request-to-pod-interface' + +let RequestToPod: Sequelize.Model +let removeByRequestIdsAndPod: RequestToPodMethods.RemoveByRequestIdsAndPod + +export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { + RequestToPod = sequelize.define('RequestToPod', {}, { + indexes: [ + { + fields: [ 'requestId' ] + }, + { + fields: [ 'podId' ] + }, + { + fields: [ 'requestId', 'podId' ], + unique: true + } + ] + }) + + const classMethods = [ + removeByRequestIdsAndPod + ] + addMethodsToModel(RequestToPod, classMethods) + + return RequestToPod +} + +// --------------------------------------------------------------------------- + +removeByRequestIdsAndPod = function (requestsIds: number[], podId: number, callback?: RequestToPodMethods.RemoveByRequestIdsAndPodCallback) { + if (!callback) callback = function () { /* empty */ } + + const query = { + where: { + requestId: { + $in: requestsIds + }, + podId: podId + } + } + + RequestToPod.destroy(query).asCallback(callback) +} diff --git a/server/models/request/request-video-event-interface.ts b/server/models/request/request-video-event-interface.ts new file mode 100644 index 000000000..219d8edc0 --- /dev/null +++ b/server/models/request/request-video-event-interface.ts @@ -0,0 +1,48 @@ +import * as Sequelize from 'sequelize' + +import { VideoInstance } from '../video' +import { PodInstance } from '../pod' + +export type RequestsVideoEventGrouped = { + [ podId: number ]: { + id: number + type: string + count: number + video: VideoInstance + pod: PodInstance + }[] +} + +export namespace RequestVideoEventMethods { + export type CountTotalRequestsCallback = (err: Error, total: number) => void + export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void + + export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoEventGrouped) => void + export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void + + export type RemoveByRequestIdsAndPodCallback = () => void + export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void + + export type RemoveAllCallback = () => void + export type RemoveAll = (callback: RemoveAllCallback) => void +} + +export interface RequestVideoEventClass { + countTotalRequests: RequestVideoEventMethods.CountTotalRequests + listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom + removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod + removeAll: RequestVideoEventMethods.RemoveAll +} + +export interface RequestVideoEventAttributes { + type: string + count: number +} + +export interface RequestVideoEventInstance extends RequestVideoEventClass, RequestVideoEventAttributes, Sequelize.Instance { + id: number + + Video: VideoInstance +} + +export interface RequestVideoEventModel extends RequestVideoEventClass, Sequelize.Model {} diff --git a/server/models/request/request-video-event.ts b/server/models/request/request-video-event.ts new file mode 100644 index 000000000..f552ef50b --- /dev/null +++ b/server/models/request/request-video-event.ts @@ -0,0 +1,185 @@ +/* + Request Video events (likes, dislikes, views...) +*/ + +import { values } from 'lodash' +import * as Sequelize from 'sequelize' + +import { database as db } from '../../initializers/database' +import { REQUEST_VIDEO_EVENT_TYPES } from '../../initializers' +import { isVideoEventCountValid } from '../../helpers' +import { addMethodsToModel } from '../utils' +import { + RequestVideoEventClass, + RequestVideoEventInstance, + RequestVideoEventAttributes, + + RequestVideoEventMethods, + RequestsVideoEventGrouped +} from './request-video-event-interface' + +let RequestVideoEvent: Sequelize.Model +let countTotalRequests: RequestVideoEventMethods.CountTotalRequests +let listWithLimitAndRandom: RequestVideoEventMethods.ListWithLimitAndRandom +let removeByRequestIdsAndPod: RequestVideoEventMethods.RemoveByRequestIdsAndPod +let removeAll: RequestVideoEventMethods.RemoveAll + +export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { + RequestVideoEvent = sequelize.define('RequestVideoEvent', + { + type: { + type: DataTypes.ENUM(values(REQUEST_VIDEO_EVENT_TYPES)), + allowNull: false + }, + count: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + countValid: function (value) { + const res = isVideoEventCountValid(value) + if (res === false) throw new Error('Video event count is not valid.') + } + } + } + }, + { + updatedAt: false, + indexes: [ + { + fields: [ 'videoId' ] + } + ] + } + ) + + const classMethods = [ + associate, + + listWithLimitAndRandom, + countTotalRequests, + removeAll, + removeByRequestIdsAndPod + ] + addMethodsToModel(RequestVideoEvent, classMethods) + + return RequestVideoEvent +} + +// ------------------------------ STATICS ------------------------------ + +function associate (models) { + RequestVideoEvent.belongsTo(models.Video, { + foreignKey: { + name: 'videoId', + allowNull: false + }, + onDelete: 'CASCADE' + }) +} + +countTotalRequests = function (callback: RequestVideoEventMethods.CountTotalRequestsCallback) { + const query = {} + return RequestVideoEvent.count(query).asCallback(callback) +} + +listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoEventMethods.ListWithLimitAndRandomCallback) { + const Pod = db.Pod + + // We make a join between videos and authors to find the podId of our video event requests + const podJoins = 'INNER JOIN "Videos" ON "Videos"."authorId" = "Authors"."id" ' + + 'INNER JOIN "RequestVideoEvents" ON "RequestVideoEvents"."videoId" = "Videos"."id"' + + Pod.listRandomPodIdsWithRequest(limitPods, 'Authors', podJoins, function (err, podIds) { + if (err) return callback(err) + + // We don't have friends that have requests + if (podIds.length === 0) return callback(null, []) + + const query = { + order: [ + [ 'id', 'ASC' ] + ], + include: [ + { + model: RequestVideoEvent['sequelize'].models.Video, + include: [ + { + model: RequestVideoEvent['sequelize'].models.Author, + include: [ + { + model: RequestVideoEvent['sequelize'].models.Pod, + where: { + id: { + $in: podIds + } + } + } + ] + } + ] + } + ] + } + + RequestVideoEvent.findAll(query).asCallback(function (err, requests) { + if (err) return callback(err) + + const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) + return callback(err, requestsGrouped) + }) + }) +} + +removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoEventMethods.RemoveByRequestIdsAndPodCallback) { + const query = { + where: { + id: { + $in: ids + } + }, + include: [ + { + model: RequestVideoEvent['sequelize'].models.Video, + include: [ + { + model: RequestVideoEvent['sequelize'].models.Author, + where: { + podId + } + } + ] + } + ] + } + + RequestVideoEvent.destroy(query).asCallback(callback) +} + +removeAll = function (callback: RequestVideoEventMethods.RemoveAllCallback) { + // Delete all requests + RequestVideoEvent.truncate({ cascade: true }).asCallback(callback) +} + +// --------------------------------------------------------------------------- + +function groupAndTruncateRequests (events: RequestVideoEventInstance[], limitRequestsPerPod: number) { + const eventsGrouped: RequestsVideoEventGrouped = {} + + events.forEach(function (event) { + const pod = event.Video.Author.Pod + + if (!eventsGrouped[pod.id]) eventsGrouped[pod.id] = [] + + if (eventsGrouped[pod.id].length < limitRequestsPerPod) { + eventsGrouped[pod.id].push({ + id: event.id, + type: event.type, + count: event.count, + video: event.Video, + pod + }) + } + }) + + return eventsGrouped +} diff --git a/server/models/request/request-video-qadu-interface.ts b/server/models/request/request-video-qadu-interface.ts new file mode 100644 index 000000000..625b063dc --- /dev/null +++ b/server/models/request/request-video-qadu-interface.ts @@ -0,0 +1,46 @@ +import * as Sequelize from 'sequelize' + +import { VideoInstance } from '../video' +import { PodInstance } from '../pod' + +export type RequestsVideoQaduGrouped = { + [ podId: number ]: { + request: RequestVideoQaduInstance + video: VideoInstance + pod: PodInstance + } +} + +export namespace RequestVideoQaduMethods { + export type CountTotalRequestsCallback = (err: Error, total: number) => void + export type CountTotalRequests = (callback: CountTotalRequestsCallback) => void + + export type ListWithLimitAndRandomCallback = (err: Error, requestsGrouped?: RequestsVideoQaduGrouped) => void + export type ListWithLimitAndRandom = (limitPods: number, limitRequestsPerPod: number, callback: ListWithLimitAndRandomCallback) => void + + export type RemoveByRequestIdsAndPodCallback = () => void + export type RemoveByRequestIdsAndPod = (ids: number[], podId: number, callback: RemoveByRequestIdsAndPodCallback) => void + + export type RemoveAllCallback = () => void + export type RemoveAll = (callback: RemoveAllCallback) => void +} + +export interface RequestVideoQaduClass { + countTotalRequests: RequestVideoQaduMethods.CountTotalRequests + listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom + removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod + removeAll: RequestVideoQaduMethods.RemoveAll +} + +export interface RequestVideoQaduAttributes { + type: string +} + +export interface RequestVideoQaduInstance extends RequestVideoQaduClass, RequestVideoQaduAttributes, Sequelize.Instance { + id: number + + Pod: PodInstance + Video: VideoInstance +} + +export interface RequestVideoQaduModel extends RequestVideoQaduClass, Sequelize.Model {} diff --git a/server/models/request/request-video-qadu.ts b/server/models/request/request-video-qadu.ts new file mode 100644 index 000000000..da62239f5 --- /dev/null +++ b/server/models/request/request-video-qadu.ts @@ -0,0 +1,164 @@ +/* + Request Video for Quick And Dirty Updates like: + - views + - likes + - dislikes + + We can't put it in the same system than basic requests for efficiency. + Moreover we don't want to slow down the basic requests with a lot of views/likes/dislikes requests. + So we put it an independant request scheduler. +*/ + +import { values } from 'lodash' +import * as Sequelize from 'sequelize' + +import { database as db } from '../../initializers/database' +import { REQUEST_VIDEO_QADU_TYPES } from '../../initializers' +import { addMethodsToModel } from '../utils' +import { + RequestVideoQaduClass, + RequestVideoQaduInstance, + RequestVideoQaduAttributes, + + RequestVideoQaduMethods +} from './request-video-qadu-interface' + +let RequestVideoQadu: Sequelize.Model +let countTotalRequests: RequestVideoQaduMethods.CountTotalRequests +let listWithLimitAndRandom: RequestVideoQaduMethods.ListWithLimitAndRandom +let removeByRequestIdsAndPod: RequestVideoQaduMethods.RemoveByRequestIdsAndPod +let removeAll: RequestVideoQaduMethods.RemoveAll + +export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { + RequestVideoQadu = sequelize.define('RequestVideoQadu', + { + type: { + type: DataTypes.ENUM(values(REQUEST_VIDEO_QADU_TYPES)), + allowNull: false + } + }, + { + timestamps: false, + indexes: [ + { + fields: [ 'podId' ] + }, + { + fields: [ 'videoId' ] + } + ] + } + ) + + const classMethods = [ + associate, + + listWithLimitAndRandom, + countTotalRequests, + removeAll, + removeByRequestIdsAndPod + ] + addMethodsToModel(RequestVideoQadu, classMethods) + + return RequestVideoQadu +} + +// ------------------------------ STATICS ------------------------------ + +function associate (models) { + RequestVideoQadu.belongsTo(models.Pod, { + foreignKey: { + name: 'podId', + allowNull: false + }, + onDelete: 'CASCADE' + }) + + RequestVideoQadu.belongsTo(models.Video, { + foreignKey: { + name: 'videoId', + allowNull: false + }, + onDelete: 'CASCADE' + }) +} + +countTotalRequests = function (callback: RequestVideoQaduMethods.CountTotalRequestsCallback) { + const query = {} + return RequestVideoQadu.count(query).asCallback(callback) +} + +listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestVideoQaduMethods.ListWithLimitAndRandomCallback) { + const Pod = db.Pod + const tableJoin = '' + + Pod.listRandomPodIdsWithRequest(limitPods, 'RequestVideoQadus', tableJoin, function (err, podIds) { + if (err) return callback(err) + + // We don't have friends that have requests + if (podIds.length === 0) return callback(null, []) + + const query = { + include: [ + { + model: RequestVideoQadu['sequelize'].models.Pod, + where: { + id: { + $in: podIds + } + } + }, + { + model: RequestVideoQadu['sequelize'].models.Video + } + ] + } + + RequestVideoQadu.findAll(query).asCallback(function (err, requests) { + if (err) return callback(err) + + const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) + return callback(err, requestsGrouped) + }) + }) +} + +removeByRequestIdsAndPod = function (ids: number[], podId: number, callback: RequestVideoQaduMethods.RemoveByRequestIdsAndPodCallback) { + const query = { + where: { + id: { + $in: ids + }, + podId + } + } + + RequestVideoQadu.destroy(query).asCallback(callback) +} + +removeAll = function (callback: RequestVideoQaduMethods.RemoveAllCallback) { + // Delete all requests + RequestVideoQadu.truncate({ cascade: true }).asCallback(callback) +} + +// --------------------------------------------------------------------------- + +function groupAndTruncateRequests (requests: RequestVideoQaduInstance[], limitRequestsPerPod: number) { + const requestsGrouped = {} + + requests.forEach(function (request) { + const pod = request.Pod + + if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = [] + + if (requestsGrouped[pod.id].length < limitRequestsPerPod) { + requestsGrouped[pod.id].push({ + request: request, + video: request.Video, + pod + }) + } + }) + + return requestsGrouped +} diff --git a/server/models/request/request.ts b/server/models/request/request.ts new file mode 100644 index 000000000..66e7da845 --- /dev/null +++ b/server/models/request/request.ts @@ -0,0 +1,150 @@ +import { values } from 'lodash' +import * as Sequelize from 'sequelize' + +import { database as db } from '../../initializers/database' +import { REQUEST_ENDPOINTS } from '../../initializers' +import { addMethodsToModel } from '../utils' +import { + RequestClass, + RequestInstance, + RequestAttributes, + + RequestMethods, + RequestsGrouped +} from './request-interface' + +let Request: Sequelize.Model +let countTotalRequests: RequestMethods.CountTotalRequests +let listWithLimitAndRandom: RequestMethods.ListWithLimitAndRandom +let removeWithEmptyTo: RequestMethods.RemoveWithEmptyTo +let removeAll: RequestMethods.RemoveAll + +export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { + Request = sequelize.define('Request', + { + request: { + type: DataTypes.JSON, + allowNull: false + }, + endpoint: { + type: DataTypes.ENUM(values(REQUEST_ENDPOINTS)), + allowNull: false + } + } + ) + + const classMethods = [ + associate, + + listWithLimitAndRandom, + + countTotalRequests, + removeAll, + removeWithEmptyTo + ] + addMethodsToModel(Request, classMethods) + + return Request +} + +// ------------------------------ STATICS ------------------------------ + +function associate (models) { + Request.belongsToMany(models.Pod, { + foreignKey: { + name: 'requestId', + allowNull: false + }, + through: models.RequestToPod, + onDelete: 'CASCADE' + }) +} + +countTotalRequests = function (callback: RequestMethods.CountTotalRequestsCallback) { + // We need to include Pod because there are no cascade delete when a pod is removed + // So we could count requests that do not have existing pod anymore + const query = { + include: [ Request['sequelize'].models.Pod ] + } + + return Request.count(query).asCallback(callback) +} + +listWithLimitAndRandom = function (limitPods: number, limitRequestsPerPod: number, callback: RequestMethods.ListWithLimitAndRandomCallback) { + const Pod = db.Pod + const tableJoin = '' + + Pod.listRandomPodIdsWithRequest(limitPods, 'RequestToPods', '', function (err, podIds) { + if (err) return callback(err) + + // We don't have friends that have requests + if (podIds.length === 0) return callback(null, []) + + // The first x requests of these pods + // It is very important to sort by id ASC to keep the requests order! + const query = { + order: [ + [ 'id', 'ASC' ] + ], + include: [ + { + model: Request['sequelize'].models.Pod, + where: { + id: { + $in: podIds + } + } + } + ] + } + + Request.findAll(query).asCallback(function (err, requests) { + if (err) return callback(err) + + const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod) + return callback(err, requestsGrouped) + }) + }) +} + +removeAll = function (callback: RequestMethods.RemoveAllCallback) { + // Delete all requests + Request.truncate({ cascade: true }).asCallback(callback) +} + +removeWithEmptyTo = function (callback?: RequestMethods.RemoveWithEmptyToCallback) { + if (!callback) callback = function () { /* empty */ } + + const query = { + where: { + id: { + $notIn: [ + Sequelize.literal('SELECT "requestId" FROM "RequestToPods"') + ] + } + } + } + + Request.destroy(query).asCallback(callback) +} + +// --------------------------------------------------------------------------- + +function groupAndTruncateRequests (requests: RequestInstance[], limitRequestsPerPod: number) { + const requestsGrouped: RequestsGrouped = {} + + requests.forEach(function (request) { + request.Pods.forEach(function (pod) { + if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = [] + + if (requestsGrouped[pod.id].length < limitRequestsPerPod) { + requestsGrouped[pod.id].push({ + request, + pod + }) + } + }) + }) + + return requestsGrouped +} -- cgit v1.2.3