Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import/Export functions #54

Merged
merged 14 commits into from
Oct 17, 2021
42 changes: 42 additions & 0 deletions app/Controllers/ExportController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const { getAllGroupVocabulary, getAllLanguagePackageVocabulary } = require('../Services/ExportServiceProvider.js');
const catchAsync = require('../utils/catchAsync');
const { getVersion } = require('../Services/InfoServiceProvider');

const exportGroup = catchAsync(async (req, res) => {
// get userId from request
const userId = req.user.id;

// get language package id from params
const { groupId } = req.params;

const group = await getAllGroupVocabulary(userId, groupId);
const formatted = {
version: getVersion(),
type: 'vocascan/group',
...group.toJSON(),
};
res.send(formatted);
});

const exportLanguagePackage = catchAsync(async (req, res) => {
// get userId from request
const userId = req.user.id;

// get language package id from params
const { languagePackageId } = req.params;
const queryStatus = (req.query.queryStatus || 'false') === 'true';

const languagePackage = await getAllLanguagePackageVocabulary({ userId, languagePackageId, queryStatus });
const formatted = {
version: getVersion(),
type: 'vocascan/package',
...languagePackage.toJSON(),
};

res.send(formatted);
});

module.exports = {
exportGroup,
exportLanguagePackage,
};
32 changes: 32 additions & 0 deletions app/Controllers/ImportController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { storeGroupVocabulary, storeLanguagePackageVocabulary } = require('../Services/ImportServiceProvider.js');
const catchAsync = require('../utils/catchAsync');
const ApiError = require('../utils/ApiError.js');
const httpStatus = require('http-status');

const importVocabs = catchAsync(async (req, res) => {
// get userId from request
const userId = req.user.id;

const active = (req.query.active || 'true') === 'true';
const activate = (req.query.activate || 'false') === 'true';

const { type } = req.body;
// use different types of import to separate the functions
if (!type) {
throw new ApiError(httpStatus.BAD_REQUEST, 'imported data has no Vocascan structure');
} else if (type === 'vocascan/package') {
const queryStatus = (req.query.queryStatus || 'false') === 'true';
await storeLanguagePackageVocabulary(req.body, userId, active, activate, queryStatus);
} else if (type === 'vocascan/group') {
const { languagePackageId } = req.query;
await storeGroupVocabulary(req.body, userId, languagePackageId, active, activate);
} else {
throw new ApiError(httpStatus.BAD_REQUEST, 'imported data type not recognized');
}

res.status(204).end();
});

module.exports = {
importVocabs,
};
12 changes: 10 additions & 2 deletions app/Controllers/VocabularyController.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ const addVocabularyCard = catchAsync(async (req, res) => {
// get userId from request
const userId = req.user.id;
const { name, description, active, translations } = req.body;
const { languagePackageId } = req.params;
const { languagePackageId, groupId } = req.params;

// check if user wants to train vocabulary card directly
const activate = req.query.activate === 'true';

// create vocabulary card
const vocabularyCard = await createVocabularyCard(req.params, name, description, userId, active, activate);
const vocabularyCard = await createVocabularyCard({
languagePackageId,
groupId,
name,
description,
userId,
active,
activate,
});

// parse vocabulary card id from response and create translations
await createTranslations(translations, userId, languagePackageId, vocabularyCard.id);
Expand Down
16 changes: 11 additions & 5 deletions app/Services/DrawerServiceProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ async function createDrawer(languagePackageId, stage, queryInterval, userId) {
return drawer;
}

async function createDrawers(drawers, languagePackageId, userId) {
await Promise.all(
drawers.map(async (drawer) => {
await createDrawer(languagePackageId, drawer.stage, drawer.queryInterval, userId);
})
async function createDrawers(drawers, languagePackageId, userId, transaction) {
const createdDrawers = await Drawer.bulkCreate(
drawers.map((drawer) => ({
userId,
languagePackageId,
stage: drawer.stage,
queryInterval: drawer.queryInterval,
})),
{ transaction }
);

return createdDrawers;
}

module.exports = {
Expand Down
85 changes: 85 additions & 0 deletions app/Services/ExportServiceProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
const { VocabularyCard, Translation, Group, LanguagePackage, Drawer } = require('../../database');

// get every vocabulary of a group
async function getAllGroupVocabulary(userId, groupId) {
const group = await Group.findOne({
attributes: ['name', 'description'],
include: [
{
model: VocabularyCard,
attributes: ['name', 'description'],
include: [
{
model: Translation,
attributes: ['name'],
},
],
},
],
where: {
id: groupId,
userId,
},
});
return group;
}

async function getAllLanguagePackageVocabulary({ userId, languagePackageId, queryStatus }) {
// Get user with email from database
const languagePackage = await LanguagePackage.findOne({
// if groups is true, return groups to every language package
include: queryStatus
? [
{
model: Group,
attributes: ['name', 'description'],
include: [
{
model: VocabularyCard,
attributes: queryStatus ? ['name', 'description', 'drawerId'] : ['name', 'description'],
include: [
{
model: Translation,
attributes: ['name'],
},
],
},
],
},
{
model: Drawer,
attributes: ['id', 'stage', 'queryInterval'],
},
]
: [
{
model: Group,
attributes: ['name', 'description'],
include: [
{
model: VocabularyCard,
attributes: queryStatus ? ['name', 'description', 'drawerId'] : ['name', 'description'],
include: [
{
model: Translation,
attributes: ['name'],
},
],
},
],
},
],
attributes: ['name', 'foreignWordLanguage', 'translatedWordLanguage', 'vocabsPerDay', 'rightWords'],
where: {
userId,
id: languagePackageId,
},
});

return languagePackage;
}

module.exports = {
getAllGroupVocabulary,
getAllLanguagePackageVocabulary,
};
173 changes: 173 additions & 0 deletions app/Services/ImportServiceProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
const { Group, VocabularyCard, Translation, Drawer, sequelize } = require('../../database');
const ApiError = require('../utils/ApiError.js');
const httpStatus = require('http-status');
const { createDrawers } = require('./DrawerServiceProvider.js');
const { createLanguagePackage } = require('./LanguagePackageServiceProvider.js');
const { drawers } = require('../utils/constants.js');

async function storeGroupVocabulary(
{ name, description, VocabularyCards },
userId,
languagePackageId,
active,
activate
) {
const transaction = await sequelize.transaction();
noctera marked this conversation as resolved.
Show resolved Hide resolved

try {
const group = await Group.create(
noctera marked this conversation as resolved.
Show resolved Hide resolved
{
userId,
languagePackageId,
name,
description,
active,
},
{ transaction }
);

const drawer = await Drawer.findOne({
where: {
userId,
languagePackageId,
stage: activate ? 1 : 0,
},
transaction,
});

const yesterday = new Date().setDate(new Date().getDate() - 1);

await Promise.all(
VocabularyCards.map(async (vocabularyCard) => {
await VocabularyCard.create(
{
userId,
languagePackageId,
groupId: group.id,
drawerId: drawer.id,
name: vocabularyCard.name,
description: vocabularyCard.description,
lastQuery: yesterday,
lastQueryCorrect: yesterday,
active,
Translations: vocabularyCard.Translations.map((translation) => ({
name: translation.name,
userId,
languagePackageId,
})),
},
{
transaction,
include: [
{
model: Translation,
},
],
}
);
})
);

await transaction.commit();
} catch (error) {
await transaction.rollback();
throw new ApiError(httpStatus.BAD_REQUEST, 'Error importing vocabs');
}
}

async function storeLanguagePackageVocabulary(
{ name, foreignWordLanguage, translatedWordLanguage, vocabsPerDay, rightWords, Groups, Drawers },
userId,
active,
activate,
queryStatus
) {
if (!Drawers && queryStatus) {
throw new ApiError(httpStatus.BAD_REQUEST, 'No exported drawers found');
}

const transaction = await sequelize.transaction();

try {
// create language Package
const createdLanguagePackage = await createLanguagePackage(
{ name, foreignWordLanguage, translatedWordLanguage, vocabsPerDay, rightWords },
userId,
transaction
);

let getDrawer = () => null;
const yesterday = new Date().setDate(new Date().getDate() - 1);

// store drawers for language package in database
if (queryStatus) {
const createdDrawers = await createDrawers(Drawers, createdLanguagePackage.id, userId, transaction);
const drawerMap = Drawers.reduce(
(_drawerMap, oldDrawer, index) => ({
..._drawerMap,
[oldDrawer.id]: createdDrawers[index],
}),
{}
);

getDrawer = (oldId) => drawerMap[oldId];
} else {
const createdDrawers = await createDrawers(drawers, createdLanguagePackage.id, userId, transaction);
const drawer = createdDrawers.find((_drawer) => +_drawer.stage === (activate ? 1 : 0));

getDrawer = () => drawer;
}

await Promise.all(
Groups.map(async (group) => {
await Group.create(
{
userId,
languagePackageId: createdLanguagePackage.id,
name: group.name,
description: group.description,
active,
VocabularyCards: group.VocabularyCards.map((vocabularyCard) => ({
userId,
languagePackageId: createdLanguagePackage.id,
drawerId: queryStatus ? getDrawer(vocabularyCard.drawerId).id : getDrawer().id,
name: vocabularyCard.name,
description: vocabularyCard.description,
lastQuery: yesterday,
lastQueryCorrect: yesterday,
active,
Translations: vocabularyCard.Translations.map((translation) => ({
name: translation.name,
userId,
languagePackageId: createdLanguagePackage.id,
})),
})),
},
{
transaction,
include: [
{
model: VocabularyCard,
include: [
{
model: Translation,
},
],
},
],
}
);
})
);

await transaction.commit();
} catch (error) {
await transaction.rollback();
throw new ApiError(httpStatus.BAD_REQUEST, 'Error importing vocabs');
}
}

module.exports = {
storeGroupVocabulary,
storeLanguagePackageVocabulary,
};
Loading