Implementing Exercise system in database with API and frontend visualization
This commit is contained in:
54
software/backend/data/exercises.json
Normal file
54
software/backend/data/exercises.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"nameDe": "Den Shop kennenlernen",
|
||||
"nameEn": "Getting to know the shop",
|
||||
"groupNr": 0,
|
||||
"exercises": [
|
||||
{
|
||||
"nameDe": "Registrieren",
|
||||
"nameEn": "Register",
|
||||
"exerciseNr": 1,
|
||||
"descriptionDe": "Erstelle einen neuen Account im Online Shop",
|
||||
"descriptionEn": "Create a new account in the online shop",
|
||||
"solved": false
|
||||
},
|
||||
{
|
||||
"nameDe": "Ein Ticket kaufen",
|
||||
"nameEn": "Buy a ticket",
|
||||
"exerciseNr": 2,
|
||||
"descriptionDe": "Suche dir ein Event deiner Wahl und kaufe dafür ein Ticket",
|
||||
"descriptionEn": "Search for an event of choice and buy a ticket for",
|
||||
"solved": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nameDe": "SQL Injections",
|
||||
"nameEn": "SQL Injections",
|
||||
"groupNr": 1,
|
||||
"exercises": []
|
||||
},
|
||||
{
|
||||
"nameDe": "Broken Access Control",
|
||||
"nameEn": "Broken Access Control",
|
||||
"groupNr": 2,
|
||||
"exercises": []
|
||||
},
|
||||
{
|
||||
"nameDe": "Cross-Site Scripting (XSS)",
|
||||
"nameEn": "Cross-Site Scripting (XSS)",
|
||||
"groupNr": 3,
|
||||
"exercises": [
|
||||
{
|
||||
"nameDe": "Hallo Welt!",
|
||||
"nameEn": "Hello World!",
|
||||
"exerciseNr": 1,
|
||||
"descriptionDe": "Nimm dir eine URL des Shops und erweitere sie mit JavaScript Code so, dass beim Öffnen des Links eine 'Hallo Welt' Nachricht erscheint",
|
||||
"descriptionEn": "Take an URL of the shop and extend it with JavaScript code so that a 'Hello World' message appears whent the link is opened",
|
||||
"solved": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,9 @@
|
||||
{
|
||||
"concertId": 0,
|
||||
"orderPrice": 184,
|
||||
"seatId": 43
|
||||
"seatGroup": "A",
|
||||
"seatRow": 0,
|
||||
"seat": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -24,12 +26,16 @@
|
||||
{
|
||||
"concertId": 0,
|
||||
"orderPrice": 184,
|
||||
"seatId": 2
|
||||
"seatGroup": "A",
|
||||
"seatRow": 0,
|
||||
"seat": 2
|
||||
},
|
||||
{
|
||||
"concertId": 0,
|
||||
"orderPrice": 184,
|
||||
"seatId": 3
|
||||
"seatGroup": "A",
|
||||
"seatRow": 0,
|
||||
"seat": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -43,7 +49,9 @@
|
||||
{
|
||||
"concertId": 0,
|
||||
"orderPrice": 184,
|
||||
"seatId": 3
|
||||
"seatGroup": "A",
|
||||
"seatRow": 0,
|
||||
"seat": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import { BandGenre } from "./models/acts/bandGenre.model"
|
||||
import { Seat } from "./models/locations/seat.model"
|
||||
import { SeatGroup } from "./models/locations/seatGroup.model"
|
||||
import { SeatRow } from "./models/locations/seatRow.model"
|
||||
import { Exercise } from "./models/exercises/exercise.model"
|
||||
import { ExerciseGroup } from "./models/exercises/exerciseGroup.model"
|
||||
|
||||
const dbName = "database"
|
||||
const dbUser = "root"
|
||||
@@ -36,19 +38,20 @@ export const sequelize = new Sequelize({
|
||||
AccountRole, Account, Payment, Address,
|
||||
City, Location, SeatGroup, SeatRow, Seat,
|
||||
Genre, Band, BandGenre, Rating, Member, Event, Concert,
|
||||
Order, Ticket
|
||||
Order, Ticket,
|
||||
Exercise, ExerciseGroup
|
||||
]
|
||||
})
|
||||
|
||||
export function startDatabase() {
|
||||
let force = false
|
||||
let recreateDb = false
|
||||
|
||||
// Create database and tables
|
||||
sequelize.sync({ force: force })
|
||||
sequelize.sync({ force: recreateDb })
|
||||
.then(() => {
|
||||
console.log("Database & tables created!")
|
||||
|
||||
if (force) {
|
||||
if (recreateDb) {
|
||||
prepopulateDatabase()
|
||||
}
|
||||
|
||||
|
||||
33
software/backend/models/exercises/exercise.model.ts
Normal file
33
software/backend/models/exercises/exercise.model.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { BelongsTo, Column, ForeignKey, Model, Table } from "sequelize-typescript";
|
||||
import { ExerciseGroup } from "./exerciseGroup.model";
|
||||
|
||||
@Table({ timestamps: false })
|
||||
export class Exercise extends Model {
|
||||
@Column
|
||||
nameDe: String
|
||||
|
||||
@Column
|
||||
nameEn: String
|
||||
|
||||
@Column
|
||||
exerciseNr: Number
|
||||
|
||||
@Column
|
||||
descriptionDe: String
|
||||
|
||||
@Column
|
||||
descriptionEn: String
|
||||
|
||||
@Column
|
||||
solved: Boolean
|
||||
|
||||
@ForeignKey(() => ExerciseGroup)
|
||||
@Column
|
||||
exerciseGroupId: Number
|
||||
|
||||
|
||||
// Relations
|
||||
|
||||
@BelongsTo(() => ExerciseGroup)
|
||||
exerciseGroup: ExerciseGroup
|
||||
}
|
||||
20
software/backend/models/exercises/exerciseGroup.model.ts
Normal file
20
software/backend/models/exercises/exerciseGroup.model.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Column, HasMany, Model, Table } from "sequelize-typescript";
|
||||
import { Exercise } from "./exercise.model";
|
||||
|
||||
@Table({ timestamps: false })
|
||||
export class ExerciseGroup extends Model {
|
||||
@Column
|
||||
nameDe: String
|
||||
|
||||
@Column
|
||||
nameEn: String
|
||||
|
||||
@Column
|
||||
groupNr: Number
|
||||
|
||||
|
||||
// Relations
|
||||
|
||||
@HasMany(() => Exercise)
|
||||
exercises: Exercise[]
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { validateString } from "../scripts/validateHelper";
|
||||
import { Address } from "../models/user/address.model";
|
||||
import { Payment } from "../models/user/payment.model";
|
||||
import { AccountRole } from "../models/user/accountRole.model";
|
||||
import { Exercise } from "../models/exercises/exercise.model";
|
||||
|
||||
export const account = Router()
|
||||
|
||||
@@ -65,16 +66,26 @@ account.post("/", (req: Request, res: Response) => {
|
||||
|
||||
// Create account
|
||||
Account.create(req.body)
|
||||
.then(account => {
|
||||
// Status: 201 Created
|
||||
res.status(201).json(account)
|
||||
}).catch(reason => {
|
||||
// Status: 409 Conflict
|
||||
res.status(409).json({
|
||||
code: 409,
|
||||
message: "Username already in use"
|
||||
.then(account => {
|
||||
// Status: 201 Created
|
||||
res.status(201).json(account)
|
||||
|
||||
// Check exercise in table
|
||||
Exercise.update(
|
||||
{ solved: true },
|
||||
{
|
||||
where: {
|
||||
nameEn: "Register"
|
||||
}
|
||||
}
|
||||
)
|
||||
}).catch(reason => {
|
||||
// Status: 409 Conflict
|
||||
res.status(409).json({
|
||||
code: 409,
|
||||
message: "Username already in use"
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
account.patch("/", (req: Request, res: Response) => {
|
||||
|
||||
@@ -7,12 +7,12 @@ api.get("/", (req: Request, res: Response, next: NextFunction) => {
|
||||
res.status(200).send()
|
||||
})
|
||||
|
||||
api.get("/resetdatabase", (req: Request, res: Response, next: NextFunction) => {
|
||||
api.get("/resetdatabase", async (req: Request, res: Response, next: NextFunction) => {
|
||||
// Step 1: Delete all data tables
|
||||
deleteAllTables()
|
||||
|
||||
// Step 2: Prepopulate with default values
|
||||
prepopulateDatabase()
|
||||
await prepopulateDatabase()
|
||||
|
||||
// Step 3: Send status back
|
||||
res.status(200).send()
|
||||
|
||||
23
software/backend/routes/exercise.routes.ts
Normal file
23
software/backend/routes/exercise.routes.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { Exercise } from "../models/exercises/exercise.model";
|
||||
import { ExerciseGroup } from "../models/exercises/exerciseGroup.model";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
export const exercises = Router()
|
||||
|
||||
exercises.get("/", (req: Request, res: Response) => {
|
||||
ExerciseGroup.findAll(
|
||||
{
|
||||
include: [
|
||||
{
|
||||
model: Exercise,
|
||||
separate: true,
|
||||
order: [
|
||||
[ 'id', 'ASC' ]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
).then(result => {
|
||||
res.status(200).json(result)
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,8 @@ import { BandGenre } from '../models/acts/bandGenre.model'
|
||||
import { SeatGroup } from '../models/locations/seatGroup.model'
|
||||
import { Seat } from '../models/locations/seat.model'
|
||||
import { SeatRow } from '../models/locations/seatRow.model'
|
||||
import { Exercise } from '../models/exercises/exercise.model'
|
||||
import { ExerciseGroup } from '../models/exercises/exerciseGroup.model'
|
||||
|
||||
import accounts from "./../data/accounts.json"
|
||||
import orders from "./../data/orders.json"
|
||||
@@ -24,6 +26,7 @@ import bands from "./../data/bands.json"
|
||||
import genres from "./../data/genres.json"
|
||||
import events from "./../data/events.json"
|
||||
import cities from "./../data/cities.json"
|
||||
import exercises from "./../data/exercises.json"
|
||||
|
||||
|
||||
/**
|
||||
@@ -49,6 +52,9 @@ export function deleteAllTables() {
|
||||
Payment.destroy({ truncate: true })
|
||||
Account.destroy({ truncate: true })
|
||||
AccountRole.destroy({ truncate: true})
|
||||
|
||||
Exercise.destroy({truncate: true})
|
||||
ExerciseGroup.destroy({truncate: true})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,10 +150,10 @@ export async function prepopulateDatabase() {
|
||||
}
|
||||
|
||||
|
||||
for (let tour of events.data) {
|
||||
await Event.create(tour)
|
||||
for (let event of events.data) {
|
||||
await Event.create(event)
|
||||
.then(async dataset => {
|
||||
for (let concert of tour.concerts) {
|
||||
for (let concert of event.concerts) {
|
||||
concert["eventId"] = dataset.id
|
||||
|
||||
await Concert.create(concert)
|
||||
@@ -161,7 +167,42 @@ export async function prepopulateDatabase() {
|
||||
for (let ticket of order.tickets) {
|
||||
ticket["orderId"] = dataset.id
|
||||
|
||||
await Ticket.create(ticket)
|
||||
SeatGroup.findOne({
|
||||
where: {
|
||||
name: ticket.seatGroup
|
||||
}
|
||||
})
|
||||
.then(seatGroup => {
|
||||
SeatRow.findOne({
|
||||
where: {
|
||||
seatGroupId: seatGroup.id,
|
||||
row: ticket.seatRow
|
||||
}
|
||||
})
|
||||
.then(seatRow => {
|
||||
Seat.findOne({
|
||||
where: {
|
||||
seatRowId: seatRow.id,
|
||||
seatNr: ticket.seat
|
||||
}
|
||||
})
|
||||
.then(async seat => {
|
||||
ticket["seatId"] = seat.id
|
||||
await Ticket.create(ticket)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
for (let exerciseGroup of exercises.data) {
|
||||
ExerciseGroup.create(exerciseGroup)
|
||||
.then(async dataset => {
|
||||
for (let exercise of exerciseGroup.exercises) {
|
||||
exercise["exerciseGroupId"] = dataset.id
|
||||
|
||||
await Exercise.create(exercise)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { genre } from './routes/genre.routes'
|
||||
import { location } from './routes/location.routes'
|
||||
import { events } from './routes/events.routes'
|
||||
import { city } from './routes/city.routes'
|
||||
import { exercises } from './routes/exercise.routes'
|
||||
|
||||
const app = express()
|
||||
const port = 3000
|
||||
@@ -43,6 +44,7 @@ app.use("/orders", order)
|
||||
app.use("/accounts", account)
|
||||
app.use("/cities", city)
|
||||
app.use("/concerts", concert)
|
||||
app.use("/exercises", exercises)
|
||||
|
||||
|
||||
// Start server
|
||||
|
||||
Reference in New Issue
Block a user