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
-