GladysProject/Gladys

View on GitHub
server/models/index.js

Summary

Maintainability
A
0 mins
Test Coverage
const Sequelize = require('sequelize');
const duckdb = require('duckdb');
const Umzug = require('umzug');
const Promise = require('bluebird');
const chunk = require('lodash.chunk');
const path = require('path');
const util = require('util');
const getConfig = require('../utils/getConfig');
const logger = require('../utils/logger');
const { formatDateInUTC } = require('../utils/date');

const config = getConfig();

// SQLite
const sequelize = new Sequelize('mainDB', null, null, config);

// Migrations
const umzug = new Umzug({
  migrations: {
    path: path.join(__dirname, '../migrations'),
    params: [sequelize.getQueryInterface(), Sequelize],
  },
  logging: logger.debug,
  storage: 'sequelize',
  storageOptions: {
    sequelize,
  },
});

const AreaModel = require('./area');
const CalendarModel = require('./calendar');
const CalendarEventModel = require('./calendar_event');
const DashboardModel = require('./dashboard');
const DeviceFeatureStateModel = require('./device_feature_state');
const DeviceFeatureAggregateModel = require('./device_feature_state_aggregate');
const DeviceFeatureModel = require('./device_feature');
const DeviceParamModel = require('./device_param');
const DeviceModel = require('./device');
const HouseModel = require('./house');
const JobModel = require('./job');
const LifeEventModel = require('./life_event');
const LocationModel = require('./location');
const MessageModel = require('./message');
const PodModel = require('./pod');
const RoomModel = require('./room');
const SceneModel = require('./scene');
const ScriptModel = require('./script');
const ServiceModel = require('./service');
const SessionModel = require('./session');
const TagScene = require('./tag_scene');
const UserModel = require('./user');
const VariableModel = require('./variable');

const models = {
  Area: AreaModel(sequelize, Sequelize),
  Calendar: CalendarModel(sequelize, Sequelize),
  CalendarEvent: CalendarEventModel(sequelize, Sequelize),
  Dashboard: DashboardModel(sequelize, Sequelize),
  DeviceFeatureState: DeviceFeatureStateModel(sequelize, Sequelize),
  DeviceFeatureStateAggregate: DeviceFeatureAggregateModel(sequelize, Sequelize),
  DeviceFeature: DeviceFeatureModel(sequelize, Sequelize),
  DeviceParam: DeviceParamModel(sequelize, Sequelize),
  Device: DeviceModel(sequelize, Sequelize),
  House: HouseModel(sequelize, Sequelize),
  Job: JobModel(sequelize, Sequelize),
  LifeEvent: LifeEventModel(sequelize, Sequelize),
  Location: LocationModel(sequelize, Sequelize),
  Message: MessageModel(sequelize, Sequelize),
  Pod: PodModel(sequelize, Sequelize),
  Room: RoomModel(sequelize, Sequelize),
  Scene: SceneModel(sequelize, Sequelize),
  Script: ScriptModel(sequelize, Sequelize),
  Service: ServiceModel(sequelize, Sequelize),
  Session: SessionModel(sequelize, Sequelize),
  TagScene: TagScene(sequelize, Sequelize),
  User: UserModel(sequelize, Sequelize),
  Variable: VariableModel(sequelize, Sequelize),
};

// Associate all model
// Run `.associate` if it exists,
// ie create relationships in the ORM
Object.values(models)
  .filter((model) => typeof model.associate === 'function')
  .forEach((model) => model.associate(models));

// DuckDB
const duckDbFilePath = `${config.storage.replace('.db', '')}.duckdb`;
const duckDb = new duckdb.Database(duckDbFilePath);
const duckDbWriteConnection = duckDb.connect();
const duckDbReadConnection = duckDb.connect();
const duckDbWriteConnectionAllAsync = util.promisify(duckDbWriteConnection.all).bind(duckDbWriteConnection);
const duckDbReadConnectionAllAsync = util.promisify(duckDbReadConnection.all).bind(duckDbReadConnection);

const duckDbCreateTableIfNotExist = async () => {
  logger.info(`DuckDB - Creating database table if not exist`);
  await duckDbWriteConnectionAllAsync(`
    CREATE TABLE IF NOT EXISTS t_device_feature_state (
        device_feature_id UUID,
        value DOUBLE,
        created_at TIMESTAMPTZ
    );
  `);
};

const duckDbInsertState = async (deviceFeatureId, value, createdAt) => {
  const createdAtInString = formatDateInUTC(createdAt);
  await duckDbWriteConnectionAllAsync(
    'INSERT INTO t_device_feature_state VALUES (?, ?, ?)',
    deviceFeatureId,
    value,
    createdAtInString,
  );
};

const duckDbBatchInsertState = async (deviceFeatureId, states) => {
  const chunks = chunk(states, 10000);
  await Promise.each(chunks, async (oneStatesChunk, chunkIndex) => {
    let queryString = `INSERT INTO t_device_feature_state (device_feature_id, value, created_at) VALUES `;
    const queryParams = [];
    oneStatesChunk.forEach((state, index) => {
      if (index > 0) {
        queryString += `,`;
      }
      queryString += '(?, ?, ?)';
      queryParams.push(deviceFeatureId);
      queryParams.push(state.value);
      queryParams.push(formatDateInUTC(state.created_at));
    });
    logger.info(`DuckDB : Inserting chunk ${chunkIndex} for deviceFeature = ${deviceFeatureId}.`);
    await duckDbWriteConnectionAllAsync(queryString, ...queryParams);
  });
};

const db = {
  ...models,
  sequelize,
  umzug,
  duckDb,
  duckDbWriteConnectionAllAsync,
  duckDbReadConnectionAllAsync,
  duckDbCreateTableIfNotExist,
  duckDbInsertState,
  duckDbBatchInsertState,
};

module.exports = db;