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

Generate sqlite3 with migrations #138

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ node_modules
npm-debug.log
.vscode
exercises.json
exercises-psql.sql
exercises-*.sql
*.sqlite3
31 changes: 31 additions & 0 deletions database/migrations/20210718114500_enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Knex from "knex";

// Migration to create the "enum" tables. These are lookup tables used
// by the main "exercises" table.

const ENUM_TABLES = [
"muscles",
"force",
"level",
"mechanic",
"equipment",
"category",
];

exports.up = async (knex: Knex) => {
for (const table of ENUM_TABLES) {
await knex.schema.createTable(table, (table) => {
table.increments().primary();
table.string("value").notNullable();
table.timestamps();
});
}
};

exports.down = async (knex: Knex) => {
for (const table of ENUM_TABLES) {
await knex.schema.dropTable(table);
}
};

exports.config = { transaction: false };
54 changes: 54 additions & 0 deletions database/migrations/20210718114600_exercises.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Knex from "knex";

// Migration to create the main "exercises" table. It uses the "enum" tables
// that must have been created previously.

exports.up = (knex: Knex) => {
return knex.schema
.createTable("exercises", (table) => {
table.increments().primary();
table.string("name").notNullable().unique();
table.integer("force_id").unsigned();
table.integer("level_id").unsigned();
table.integer("mechanic_id").unsigned();
table.integer("equipment_id").unsigned();
table.integer("category_id").unsigned();
table.text("instructions");
table.text("description");
table.text("tips");
table.timestamps();

table.foreign("force_id").references("force.id");
table.foreign("level_id").references("level.id");
table.foreign("mechanic_id").references("mechanic.id");
table.foreign("equipment_id").references("equipment.id");
table.foreign("category_id").references("category.id");
})
.createTable("exercises_primary_muscles", (table) => {
table.integer("exercise_id").unsigned();
table.integer("muscle_id").unsigned();

table.primary(["exercise_id", "muscle_id"]);
table.foreign("exercise_id").references("exercises.id");
table.foreign("muscle_id").references("muscles.id");
table.timestamps();
})
.createTable("exercises_secondary_muscles", (table) => {
table.integer("exercise_id").unsigned();
table.integer("muscle_id").unsigned();

table.primary(["exercise_id", "muscle_id"]);
table.foreign("exercise_id").references("exercises.id");
table.foreign("muscle_id").references("muscles.id");
table.timestamps();
});
};

exports.down = (knex: Knex) => {
return knex.schema
.dropTable("exercises_secondary_muscles")
.dropTable("exercises_primary_muscles")
.dropTable("exercises");
};

exports.config = { transaction: false };
98 changes: 98 additions & 0 deletions database/seeds/20210718151500_exercises.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import Knex from "knex";
import { Dirent, readdirSync } from "fs";
import { Exercise } from "../../types/exercise";
import { resolve } from "path";
import { EOL } from "os";

const getDirectories = (folder: string): Array<Dirent> => {
return readdirSync(folder, {
withFileTypes: true,
}).filter((dir) => dir.isDirectory());
};

const getExercises = (directories: Array<Dirent>): Array<Exercise> => {
return directories.map((dir) => {
const exercisePath = resolve(`./exercises/${dir.name}/exercise.json`);
return require(exercisePath);
});
};

const getEnumId = async (knex: Knex, tableName: string, value: any) => {
if (value === null) {
return null;
}
const enumId = await knex(tableName)
.select("id")
.where({
value,
})
.first();
if (enumId) {
return enumId.id;
}
// This value hasn't been inserted yet...
const count = await knex(tableName).count<number>({ count: "*" });
// @ts-ignore
const id = 1 + count.count;
await knex(tableName).insert({
id,
value,
});
return id;
};

const insertExercises = async (knex: Knex): Promise<void> => {
const directories = getDirectories("./exercises");
const exercises = getExercises(directories);
for (const exercise of exercises) {
let index = exercises.indexOf(exercise);
// Add new exercise
const instructions = exercise.instructions
? exercise.instructions.join(EOL)
: [];
const tips = exercise.tips ? exercise.tips.join(EOL) : [];
const force_id = await getEnumId(knex, "force", exercise.force);
const level_id = await getEnumId(knex, "level", exercise.level);
const mechanic_id = await getEnumId(knex, "mechanic", exercise.mechanic);
const equipment_id = await getEnumId(knex, "equipment", exercise.equipment);
const category_id = await getEnumId(knex, "category", exercise.category);
const exerciseId = ++index;
const data = {
id: exerciseId,
name: exercise.name,
force_id,
level_id,
mechanic_id,
equipment_id,
category_id,
instructions,
description: exercise.description,
tips,
};
await knex("exercises").insert(data);
// And also add its relationships
for (const muscle of exercise.primaryMuscles) {
const primaryMuscleId = await getEnumId(knex, "muscles", muscle);
await knex("exercises_primary_muscles").insert({
exercise_id: exerciseId,
muscle_id: primaryMuscleId,
});
}
for (const muscle of exercise.secondaryMuscles) {
const secondaryMuscleId = await getEnumId(knex, "muscles", muscle);
await knex("exercises_secondary_muscles").insert({
exercise_id: exerciseId,
muscle_id: secondaryMuscleId,
});
}
}
};

exports.seed = (knex: Knex) =>
Promise.all([
knex("exercises_secondary_muscles").del(),
knex("exercises_primary_muscles").del(),
knex("exercises").del(),
]).then(() => insertExercises(knex));

exports.config = { transaction: false };
19 changes: 19 additions & 0 deletions knexfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Update with your config settings.

module.exports = {
development: {
client: "sqlite3",
connection: {
filename: "./dev.exercises.sqlite3",
},
useNullAsDefault: true,
migrations: {
directory: "database/migrations/",
loadExtensions: [".ts"],
},
seeds: {
directory: "database/seeds",
loadExtensions: [".ts"],
},
},
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"lint": "eslint . --ext .ts --quiet",
"test": "jest",
"build:json": "ts-node scripts/json-builder.ts",
"build:psql": "ts-node scripts/psql-builder.ts"
"build:psql": "ts-node scripts/psql-builder.ts",
"build:sqlite3": "ts-node scripts/sqlite3-builder.ts"
},
"keywords": [
"exercise",
Expand Down Expand Up @@ -46,6 +47,7 @@
"dependencies": {
"knex": "^0.21.5",
"lodash.snakecase": "^4.1.1",
"sqlite3": "^5.0.2",
"uuid": "^8.3.0"
}
}
16 changes: 16 additions & 0 deletions scripts/builders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Dirent, readdirSync } from "fs";
import { Exercise } from "../types/exercise";
import { resolve } from "path";

export const getDirectories = (folder: string): Array<Dirent> => {
return readdirSync(folder, {
withFileTypes: true,
}).filter((dir) => dir.isDirectory());
};

export const getExercises = (directories: Array<Dirent>): Array<Exercise> => {
return directories.map((dir) => {
const exercisePath = resolve(`./exercises/${dir.name}/exercise.json`);
return require(exercisePath);
});
};
19 changes: 2 additions & 17 deletions scripts/json-builder.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,6 @@
import { readdirSync, Dirent, writeFileSync } from "fs";
import { resolve } from "path";
import { writeFileSync } from "fs";
import { Exercise } from "../types/exercise";

const getDirectories = (folder: string): Array<Dirent> => {
const subFolders = readdirSync(folder, {
withFileTypes: true,
}).filter((dir) => dir.isDirectory());

return subFolders;
};

const getExercises = (directories: Array<Dirent>): Array<Exercise> => {
return directories.map((dir) => {
const exercisePath = resolve(`./exercises/${dir.name}/exercise.json`);
return require(exercisePath);
});
};
import { getDirectories, getExercises } from "./builders";

const createJSONFile = (exercises: Array<Exercise>) => {
writeFileSync(
Expand Down
20 changes: 2 additions & 18 deletions scripts/psql-builder.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// npm run build:psql <db.name> | 'exercises';

import { readdirSync, Dirent, writeFileSync } from "fs";
import { writeFileSync } from "fs";
import { EOL } from "os";
import { resolve } from "path";
import { v4 as uuid } from "uuid";
import knex from "knex";
import snakecase from "lodash.snakecase";
import { Exercise } from "../types/exercise";
import { getDirectories, getExercises } from "./builders";

const psql = knex({
client: "pg",
Expand All @@ -15,22 +15,6 @@ const psql = knex({

const tableName = process.argv[2] || "exercises";

//
const getDirectories = (folder: string): Array<Dirent> => {
const subFolders = readdirSync(folder, {
withFileTypes: true,
}).filter((dir) => dir.isDirectory());

return subFolders;
};

const getExercises = (directories: Array<Dirent>): Array<Exercise> => {
return directories.map((dir) => {
const exercisePath = resolve(`./exercises/${dir.name}/exercise.json`);
return require(exercisePath);
});
};

const createPostgresEnum = (
exercises: Array<Exercise>,
field: string,
Expand Down
Loading