Skip to content

Commit

Permalink
Merge pull request #54 from vocascan/import-export
Browse files Browse the repository at this point in the history
Import/Export functions
  • Loading branch information
luwol03 authored Oct 17, 2021
2 parents 8001ed2 + da25c89 commit dea4b16
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 26 deletions.
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,
};
184 changes: 184 additions & 0 deletions app/Services/ImportServiceProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
const { Group, LanguagePackage, 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 languagePackage = await LanguagePackage.findOne({
where: {
userId,
id: languagePackageId,
},
});

if (!languagePackage) {
throw new ApiError(httpStatus.NOT_FOUND, 'language package not found');
}

const transaction = await sequelize.transaction();

try {
const group = await Group.create(
{
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

0 comments on commit dea4b16

Please sign in to comment.