diff --git a/backend/package-lock.json b/backend/package-lock.json index fa01c4c..68ab0e6 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -13,7 +13,8 @@ "body-parser": "^1.20.2", "express": "^4.18.3", "sequelize": "^6.37.1", - "sqlite3": "^5.1.7" + "sqlite3": "^5.1.7", + "tokenizr": "^1.7.0" }, "devDependencies": { "@types/express": "^4.17.21", @@ -3706,6 +3707,11 @@ "node": ">=0.6" } }, + "node_modules/tokenizr": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/tokenizr/-/tokenizr-1.7.0.tgz", + "integrity": "sha512-XHJRmcTZRK4xr9Rjci/Z5JerDwV4SOczO7G/hhrIddNLDJY9hZY7pX6vC7GvDUtlfjFb0BvBblE5JAVKbB2OEQ==" + }, "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", diff --git a/backend/package.json b/backend/package.json index 4279362..8514744 100644 --- a/backend/package.json +++ b/backend/package.json @@ -27,6 +27,7 @@ "body-parser": "^1.20.2", "express": "^4.18.3", "sequelize": "^6.37.1", - "sqlite3": "^5.1.7" + "sqlite3": "^5.1.7", + "tokenizr": "^1.7.0" } } diff --git a/backend/src/app.ts b/backend/src/app.ts index ffb6ebb..6f073bb 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -1,6 +1,8 @@ import express from 'express' import { json } from 'body-parser' +import { Sequelize } from 'sequelize' import { database, Character, Game, Pick, App } from './db' +import { lexr, orderByLexr, parseOrderByString } from './tokenizer' const app = express() const jsonParser = json() @@ -34,16 +36,17 @@ app.get('/api/game/:gameId', async (req, res) => { }) app.post('/api/game', jsonParser, async (req, res) => { + console.log(req.body) + const page = req.body.page || 0 - const orderBy = req.body.orderBy || 'id' + const orderBy = req.body.orderBy ? parseOrderByString(req.body.orderBy) : ['id'] const count = req.body.count || 10 const filter = req.body.filter || '' - console.log(filter) - const gameData = await Game.findAll({ offset: page * count, - limit: count + limit: count, + order: orderBy }) const pageCount = Math.ceil((await Character.count()) / count) diff --git a/backend/src/db.ts b/backend/src/db.ts new file mode 100644 index 0000000..a7a4842 --- /dev/null +++ b/backend/src/db.ts @@ -0,0 +1,77 @@ +import { Sequelize, Model, DataTypes } from 'sequelize' + +const databasePath = './testdb.db' + +export const database = new Sequelize({ + dialect: 'sqlite', + storage: databasePath +}) + +export const Character = database.define( + 'Characters', + { + id: { type: DataTypes.INTEGER, primaryKey: true }, + characterName: { type: DataTypes.TEXT }, + playerName: { type: DataTypes.TEXT }, + role: { type: DataTypes.TEXT }, + creationDate: { type: DataTypes.INTEGER }, + status: { type: DataTypes.TEXT } + }, + { timestamps: false } +) + +export const Game = database.define( + 'Games', + { + id: { type: DataTypes.INTEGER, primaryKey: true }, + title: { type: DataTypes.TEXT }, + status: { type: DataTypes.TEXT }, + fix: { type: DataTypes.BOOLEAN }, + postdate: { type: DataTypes.INTEGER }, + gamemaster: { type: DataTypes.TEXT }, + payoutEB: { type: DataTypes.INTEGER }, + payoutIP: { type: DataTypes.INTEGER }, + payputLoot: { type: DataTypes.INTEGER } + }, + { timestamps: false } +) + +export const Pick = database.define( + 'Picks', + { + gameId: { type: DataTypes.INTEGER, primaryKey: true }, + gameTitle: { type: DataTypes.TEXT }, + characterId: { type: DataTypes.INTEGER, primaryKey: true }, + characterName: { type: DataTypes.TEXT } + }, + { timestamps: false } +) + +export const App = database.define( + 'Apps', + { + gameId: { type: DataTypes.INTEGER, primaryKey: true }, + gameTitle: { type: DataTypes.TEXT }, + characterId: { type: DataTypes.INTEGER, primaryKey: true }, + characterName: { type: DataTypes.TEXT } + }, + { timestamps: false } +) + +// Bind characters to applications and picks +Character.hasMany(App, { foreignKey: 'characterId', as: 'appliedCharacter' }) +App.belongsTo(Character, { foreignKey: 'characterId', as: 'appliedCharacter' }) +Character.hasMany(Pick, { foreignKey: 'characterId', as: 'pickedCharacter' }) +Pick.belongsTo(Character, { foreignKey: 'characterId', as: 'pickedCharacter' }) + +// Bind games to applications and picks +Game.hasMany(App, { foreignKey: 'gameId', as: 'gameApplications' }) +App.belongsTo(Game, { foreignKey: 'gameId', as: 'gameApplications' }) +Game.hasMany(Pick, { foreignKey: 'gameId', as: 'gamePicks' }) +Pick.belongsTo(Game, { foreignKey: 'gameId', as: 'gamePicks' }) + +// Bind picked characters to games. +Game.belongsToMany(Character, { through: 'Apps', as: 'characterAppliedForGame' }) +Character.belongsToMany(Game, { through: 'Apps', as: 'characterAppliedForGame' }) +Game.belongsToMany(Character, { through: 'Picks', as: 'characterPickedForGame' }) +Character.belongsToMany(Game, { through: 'Picks', as: 'characterPickedForGame' }) diff --git a/backend/src/tokenizer.ts b/backend/src/tokenizer.ts new file mode 100644 index 0000000..c5ce390 --- /dev/null +++ b/backend/src/tokenizer.ts @@ -0,0 +1,49 @@ +import Tokenizr from 'tokenizr' + +export const lexr = new Tokenizr() + +lexr.rule(/[AND|OR]/, (ctx, m) => { + ctx.accept('conjunction', m[0]) +}) + +export const orderByLexr = new Tokenizr() + +orderByLexr.rule(/,/, (ctx, m) => { + ctx.accept('spacer') +}) + +orderByLexr.rule(/ASC|DESC/, (ctx, m) => { + ctx.accept('direction', m[0]) +}) + +orderByLexr.rule(/[a-zA-Z]+/, (ctx, m) => { + ctx.accept('column', m[0]) +}) + +orderByLexr.rule(/\s/, (ctx, m) => { + ctx.ignore() +}) + +export function parseOrderByString(orderBy: string) { + const output = [] + let holding = [] + orderByLexr + .input(orderBy) + .tokens() + .forEach((token) => { + switch (token.type) { + case 'spacer': + output.push(holding) + holding = [] + break + case 'column': + case 'direction': + holding.push(token.value) + break + } + }) + if (holding) { + output.push(holding) + } + return output +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 12c718a..0833ea6 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,7 +6,6 @@ Characters GMs -