Leon Versteeg преди 3 години
родител
ревизия
7a9d838d83

+ 11 - 0
src/main/kotlin/com/virtualprogrammers/theater/TheaterApplication.kt

@@ -0,0 +1,11 @@
+package com.virtualprogrammers.theater
+
+import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.runApplication
+
+@SpringBootApplication
+class TheaterApplication
+
+fun main(args: Array<String>) {
+	runApplication<TheaterApplication>(*args)
+}

+ 88 - 0
src/main/kotlin/com/virtualprogrammers/theater/control/MainController.kt

@@ -0,0 +1,88 @@
+package com.virtualprogrammers.theater.control
+
+import com.virtualprogrammers.theater.data.PerformanceRepository
+import com.virtualprogrammers.theater.data.SeatRepository
+import com.virtualprogrammers.theater.domain.Booking
+import com.virtualprogrammers.theater.domain.Performance
+import com.virtualprogrammers.theater.domain.Seat
+import com.virtualprogrammers.theater.services.BookingService
+import com.virtualprogrammers.theater.services.TheaterService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Controller
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestMethod
+import org.springframework.web.servlet.ModelAndView
+
+@Controller
+class MainController {
+
+    @Autowired
+    lateinit var theaterService: TheaterService
+
+    @Autowired
+    lateinit var bookingService: BookingService
+
+    @Autowired
+    lateinit var seatRepository: SeatRepository
+
+    @Autowired
+    lateinit var performanceRepository: PerformanceRepository
+
+    @RequestMapping("")
+    fun homepage(): ModelAndView {
+        val model = mapOf(
+            "bean" to CheckAvailabilityBackingBean(),
+            "performances" to performanceRepository.findAll(),
+            "seatNums" to 1..36,
+            "seatRows" to 'A'..'O'
+        )
+
+        return ModelAndView("seatBooking", model)
+    }
+
+    @RequestMapping("checkAvailability", method = [RequestMethod.POST])
+    fun checkAvailability(bean: CheckAvailabilityBackingBean): ModelAndView {
+        val selectedSeat: Seat = bookingService.findSeat(bean.selectedSeatNum, bean.selectedSeatRow)!!
+        val selectedPerformance = performanceRepository.findById(bean.selectedPerformance!!).get()
+        bean.seat = bookingService.findSeat(bean.selectedSeatNum, bean.selectedSeatRow)!!
+        bean.performance = performanceRepository.findById(bean.selectedPerformance!!).get()
+        bean.available = bookingService.isSeatFree(selectedSeat, selectedPerformance)
+
+        if (!bean.available!!) {
+            bean.booking = bookingService.findBooking(selectedSeat, selectedPerformance)
+        }
+        val model = mapOf(
+            "bean" to bean,
+            "performances" to performanceRepository.findAll(),
+            "seatNums" to 1..36,
+            "seatRows" to 'A'..'O'
+        )
+
+        return ModelAndView("seatBooking", model)
+    }
+
+    @RequestMapping(value = ["booking"], method = [RequestMethod.POST])
+    fun bookASeat(bean: CheckAvailabilityBackingBean): ModelAndView {
+        val booking = bookingService.reserveSeat(bean.seat!!, bean.performance!!, bean.customerName)
+        return ModelAndView("bookingConfirmed", "booking", booking)
+    }
+
+    /* @RequestMapping("bootstrap")
+     fun createInitialData(): ModelAndView {
+         val seats = theaterService.seats
+         seatRepository.saveAll(seats)
+         return homepage()
+     }*/
+}
+
+class CheckAvailabilityBackingBean() {
+    var selectedSeatNum = 1
+    var selectedSeatRow = 'A'
+    var selectedPerformance: Long? = null
+    var customerName: String = ""
+
+    var available: Boolean? = null
+    var seat: Seat? = null
+    var performance: Performance? = null
+    var booking: Booking? = null
+}

+ 31 - 0
src/main/kotlin/com/virtualprogrammers/theater/control/PerformanceController.kt

@@ -0,0 +1,31 @@
+package com.virtualprogrammers.theater.control
+
+import com.virtualprogrammers.theater.data.PerformanceRepository
+import com.virtualprogrammers.theater.domain.Performance
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Controller
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.bind.annotation.RequestMethod
+import org.springframework.web.servlet.ModelAndView
+
+@Controller
+@RequestMapping("/performances")
+class PerformanceController() {
+
+    @Autowired
+    lateinit var performanceRepository: PerformanceRepository
+
+    @RequestMapping("")
+    fun performancesHomePage() =
+        ModelAndView("performances/home", "performances", performanceRepository.findAll())
+
+    @RequestMapping("/add")
+    fun addPerformance() =
+        ModelAndView("performances/add", "performance", Performance(0, ""))
+
+    @RequestMapping(value = ["save"], method = [RequestMethod.POST])
+    fun savePerformance(performance: Performance): String {
+        performanceRepository.save(performance)
+        return "redirect:/performances/"
+    }
+}

+ 29 - 0
src/main/kotlin/com/virtualprogrammers/theater/control/ReportsController.kt

@@ -0,0 +1,29 @@
+package com.virtualprogrammers.theater.control
+
+import com.virtualprogrammers.theater.services.ReportingService
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Controller
+import org.springframework.web.bind.annotation.RequestMapping
+import org.springframework.web.servlet.ModelAndView
+import javax.websocket.server.PathParam
+import kotlin.reflect.full.declaredMemberFunctions
+
+@Controller
+@RequestMapping("/reports")
+class ReportsController {
+
+    @Autowired
+    lateinit var reportingService: ReportingService
+
+    private fun getListOfReports() = reportingService::class.declaredMemberFunctions.map { it.name }
+
+    @RequestMapping("")
+    fun main() = ModelAndView("reports", mapOf("reports" to getListOfReports()))
+
+    @RequestMapping("/getReport")
+    fun getReports(@PathParam("report") report: String): ModelAndView {
+        val matchedReport = reportingService::class.declaredMemberFunctions.firstOrNull { it.name == report }
+        val result = matchedReport?.call(reportingService) ?: ""
+        return ModelAndView("reports", mapOf("reports" to getListOfReports(), "result" to result))
+    }
+}

+ 12 - 0
src/main/kotlin/com/virtualprogrammers/theater/data/JPAInterface.kt

@@ -0,0 +1,12 @@
+package com.virtualprogrammers.theater.data
+
+import com.virtualprogrammers.theater.domain.Booking
+import com.virtualprogrammers.theater.domain.Performance
+import com.virtualprogrammers.theater.domain.Seat
+import org.springframework.data.jpa.repository.JpaRepository
+
+interface SeatRepository : JpaRepository<Seat, Long>
+
+interface PerformanceRepository : JpaRepository<Performance, Long>
+
+interface BookingRepository : JpaRepository<Booking, Long>

+ 16 - 0
src/main/kotlin/com/virtualprogrammers/theater/domain/Booking.kt

@@ -0,0 +1,16 @@
+package com.virtualprogrammers.theater.domain
+
+import javax.persistence.*
+
+@Entity
+data class Booking(
+    @Id @GeneratedValue(strategy = GenerationType.AUTO)
+    val id: Long,
+    val customerName: String
+) {
+    @ManyToOne
+    lateinit var seat: Seat
+
+    @ManyToOne
+    lateinit var performance: Performance
+}

+ 13 - 0
src/main/kotlin/com/virtualprogrammers/theater/domain/Performance.kt

@@ -0,0 +1,13 @@
+package com.virtualprogrammers.theater.domain
+
+import javax.persistence.*
+
+@Entity
+data class Performance(
+    @Id @GeneratedValue(strategy = GenerationType.AUTO)
+    val id: Long,
+    val title: String
+) {
+    @OneToMany(mappedBy = "performance")
+    lateinit var bookings: List<Booking>
+}

+ 19 - 0
src/main/kotlin/com/virtualprogrammers/theater/domain/Seat.kt

@@ -0,0 +1,19 @@
+package com.virtualprogrammers.theater.domain
+
+import java.math.BigDecimal
+import javax.persistence.Entity
+import javax.persistence.GeneratedValue
+import javax.persistence.GenerationType
+import javax.persistence.Id
+
+@Entity
+data class Seat(
+    @Id @GeneratedValue(strategy = GenerationType.AUTO)
+    val id: Long,
+    val row: Char,
+    val num: Int,
+    val price: BigDecimal,
+    val description: String
+) {
+    override fun toString(): String = "Seat $row-$num $$price ($description)"
+}

+ 44 - 0
src/main/kotlin/com/virtualprogrammers/theater/services/BookingService.kt

@@ -0,0 +1,44 @@
+package com.virtualprogrammers.theater.services
+
+import com.virtualprogrammers.theater.data.BookingRepository
+import com.virtualprogrammers.theater.data.SeatRepository
+import com.virtualprogrammers.theater.domain.Booking
+import com.virtualprogrammers.theater.domain.Performance
+import com.virtualprogrammers.theater.domain.Seat
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+
+@Service
+class BookingService {
+
+    @Autowired
+    lateinit var bookingRepository: BookingRepository
+
+    @Autowired
+    lateinit var seatRepository: SeatRepository
+
+    fun isSeatFree(seat: Seat, performance: Performance): Boolean {
+        val bookings = bookingRepository.findAll()
+        val matchedBookings = bookings.filter { it.seat == seat && it.performance == performance }
+        return matchedBookings.isEmpty()
+    }
+
+    fun findSeat(seatNum: Int, seatRow: Char): Seat? {
+        val allSeats = seatRepository.findAll()
+        val foundSeat = allSeats.filter { it.num == seatNum && it.row == seatRow }
+        return foundSeat.firstOrNull()
+    }
+
+    fun findBooking(selectedSeat: Seat, selectedPerformance: Performance): Booking? {
+        return bookingRepository.findAll()
+            .firstOrNull { it.seat == selectedSeat && it.performance == selectedPerformance }
+    }
+
+    fun reserveSeat(seat: Seat, performance: Performance, customerName: String): Booking {
+        val booking = Booking(id = 0L, customerName = customerName)
+        booking.seat = seat
+        booking.performance = performance
+        bookingRepository.save(booking)
+        return booking
+    }
+}

+ 33 - 0
src/main/kotlin/com/virtualprogrammers/theater/services/ReportingService.kt

@@ -0,0 +1,33 @@
+package com.virtualprogrammers.theater.services
+
+import com.virtualprogrammers.theater.data.BookingRepository
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.stereotype.Service
+import java.math.BigDecimal
+
+@Service
+class ReportingService {
+
+    @Autowired
+    lateinit var bookingRepository: BookingRepository
+
+    fun all_bookings(): String {
+        val bookings = bookingRepository.findAll()
+        val htmlBookings =
+            bookings.map { "<tr><td>${it.performance.title}</td><td>${it.seat}</td><td>${it.customerName}</td></tr>" }
+        val reportHeader = "<table><tr><th>Performance</th><th>Seat</th><th>Customer</th></tr>"
+        val reportFooter = "</table>"
+        return "${reportHeader}${htmlBookings.joinToString()}${reportFooter}"
+    }
+
+    fun premium_bookings(): String {
+        val bookings = bookingRepository.findAll()
+        val htmlBookings = bookings
+            .filter { it.seat.price > BigDecimal(15) }
+            .map { "<tr><td>${it.performance.title}</td><td>${it.seat}</td><td>${it.customerName}</td></tr>" }
+        val reportHeader = "<table><tr><th>Performance</th><th>Seat</th><th>Customer</th></tr>"
+        val reportFooter = "</table>"
+        return "${reportHeader}${htmlBookings.joinToString()}${reportFooter}"
+    }
+
+}

+ 45 - 0
src/main/kotlin/com/virtualprogrammers/theater/services/TheaterService.kt

@@ -0,0 +1,45 @@
+package com.virtualprogrammers.theater.services
+
+import com.virtualprogrammers.theater.domain.Seat
+import org.springframework.stereotype.Service
+import java.math.BigDecimal
+
+@Service
+class TheaterService() {
+
+    private val hiddenSeats = mutableListOf<Seat>()
+
+    init {
+        fun getPrice(row: Int, num: Int): BigDecimal {
+            return when {
+                row >= 14 -> BigDecimal(14.50)
+                num <= 3 || num >= 34 -> BigDecimal(16.50)
+                row == 1 -> BigDecimal(21)
+                else -> BigDecimal(18)
+            }
+
+        }
+
+        fun getDescription(row: Int, num: Int): String {
+            return when {
+                row == 15 -> "Back Row"
+                row == 14 -> "Cheaper Seat"
+                num <= 3 || num >= 34 -> "Restricted View"
+                row <= 2 -> "Best View"
+                else -> "Standard Seat"
+            }
+        }
+        for (row in 1..15) {
+            for (num in 1..36) {
+                hiddenSeats.add(Seat(0, (row + 64).toChar(), num, getPrice(row, num), getDescription(row, num)))
+            }
+        }
+    }
+
+    val seats
+        get() = hiddenSeats.toList()
+
+    fun find(num: Int, row: Char): Seat {
+        return seats.first { it.row == row && it.num == num }
+    }
+}

+ 8 - 0
src/main/resources/application.properties

@@ -0,0 +1,8 @@
+spring.datasource.url=jdbc:h2:file:~/theater
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.properties.hibernate.hbm2dll.out=update
+spring.h2.console.enabled=true
+spring.jpa.properties.hibernate.globally_quoted_identifiers=true
+spring.jpa.generate-ddl=true

+ 14 - 0
src/main/resources/templates/bookingConfirmed.html

@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Booking Confirmed</title>
+</head>
+<body>
+    <h1>Booking Confirmed</h1>
+    <p>Seat : <span th:text="${booking.seat}">seat</span></p>
+    <p>Performance : <span th:text="${booking.performance.title}">performance</span></p>
+    <p>Customer : <span th:text="${booking.customerName}">customer</span></p>
+    <a href="/">Book another</a>
+</body>
+</html>

+ 15 - 0
src/main/resources/templates/performances/add.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Add a performance</title>
+</head>
+<body>
+<h1>Add a performance</h1>
+<form action="/performances/save" method="POST" th:object="${performance}">
+    <input type="hidden" th:field="*{id}"/>
+    <p>Name of performance: <input type="text" th:field="*{title}"/></p>
+    <input type="submit"/>
+</form>
+</body>
+</html>

+ 19 - 0
src/main/resources/templates/performances/home.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Performances</title>
+</head>
+<body>
+    <h1>All performances</h1>
+    <table>
+        <tr><th>ID</th><th>Title</th></tr>
+        <tr th:each="performance : ${performances}">
+            <td th:text="${performance.id}">id</td>
+            <td th:text="${performance.title}">title</td>
+        </tr>
+    </table>
+
+    <a href="/performances/add">Add a performance</a>
+</body>
+</html>

+ 23 - 0
src/main/resources/templates/reports.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th:="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Reports</title>
+</head>
+<body>
+    <h1>Reports</h1>
+
+    <form action="/reports/getReport" method="GET">
+        <select name="report" id="report">
+            <option th:each="report : ${reports}" th:value="${report}" th:text="${report}"></option>
+        </select>
+        <input type="submit" value="get" />
+    </form>
+
+    <div th:if="${result}">
+        <h2>Report</h2>
+        <div th:utext="${result}"></div>
+    </div>
+
+</body>
+</html>

+ 45 - 0
src/main/resources/templates/seatBooking.html

@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8">
+    <title>Theater Booking</title>
+</head>
+<body>
+    <h1>Theater Booking</h1>
+<h2>Seat availability</h2>
+
+    <form action="/checkAvailability" th:object="${bean}" method="POST">
+        <p>Check if seat number
+            <select th:field="*{selectedSeatNum}">
+                <option th:each="n : ${seatNums}" th:value="${n}" th:text="${n}" />
+            </select>
+            in row
+            <select th:field="*{selectedSeatRow}">
+                <option th:each="r : ${seatRows}" th:value="${r}" th:text="${r}" />
+            </select>
+            is available for performance
+            <select th:field="*{selectedPerformance}">
+                <option th:each="p : ${performances}" th:value="${p.id}" th:text="${p.title}" />
+            </select>
+
+            <input type="submit"/></p>
+
+    </form>
+
+    <div th:if="${bean.available}">
+        Seat <span th:text="${bean.seat}">seatNumber</span> is available for this performance - do you want to book it?
+
+        <form action="/booking" method="POST" th:object="${bean}">
+            <input type="hidden" th:field="*{seat}" />
+            <input type="hidden" th:field="*{performance}"/>
+            Customer Name: <input type="text" th:field="*{customerName}" />
+            <input type="submit" value="Book now" />
+        </form>
+    </div>
+
+    <p th:if="${bean.available== false}">
+        I'm sorry - this seat is already booked for this performance. The customer is <span th:text="${bean.booking.customerName}"}></span></a>
+    </p>
+
+</body>
+</html>

+ 13 - 0
src/test/kotlin/com/virtualprogrammers/theater/TheaterApplicationTests.kt

@@ -0,0 +1,13 @@
+package com.virtualprogrammers.theater
+
+import org.junit.jupiter.api.Test
+import org.springframework.boot.test.context.SpringBootTest
+
+@SpringBootTest
+class TheaterApplicationTests {
+
+	@Test
+	fun contextLoads() {
+	}
+
+}