commit
ab752160c9
@ -21,9 +21,9 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<kotlin.version>1.3.40</kotlin.version>
|
<kotlin.version>1.4.10</kotlin.version>
|
||||||
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
|
||||||
<spring.version>2.1.6.RELEASE</spring.version>
|
<spring.version>2.3.3.RELEASE</spring.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -103,11 +103,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>joda-time</groupId>
|
<groupId>joda-time</groupId>
|
||||||
<artifactId>joda-time</artifactId>
|
<artifactId>joda-time</artifactId>
|
||||||
|
<version>2.10.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
<artifactId>jackson-datatype-joda</artifactId>
|
<artifactId>jackson-datatype-joda</artifactId>
|
||||||
<version>2.9.9</version>
|
<version>2.11.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jadira.usertype</groupId>
|
<groupId>org.jadira.usertype</groupId>
|
||||||
@ -117,19 +118,23 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
<artifactId>caffeine</artifactId>
|
<artifactId>caffeine</artifactId>
|
||||||
<version>2.6.2</version>
|
<version>2.8.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
<artifactId>simple-java-mail</artifactId>
|
<artifactId>simple-java-mail</artifactId>
|
||||||
<version>6.0.3</version>
|
<version>6.4.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains.kotlinx</groupId>
|
<groupId>org.jetbrains.kotlinx</groupId>
|
||||||
<artifactId>kotlinx-coroutines-core</artifactId>
|
<artifactId>kotlinx-coroutines-core</artifactId>
|
||||||
<version>1.3.0-M2</version>
|
<version>1.3.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.validation</groupId>
|
||||||
|
<artifactId>validation-api</artifactId>
|
||||||
|
<version>2.0.1.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
@ -93,7 +93,7 @@ data class Observation(
|
|||||||
) {
|
) {
|
||||||
fun toCsvFormat(): String {
|
fun toCsvFormat(): String {
|
||||||
fun escapeSpecialCharacters(data: String): String {
|
fun escapeSpecialCharacters(data: String): String {
|
||||||
return data.replace("\"", "\"\"")
|
return data.replace("\"", "\"\"").replace("\n", "")
|
||||||
}
|
}
|
||||||
val dataPortion = "${date.toString("dd/MM/yyyy")},\"$person, ${type.name} ${scenarios.joinToString { it.title }}\"," +
|
val dataPortion = "${date.toString("dd/MM/yyyy")},\"$person, ${type.name} ${scenarios.joinToString { it.title }}\"," +
|
||||||
"\"Training\",\"Operations - Shift Operations\",\"${site.name}\",\"N/A\"," +
|
"\"Training\",\"Operations - Shift Operations\",\"${site.name}\",\"N/A\"," +
|
||||||
@ -121,7 +121,7 @@ data class Observation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun roundScore(input: Double?): String {
|
private fun roundScore(input: Double?): String {
|
||||||
if (input != null) {
|
if (input != null && input > 0) {
|
||||||
return input.roundToInt().toString()
|
return input.roundToInt().toString()
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
@ -185,7 +185,7 @@ data class Scenario(
|
|||||||
|
|
||||||
) {
|
) {
|
||||||
private fun ratingValid(rating: Byte): Boolean {
|
private fun ratingValid(rating: Byte): Boolean {
|
||||||
return rating in 1..5
|
return rating in 0..5
|
||||||
}
|
}
|
||||||
fun ratingsAllValid(): Boolean {
|
fun ratingsAllValid(): Boolean {
|
||||||
return ratingValid(monitoringRating) &&
|
return ratingValid(monitoringRating) &&
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
package uk.co.neviyn.observationdatabase.controller
|
|
||||||
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.joda.time.LocalDate
|
|
||||||
import org.slf4j.Logger
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
|
||||||
import org.springframework.core.env.Environment
|
|
||||||
import org.springframework.core.env.get
|
|
||||||
import org.springframework.http.HttpStatus
|
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate
|
|
||||||
import org.springframework.web.bind.annotation.CrossOrigin
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
|
||||||
import org.springframework.web.bind.annotation.PathVariable
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
|
||||||
import org.springframework.web.bind.annotation.RestController
|
|
||||||
import org.springframework.web.server.ResponseStatusException
|
|
||||||
import uk.co.neviyn.observationdatabase.Email
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupObservation
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupObservationInit
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupSessionManager
|
|
||||||
import uk.co.neviyn.observationdatabase.Observation
|
|
||||||
import uk.co.neviyn.observationdatabase.ObservationRepository
|
|
||||||
import uk.co.neviyn.observationdatabase.SiteRepository
|
|
||||||
import uk.co.neviyn.observationdatabase.TutorRepository
|
|
||||||
import java.net.Inet4Address
|
|
||||||
import java.net.NetworkInterface
|
|
||||||
import javax.validation.Valid
|
|
||||||
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/api/grpob")
|
|
||||||
@CrossOrigin
|
|
||||||
class GroupSessionController {
|
|
||||||
|
|
||||||
private val logger: Logger = LoggerFactory.getLogger(javaClass)!!
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
lateinit var environment: Environment
|
|
||||||
@Autowired
|
|
||||||
lateinit var siteRepository: SiteRepository
|
|
||||||
@Autowired
|
|
||||||
lateinit var tutorRepository: TutorRepository
|
|
||||||
@Autowired
|
|
||||||
lateinit var observationRepository: ObservationRepository
|
|
||||||
@Autowired
|
|
||||||
lateinit var websocketMessenger: SimpMessagingTemplate
|
|
||||||
@Autowired
|
|
||||||
lateinit var mailer: Email
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a new Group Observation session
|
|
||||||
*/
|
|
||||||
@PostMapping("/start")
|
|
||||||
fun startGroupObservation(@Valid @RequestBody initData: GroupObservationInit): Map<String, String> {
|
|
||||||
val site = siteRepository.findById(initData.site)
|
|
||||||
val tutors = tutorRepository.findAllById(initData.tutors).toSet()
|
|
||||||
if (!site.isPresent) {
|
|
||||||
logger.info("Attempted to add Observation without a site.")
|
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Site required")
|
|
||||||
}
|
|
||||||
if (tutors.isEmpty() || tutors.size != initData.tutors.size) {
|
|
||||||
logger.info("Attempted to add Observation without a tutor")
|
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Tutor required")
|
|
||||||
}
|
|
||||||
val sessionId = GroupSessionManager.startNewSession(site.get(), tutors, initData.type, initData.scenarioTitles)
|
|
||||||
return getConnectionDetails().plus("id" to sessionId.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reconnect to the last Group Observation session
|
|
||||||
*/
|
|
||||||
@GetMapping("/recover")
|
|
||||||
fun reconnectToGroupObservation(): Map<String, Any> {
|
|
||||||
if (GroupSessionManager.isValid()) {
|
|
||||||
logger.info("Previous group observation requested, current id ${GroupSessionManager.sessionId}")
|
|
||||||
return getConnectionDetails().plus(mapOf("id" to GroupSessionManager.sessionId.toString(), "scenarios" to GroupSessionManager.asScenarioView()))
|
|
||||||
}
|
|
||||||
logger.info("Tried to recover a session but no session is active")
|
|
||||||
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "No group session is currently running")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether there is an active group session with [id]
|
|
||||||
*/
|
|
||||||
@GetMapping("/valid/{id}")
|
|
||||||
fun checkGroupObservationValidityById(@PathVariable id: Int): Map<String, Any> {
|
|
||||||
if (GroupSessionManager.isValid(id)) {
|
|
||||||
return mapOf("titles" to GroupSessionManager.scenarioTitles!!)
|
|
||||||
}
|
|
||||||
if (GroupSessionManager.isValid()) {
|
|
||||||
logger.warn("Group observation requested with id $id but id is currently ${GroupSessionManager.sessionId})")
|
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Session ID incorrect")
|
|
||||||
}
|
|
||||||
logger.warn("Group observation requested with id $id but there is no valid session")
|
|
||||||
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "No group session is currently running")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether there is any valid group session
|
|
||||||
*/
|
|
||||||
@GetMapping("/valid")
|
|
||||||
fun checkGroupObservationValidity(): Boolean {
|
|
||||||
return GroupSessionManager.isValid()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save an Observation to the database
|
|
||||||
*/
|
|
||||||
fun saveObservation(observation: Observation): Observation {
|
|
||||||
logger.debug("Saving new Observation to database")
|
|
||||||
val committedObservation = observationRepository.save(observation)
|
|
||||||
logger.debug("Adding Observation data to Tutor records")
|
|
||||||
committedObservation.tutors.forEach {
|
|
||||||
it.observations.add(committedObservation)
|
|
||||||
tutorRepository.save(it)
|
|
||||||
}
|
|
||||||
logger.debug("Observation addition completed")
|
|
||||||
return committedObservation
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current observation data for a user with [name] in the current session.
|
|
||||||
*/
|
|
||||||
@GetMapping("/participant/{name}")
|
|
||||||
fun getParticipantData(@PathVariable name: String): GroupObservation {
|
|
||||||
if (GroupSessionManager.participantExistsInSession(name))
|
|
||||||
return GroupSessionManager.getObservationDataForParticipant(name)!!
|
|
||||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "No participant with the name:'$name'")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Submit an observation to be added to the session state and actual database
|
|
||||||
*/
|
|
||||||
@PostMapping("/submit")
|
|
||||||
fun addGroupObservation(@Valid @RequestBody observationData: GroupObservation) {
|
|
||||||
val titles = observationData.scenarios.map { it.title }
|
|
||||||
if (GroupSessionManager.scenarioTitles!!.size != titles.size || !GroupSessionManager.scenarioTitles!!.containsAll(titles)) {
|
|
||||||
logger.warn("Received scenario data but titles did not match\nInput:$titles\nRequired:${GroupSessionManager.scenarioTitles}")
|
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Submission data contains non-matching title(s)")
|
|
||||||
}
|
|
||||||
GroupSessionManager.updateObservationData(observationData)
|
|
||||||
websocketMessenger.convertAndSend("/ws/scenarios", mapOf("scenarios" to GroupSessionManager.asScenarioView()))
|
|
||||||
}
|
|
||||||
|
|
||||||
@PostMapping("/complete")
|
|
||||||
fun pushObservationsToDatabase(): Map<String, String> {
|
|
||||||
if (GroupSessionManager.isValid() && GroupSessionManager.dataComplete()) {
|
|
||||||
logger.info("Completing session ${GroupSessionManager.sessionId}")
|
|
||||||
val tutors = tutorRepository.findAllById(GroupSessionManager.tutors!!).toSet()
|
|
||||||
val observations = mutableListOf<Observation>()
|
|
||||||
GroupSessionManager.observations.values.forEach { x ->
|
|
||||||
val observation = Observation(
|
|
||||||
site = GroupSessionManager.site!!,
|
|
||||||
date = LocalDate.now(),
|
|
||||||
type = GroupSessionManager.trainingType!!,
|
|
||||||
observed = x.scenarios.joinToString { it.title },
|
|
||||||
monitoring = x.scenarios.map { it.monitoringRating }.average(),
|
|
||||||
conservatism = x.scenarios.map { it.conservatismRating }.average(),
|
|
||||||
controlProcedural = x.scenarios.map { it.controlProceduralRating }.average(),
|
|
||||||
control = x.scenarios.map { it.controlRating }.average(),
|
|
||||||
teamworkCommunications = x.scenarios.map { it.teamworkCommunicationsRating }.average(),
|
|
||||||
teamworkLeadership = x.scenarios.map { it.teamworkLeadershipRating }.average(),
|
|
||||||
teamworkWorkload = x.scenarios.map { it.teamworkWorkloadRating }.average(),
|
|
||||||
knowledge = x.scenarios.map { it.knowledgeRating }.average(),
|
|
||||||
scenarios = x.scenarios,
|
|
||||||
tutors = tutors,
|
|
||||||
person = x.person.toUpperCase()
|
|
||||||
)
|
|
||||||
saveObservation(observation)
|
|
||||||
observations.add(observation)
|
|
||||||
}
|
|
||||||
GroupSessionManager.invalidate()
|
|
||||||
if (this::environment.isInitialized && environment.getProperty("smtp.autosendoncomplete")!!.toBoolean())
|
|
||||||
GlobalScope.launch {
|
|
||||||
if (::mailer.isInitialized)
|
|
||||||
mailer.sendObservationData(observations)
|
|
||||||
else
|
|
||||||
logger.error("Mailer has not been initialized.")
|
|
||||||
}
|
|
||||||
else
|
|
||||||
logger.debug("Environment or Mailer is unavailable")
|
|
||||||
websocketMessenger.convertAndSend("/ws/status", mapOf("status" to "complete"))
|
|
||||||
return mapOf("success" to "The submission was successfully completed.")
|
|
||||||
} else if (!GroupSessionManager.dataComplete()) {
|
|
||||||
logger.info("Tried to complete a session whilst data was incomplete\n${GroupSessionManager.observations}")
|
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Data is incomplete")
|
|
||||||
}
|
|
||||||
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "No valid session")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get details needed to connect to this group session on the LAN
|
|
||||||
*/
|
|
||||||
@GetMapping("/address")
|
|
||||||
fun getConnectionDetails(): Map<String, String> {
|
|
||||||
var ipv4: String? = null
|
|
||||||
var retryCount = 0
|
|
||||||
while (ipv4 == null && retryCount < 3) {
|
|
||||||
ipv4 = NetworkInterface.getNetworkInterfaces().asSequence()
|
|
||||||
.filter { !it.isLoopback }.map { x -> x.inetAddresses.asSequence()
|
|
||||||
.filter { it is Inet4Address }.map { it.hostAddress } }.flatten().firstOrNull()
|
|
||||||
retryCount++
|
|
||||||
Thread.sleep(1_000) // Sleep for 1 second
|
|
||||||
}
|
|
||||||
return if (ipv4 != null && this::environment.isInitialized)
|
|
||||||
mapOf("ip" to ipv4, "port" to environment["local.server.port"]!!)
|
|
||||||
else if (ipv4 == null) {
|
|
||||||
logger.error("IP Address could not be determined")
|
|
||||||
mapOf("error" to "Could not determine IP Address")
|
|
||||||
} else {
|
|
||||||
logger.error("Port could not be determined, environment not initialised")
|
|
||||||
mapOf("error" to "Could not determine port")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +1,11 @@
|
|||||||
package uk.co.neviyn.observationdatabase
|
package uk.co.neviyn.observationdatabase
|
||||||
|
|
||||||
import junit.framework.TestCase.assertFalse
|
|
||||||
import junit.framework.TestCase.assertNotNull
|
import junit.framework.TestCase.assertNotNull
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.springframework.beans.factory.annotation.Autowired
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.test.context.SpringBootTest
|
import org.springframework.boot.test.context.SpringBootTest
|
||||||
import org.springframework.test.context.junit4.SpringRunner
|
import org.springframework.test.context.junit4.SpringRunner
|
||||||
import uk.co.neviyn.observationdatabase.controller.GroupSessionController
|
|
||||||
import uk.co.neviyn.observationdatabase.controller.ObservationsController
|
import uk.co.neviyn.observationdatabase.controller.ObservationsController
|
||||||
|
|
||||||
@RunWith(SpringRunner::class)
|
@RunWith(SpringRunner::class)
|
||||||
@ -16,13 +14,9 @@ class ObservationDatabaseApplicationTests {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
lateinit var observationsController: ObservationsController
|
lateinit var observationsController: ObservationsController
|
||||||
@Autowired
|
|
||||||
lateinit var groupSessionController: GroupSessionController
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun contextLoads() {
|
fun contextLoads() {
|
||||||
assertNotNull(observationsController)
|
assertNotNull(observationsController)
|
||||||
assertNotNull(groupSessionController)
|
|
||||||
assertFalse(GroupSessionManager.isValid())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,167 +0,0 @@
|
|||||||
package uk.co.neviyn.observationdatabase.controller
|
|
||||||
|
|
||||||
import org.joda.time.LocalDate
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mockito.ArgumentMatchers.any
|
|
||||||
import org.mockito.InjectMocks
|
|
||||||
import org.mockito.Mock
|
|
||||||
import org.mockito.Mockito
|
|
||||||
import org.mockito.Mockito.times
|
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner
|
|
||||||
import org.springframework.messaging.simp.SimpMessagingTemplate
|
|
||||||
import org.springframework.web.server.ResponseStatusException
|
|
||||||
import uk.co.neviyn.observationdatabase.Email
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupObservation
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupObservationInit
|
|
||||||
import uk.co.neviyn.observationdatabase.GroupSessionManager
|
|
||||||
import uk.co.neviyn.observationdatabase.Observation
|
|
||||||
import uk.co.neviyn.observationdatabase.ObservationRepository
|
|
||||||
import uk.co.neviyn.observationdatabase.Scenario
|
|
||||||
import uk.co.neviyn.observationdatabase.Site
|
|
||||||
import uk.co.neviyn.observationdatabase.SiteRepository
|
|
||||||
import uk.co.neviyn.observationdatabase.TrainingType
|
|
||||||
import uk.co.neviyn.observationdatabase.Tutor
|
|
||||||
import uk.co.neviyn.observationdatabase.TutorRepository
|
|
||||||
import java.util.Optional
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner::class)
|
|
||||||
class GroupSessionControllerTest {
|
|
||||||
|
|
||||||
@InjectMocks
|
|
||||||
lateinit var controller: GroupSessionController
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
lateinit var websocketMessenger: SimpMessagingTemplate
|
|
||||||
@Mock
|
|
||||||
lateinit var siteRepository: SiteRepository
|
|
||||||
@Mock
|
|
||||||
lateinit var tutorRepository: TutorRepository
|
|
||||||
@Mock
|
|
||||||
lateinit var observationRepository: ObservationRepository
|
|
||||||
@Mock
|
|
||||||
lateinit var mailer: Email
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun tearDown() {
|
|
||||||
GroupSessionManager.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testStartSession() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
assertTrue(GroupSessionManager.isValid())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testStartSession_NoSite() {
|
|
||||||
controller.startGroupObservation(GroupObservationInit(0, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testStartSession_NoTutor() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(0), listOf("Sample title")))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testRecoverSession() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
assertNotNull(controller.reconnectToGroupObservation())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testRecoverSession_NoActiveSession() {
|
|
||||||
controller.reconnectToGroupObservation()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testCompleteSession() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
val tutor = Tutor(1, "Mr X", site)
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(tutor)).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
val person = "A Student"
|
|
||||||
val scenario = Scenario(0, "Sample title", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "")
|
|
||||||
Mockito.doReturn(Observation(1, site, LocalDate.now(), TrainingType.INITIAL, "Sample title", 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, 5.0, listOf(scenario), setOf(tutor), person)).`when`(observationRepository).save(any())
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
controller.addGroupObservation(GroupObservation("A Student", listOf(scenario)))
|
|
||||||
controller.pushObservationsToDatabase()
|
|
||||||
verify(observationRepository, times(1)).save(any())
|
|
||||||
verify(websocketMessenger, times(1)).convertAndSend("/ws/scenarios", mapOf("scenarios" to mapOf("Sample title" to listOf(scenario.copy(title = "A Student")))))
|
|
||||||
assertEquals(1, tutor.observations.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testCompleteSession_NoObservationData() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
controller.pushObservationsToDatabase()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testCompleteSession_PartialObservationData() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
controller.addGroupObservation(GroupObservation("A Student", listOf(Scenario(0, "Sample title", 0, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", ""))))
|
|
||||||
verify(websocketMessenger, times(1)).convertAndSend("/ws/scenarios", mapOf("scenarios" to mapOf("Sample title" to listOf(Scenario(0, "A Student", 0, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "")))))
|
|
||||||
controller.pushObservationsToDatabase()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testSubmit_NonMatchingTitles() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
controller.addGroupObservation(GroupObservation("A Student", listOf(Scenario(0, "Different Title", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", ""))))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testSubmit_PartialData() {
|
|
||||||
val site = Site(1, "Test site")
|
|
||||||
Mockito.doReturn(Optional.of(site)).`when`(siteRepository).findById(1)
|
|
||||||
Mockito.doReturn(listOf(Tutor(1, "Mr X", site))).`when`(tutorRepository).findAllById(listOf(1))
|
|
||||||
controller.startGroupObservation(GroupObservationInit(1, TrainingType.INITIAL, listOf(1), listOf("Sample title")))
|
|
||||||
controller.addGroupObservation(GroupObservation("A Student", listOf(Scenario(0, "Sample title", 0, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", ""))))
|
|
||||||
assertEquals(1, GroupSessionManager.observations.size)
|
|
||||||
assertEquals("A Student", GroupSessionManager.observations.keys.first())
|
|
||||||
assertEquals("A Student", GroupSessionManager.observations.values.first().person)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testGetParticipantData_NoParticipant() {
|
|
||||||
controller.getParticipantData("Someone")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = ResponseStatusException::class)
|
|
||||||
fun testGetParticipantData_WrongName() {
|
|
||||||
val testData = GroupObservation("A Student", listOf(Scenario(0, "Sample title", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "")))
|
|
||||||
GroupSessionManager.observations["A Student"] = testData
|
|
||||||
controller.getParticipantData("Another Student")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGetParticipantData() {
|
|
||||||
val testData = GroupObservation("A Student", listOf(Scenario(0, "Sample title", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "", 5, "", "")))
|
|
||||||
GroupSessionManager.observations["A Student"] = testData
|
|
||||||
val output = controller.getParticipantData("A Student")
|
|
||||||
assertEquals(testData, output)
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,7 +3,7 @@
|
|||||||
<b-row>
|
<b-row>
|
||||||
<b-col cols="3">
|
<b-col cols="3">
|
||||||
<p>{{ description }}</p>
|
<p>{{ description }}</p>
|
||||||
<h4 v-bind:class="{ scorewarn: rating < 3 }">
|
<h4 v-bind:class="{ scorewarn: rating < 3}" v-if="rating > 0">
|
||||||
{{ rating }}
|
{{ rating }}
|
||||||
</h4>
|
</h4>
|
||||||
</b-col>
|
</b-col>
|
||||||
|
@ -3,11 +3,12 @@
|
|||||||
<b-form-radio-group
|
<b-form-radio-group
|
||||||
buttons
|
buttons
|
||||||
button-variant="outline-info"
|
button-variant="outline-info"
|
||||||
size="lg"
|
size="custom"
|
||||||
v-model="propModel"
|
v-model="propModel"
|
||||||
invalid-feedback="Please select a score."
|
invalid-feedback="Please select a score."
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
|
<b-form-radio button-variant="0" value="0"> </b-form-radio>
|
||||||
<b-form-radio button-variant="1" value="1">1</b-form-radio>
|
<b-form-radio button-variant="1" value="1">1</b-form-radio>
|
||||||
<b-form-radio button-variant="2" value="2">2</b-form-radio>
|
<b-form-radio button-variant="2" value="2">2</b-form-radio>
|
||||||
<b-form-radio button-variant="3" value="3">3</b-form-radio>
|
<b-form-radio button-variant="3" value="3">3</b-form-radio>
|
||||||
@ -27,13 +28,16 @@ export default {
|
|||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
this.$emit("newselection", value);
|
this.$emit("newselection", value);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
b-form-radio-group {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
.btn-1 {
|
.btn-1 {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background-color: #cc3232;
|
background-color: #cc3232;
|
||||||
@ -243,4 +247,51 @@ fieldset[disabled] .btn-5.active {
|
|||||||
color: #2dc937;
|
color: #2dc937;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
.btn-0 {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #6c757d;
|
||||||
|
border-color: #000000;
|
||||||
|
}
|
||||||
|
.btn-0:hover,
|
||||||
|
.btn-0:focus,
|
||||||
|
.btn-0:active,
|
||||||
|
.btn-0.active,
|
||||||
|
.open .dropdown-toggle.btn-0 {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #4285f4;
|
||||||
|
border-color: #000000;
|
||||||
|
}
|
||||||
|
.btn-0:active,
|
||||||
|
.btn-0.active,
|
||||||
|
.open .dropdown-toggle.btn-0 {
|
||||||
|
background-image: none;
|
||||||
|
}
|
||||||
|
.btn-0.disabled,
|
||||||
|
.btn-0[disabled],
|
||||||
|
fieldset[disabled] .btn-0,
|
||||||
|
.btn-0.disabled:hover,
|
||||||
|
.btn-0[disabled]:hover,
|
||||||
|
fieldset[disabled] .btn-0:hover,
|
||||||
|
.btn-0.disabled:focus,
|
||||||
|
.btn-0[disabled]:focus,
|
||||||
|
fieldset[disabled] .btn-0:focus,
|
||||||
|
.btn-0.disabled:active,
|
||||||
|
.btn-0[disabled]:active,
|
||||||
|
fieldset[disabled] .btn-0:active,
|
||||||
|
.btn-0.disabled.active,
|
||||||
|
.btn-0[disabled].active,
|
||||||
|
fieldset[disabled] .btn-0.active {
|
||||||
|
background-color: #6c757d;
|
||||||
|
border-color: #4285f4;
|
||||||
|
}
|
||||||
|
.btn-0 .badge {
|
||||||
|
color: #6c757d;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-custom {
|
||||||
|
padding: 10px 13px;
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -9,8 +9,6 @@ const ViewObservations = () => import("./views/ViewObservations.vue");
|
|||||||
const ObservationComplete = () => import("./views/ObservationComplete.vue");
|
const ObservationComplete = () => import("./views/ObservationComplete.vue");
|
||||||
const DBError = () => import("./views/DatabaseUnavailable.vue");
|
const DBError = () => import("./views/DatabaseUnavailable.vue");
|
||||||
const About = () => import("./views/About.vue");
|
const About = () => import("./views/About.vue");
|
||||||
const GroupSession = () => import("./views/GroupSession.vue");
|
|
||||||
const GroupSessionInput = () => import("./views/GroupSessionInput.vue");
|
|
||||||
|
|
||||||
Vue.use(Router);
|
Vue.use(Router);
|
||||||
|
|
||||||
@ -60,17 +58,6 @@ export default new Router({
|
|||||||
path: "/about",
|
path: "/about",
|
||||||
name: "about",
|
name: "about",
|
||||||
component: About
|
component: About
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/groupsession",
|
|
||||||
name: "groupsession",
|
|
||||||
component: GroupSession
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/groupsession/:id",
|
|
||||||
name: "groupsessioninput",
|
|
||||||
component: GroupSessionInput,
|
|
||||||
props: true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -1,375 +0,0 @@
|
|||||||
<template>
|
|
||||||
<b-container fluid>
|
|
||||||
<b-row v-if="active">
|
|
||||||
<b-col cols="12" md="3">
|
|
||||||
<vue-qrcode v-model="qrdata" :options="{ width: 250 }"></vue-qrcode>
|
|
||||||
<p>
|
|
||||||
Scan the code or navigate to
|
|
||||||
<br />
|
|
||||||
{{ qrdata }}
|
|
||||||
</p>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="9">
|
|
||||||
<b-card-group v-for="(item, index) in data" v-bind:key="index">
|
|
||||||
<b-card
|
|
||||||
border-variant="secondary"
|
|
||||||
header-border-variant="secondary"
|
|
||||||
:header="index"
|
|
||||||
>
|
|
||||||
<b-card-text>
|
|
||||||
<b-table :fields="tableFields" :items="item"></b-table>
|
|
||||||
</b-card-text>
|
|
||||||
</b-card>
|
|
||||||
</b-card-group>
|
|
||||||
</b-col>
|
|
||||||
<b-col>
|
|
||||||
<b-button size="lg" variant="primary" v-on:click="showCompletionModal()"
|
|
||||||
>Finish Session</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row v-else-if="complete">
|
|
||||||
<b-col>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h2>Session Complete</h2>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<p>
|
|
||||||
Observation data for this session is now saved in the database.
|
|
||||||
</p>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-button
|
|
||||||
size="lg"
|
|
||||||
variant="primary"
|
|
||||||
v-on:click="window.location.reload()"
|
|
||||||
>Start a New Session</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row v-else>
|
|
||||||
<b-col>
|
|
||||||
<b-form id="submission-form" novalidate @submit="onSubmit">
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-form-group label="Site">
|
|
||||||
<b-form-select
|
|
||||||
v-model="site"
|
|
||||||
:options="siteOptions"
|
|
||||||
style="text-align:center;"
|
|
||||||
required
|
|
||||||
></b-form-select>
|
|
||||||
</b-form-group>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-form-group label="Type">
|
|
||||||
<b-form-select
|
|
||||||
v-model="type"
|
|
||||||
style="text-align:center;"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option :value="null">Select a training type</option>
|
|
||||||
<option value="INITIAL">INITIAL</option>
|
|
||||||
<option value="CONTINUING">CONTINUING</option>
|
|
||||||
</b-form-select>
|
|
||||||
</b-form-group>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-form-group label="Tutor(s)">
|
|
||||||
<p v-if="site == null">Select a site first.</p>
|
|
||||||
<b-form-checkbox-group
|
|
||||||
v-model="tutors"
|
|
||||||
:options="tutorOptions"
|
|
||||||
></b-form-checkbox-group>
|
|
||||||
</b-form-group>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row
|
|
||||||
v-for="(item, index) in scenarioTitles"
|
|
||||||
v-bind:key="index"
|
|
||||||
class="border bottom-buffer"
|
|
||||||
fluid
|
|
||||||
>
|
|
||||||
<b-col>
|
|
||||||
<b-form-input
|
|
||||||
v-model="item.data"
|
|
||||||
type="text"
|
|
||||||
placeholder="Enter scenario description."
|
|
||||||
></b-form-input>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="1">
|
|
||||||
<b-button
|
|
||||||
v-on:click="scenarioTitles.splice(index, 1)"
|
|
||||||
variant="danger"
|
|
||||||
>
|
|
||||||
<b>Delete</b>
|
|
||||||
</b-button>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-button
|
|
||||||
v-on:click="scenarioTitles.push({ data: '' })"
|
|
||||||
size="lg"
|
|
||||||
variant="primary"
|
|
||||||
>Add Another Scenario</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<br />
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-button type="submit" size="lg" variant="primary"
|
|
||||||
>Start</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
<b-col>
|
|
||||||
<b-button
|
|
||||||
size="lg"
|
|
||||||
variant="secondary"
|
|
||||||
v-on:click="connectToPrevious()"
|
|
||||||
>Connect to Previous Session</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-form>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-modal
|
|
||||||
id="submissionModal"
|
|
||||||
ref="submissionModal"
|
|
||||||
title="Enter password to confirm session start"
|
|
||||||
@ok="handleOk"
|
|
||||||
@shown="clearPassword"
|
|
||||||
>
|
|
||||||
<form @submit.stop.prevent="handleSubmit">
|
|
||||||
<b-form-input
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter password"
|
|
||||||
v-model="submitPassword"
|
|
||||||
></b-form-input>
|
|
||||||
</form>
|
|
||||||
</b-modal>
|
|
||||||
<b-modal
|
|
||||||
id="completionModal"
|
|
||||||
ref="completionModal"
|
|
||||||
title="Enter password to confirm submission"
|
|
||||||
@ok="handleSubmitComplete"
|
|
||||||
@shown="clearPassword"
|
|
||||||
>
|
|
||||||
<form @submit.stop.prevent="handleSubmit">
|
|
||||||
<b-form-input
|
|
||||||
type="password"
|
|
||||||
placeholder="Enter password"
|
|
||||||
v-model="submitPassword"
|
|
||||||
></b-form-input>
|
|
||||||
</form>
|
|
||||||
</b-modal>
|
|
||||||
</b-container>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Vue from "vue";
|
|
||||||
import VueQrcode from "@chenfengyuan/vue-qrcode";
|
|
||||||
import webstomp from "webstomp-client";
|
|
||||||
import SockJS from "sockjs-client";
|
|
||||||
export default {
|
|
||||||
name: "groupsession",
|
|
||||||
title: "Group Session",
|
|
||||||
components: { VueQrcode },
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
active: false,
|
|
||||||
stompclient: null,
|
|
||||||
complete: false,
|
|
||||||
qrdata: "N/A",
|
|
||||||
data: [{ person: { name: "No data yet received." } }],
|
|
||||||
site: null,
|
|
||||||
tutors: null,
|
|
||||||
siteOptions: [],
|
|
||||||
tutorOptions: [],
|
|
||||||
scenarioTitles: [{ data: "" }, { data: "" }, { data: "" }],
|
|
||||||
type: null,
|
|
||||||
submitPassword: null,
|
|
||||||
tableFields: [
|
|
||||||
{ key: "title", label: "Name" },
|
|
||||||
{ key: "monitoring.rating", label: "Monitoring" },
|
|
||||||
{ key: "controlProcedural.rating", label: "Control Procedural" },
|
|
||||||
{ key: "control.rating", label: "Control" },
|
|
||||||
{ key: "conservatism.rating", label: "Conservatism" },
|
|
||||||
{
|
|
||||||
key: "teamworkCommunications.rating",
|
|
||||||
label: "Teamwork Communications"
|
|
||||||
},
|
|
||||||
{ key: "teamworkLeadership.rating", label: "Teamwork Leadership" },
|
|
||||||
{ key: "teamworkWorkload.rating", label: "Teamwork Workload" },
|
|
||||||
{ key: "knowledge.rating", label: "Knowledge" }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
site: function() {
|
|
||||||
this.tutorOptions = [];
|
|
||||||
this.getTutors();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
Vue.axios
|
|
||||||
.get("/site")
|
|
||||||
.then(response => {
|
|
||||||
this.siteOptions = response.data;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.response.status === 404) {
|
|
||||||
//this.$router.push("/dberror");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
startSession: function() {
|
|
||||||
var self = this;
|
|
||||||
let axiosConfig = {
|
|
||||||
auth: {
|
|
||||||
username: "admin",
|
|
||||||
password: this.submitPassword
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Vue.axios
|
|
||||||
.post(
|
|
||||||
"/grpob/start",
|
|
||||||
{
|
|
||||||
site: self.site,
|
|
||||||
tutors: self.tutors,
|
|
||||||
scenarioTitles: self.scenarioTitles.map(x => x.data),
|
|
||||||
type: self.type
|
|
||||||
},
|
|
||||||
axiosConfig
|
|
||||||
)
|
|
||||||
.then(function(response) {
|
|
||||||
if (!("error" in response.data)) {
|
|
||||||
self.setupSession(response.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function() {
|
|
||||||
self.active = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
connectToPrevious: function() {
|
|
||||||
var self = this;
|
|
||||||
Vue.axios
|
|
||||||
.get("/grpob/recover")
|
|
||||||
.then(function(response) {
|
|
||||||
if (!("error" in response.data)) {
|
|
||||||
self.setupSession(response.data);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
if (error.response.status === 404) {
|
|
||||||
this.$router.push("/dberror");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setupSession: function(rdata) {
|
|
||||||
var self = this;
|
|
||||||
self.qrdata = `http://${rdata.ip}:${rdata.port}/#/groupsession/${
|
|
||||||
rdata.id
|
|
||||||
}`;
|
|
||||||
self.active = true;
|
|
||||||
self.stompclient = webstomp.over(
|
|
||||||
new SockJS("http://127.0.0.1:8080/websocket", {
|
|
||||||
heartbeat: false
|
|
||||||
})
|
|
||||||
);
|
|
||||||
self.stompclient.connect([], function() {
|
|
||||||
self.stompclient.subscribe("/ws/scenarios", function(incomingData) {
|
|
||||||
self.data = JSON.parse(incomingData.body).scenarios;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
self.$refs.submissionModal.hide();
|
|
||||||
self.clearPassword();
|
|
||||||
},
|
|
||||||
getTutors: function() {
|
|
||||||
if (this.site != null) {
|
|
||||||
Vue.axios
|
|
||||||
.get("/site/" + this.site + "/tutors")
|
|
||||||
.then(response => {
|
|
||||||
this.tutorOptions = response.data;
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
if (error.response.status === 404) {
|
|
||||||
this.$router.push("/dberror");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSubmit: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
var form = document.getElementById("submission-form");
|
|
||||||
if (form.checkValidity()) {
|
|
||||||
this.showModal();
|
|
||||||
}
|
|
||||||
form.classList.add("was-validated");
|
|
||||||
},
|
|
||||||
showModal() {
|
|
||||||
this.$refs.submissionModal.show();
|
|
||||||
},
|
|
||||||
showCompletionModal() {
|
|
||||||
this.$refs.completionModal.show();
|
|
||||||
},
|
|
||||||
clearPassword() {
|
|
||||||
this.submitPassword = null;
|
|
||||||
},
|
|
||||||
handleOk(evt) {
|
|
||||||
// Prevent modal from closing
|
|
||||||
evt.preventDefault();
|
|
||||||
if (this.submitPassword !== null) {
|
|
||||||
this.startSession();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleSubmitComplete(evt) {
|
|
||||||
// Prevent modal from closing
|
|
||||||
var self = this;
|
|
||||||
evt.preventDefault();
|
|
||||||
if (this.submitPassword !== null) {
|
|
||||||
let axiosConfig = {
|
|
||||||
auth: {
|
|
||||||
username: "admin",
|
|
||||||
password: this.submitPassword
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Vue.axios
|
|
||||||
.post("/grpob/complete", {}, axiosConfig)
|
|
||||||
.then(function(response) {
|
|
||||||
if ("success" in response.data) {
|
|
||||||
self.complete = true;
|
|
||||||
self.active = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (this.stompclient != null) {
|
|
||||||
this.stompclient.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
@ -1,462 +0,0 @@
|
|||||||
<template>
|
|
||||||
<b-container fluid>
|
|
||||||
<b-container v-if="error !== null">
|
|
||||||
<p>An error has occurred.</p>
|
|
||||||
<p>{{ error.status }}</p>
|
|
||||||
<p>{{ error.data }}</p>
|
|
||||||
</b-container>
|
|
||||||
<b-container v-else-if="complete">
|
|
||||||
<h2>Submission Complete</h2>
|
|
||||||
<p>Thank you.</p>
|
|
||||||
<p>
|
|
||||||
This observation session is now closed and your data submitted to the
|
|
||||||
database.
|
|
||||||
</p>
|
|
||||||
</b-container>
|
|
||||||
<b-container v-else-if="!valid">
|
|
||||||
<p>Getting session data from server</p>
|
|
||||||
</b-container>
|
|
||||||
<b-container v-else-if="scenarios.length === 0">
|
|
||||||
<p>
|
|
||||||
No scenarios defined for this session, please setup a new group session
|
|
||||||
</p>
|
|
||||||
</b-container>
|
|
||||||
<b-container v-else fluid>
|
|
||||||
<b-form>
|
|
||||||
<b-row align-h="center">
|
|
||||||
<b-col>
|
|
||||||
<b-form-group label="Participant">
|
|
||||||
<b-form-input
|
|
||||||
v-model="participant"
|
|
||||||
type="text"
|
|
||||||
style="text-align:center;"
|
|
||||||
placeholder="Enter your name (Initial and Surname)"
|
|
||||||
required
|
|
||||||
></b-form-input>
|
|
||||||
</b-form-group>
|
|
||||||
</b-col>
|
|
||||||
<b-col>
|
|
||||||
<b-button v-on:click="attemptReconnect()">Reconnect </b-button>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row
|
|
||||||
v-for="(item, index) in scenarios"
|
|
||||||
v-bind:key="index"
|
|
||||||
class="border bottom-buffer"
|
|
||||||
>
|
|
||||||
<b-col>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-input
|
|
||||||
v-model="item.title"
|
|
||||||
type="text"
|
|
||||||
readonly
|
|
||||||
></b-form-input>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Monitoring</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.monitoringRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.monitoringRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.monitoringStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.monitoringImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Control Procedural</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.controlProceduralRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.controlProceduralRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlProceduralStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlProceduralImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Control</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.controlRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.controlRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Conservatism</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.conservatismRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.conservatismRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.conservatismStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.conservatismImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Teamwork Communications</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkCommunicationsRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkCommunicationsRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkCommunicationsStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkCommunicationsImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Teamwork Leadership</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkLeadershipRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkLeadershipRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkLeadershipStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkLeadershipImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Teamwork Workload</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkWorkloadRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkWorkloadRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkWorkloadStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkWorkloadImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" md="6" xl="3" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h5>Knowledge</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.knowledgeRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.knowledgeRating = $event;
|
|
||||||
actuallySubmit();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.knowledgeStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.knowledgeImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<br />
|
|
||||||
<b-button variant="primary" v-on:click="actuallySubmit()"
|
|
||||||
>Update</b-button
|
|
||||||
>
|
|
||||||
</b-form>
|
|
||||||
</b-container>
|
|
||||||
</b-container>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import Vue from "vue";
|
|
||||||
import ScoreSelector from "../components/ScoreSelector.vue";
|
|
||||||
import webstomp from "webstomp-client";
|
|
||||||
import SockJS from "sockjs-client";
|
|
||||||
export default {
|
|
||||||
name: "groupsessioninput",
|
|
||||||
title: "Group Session - Input",
|
|
||||||
props: ["id"],
|
|
||||||
components: { ScoreSelector },
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
scenarios: [],
|
|
||||||
participant: null,
|
|
||||||
valid: false,
|
|
||||||
complete: false,
|
|
||||||
error: null,
|
|
||||||
stompclient: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
let self = this;
|
|
||||||
if (this.id != null) {
|
|
||||||
Vue.axios
|
|
||||||
.get(`/grpob/valid/${this.id}`)
|
|
||||||
.then(function(response) {
|
|
||||||
if (response.data.titles != null) {
|
|
||||||
response.data.titles.forEach(function(x) {
|
|
||||||
self.addAnotherObservation(x);
|
|
||||||
});
|
|
||||||
self.valid = true;
|
|
||||||
self.setupSession();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(function(error) {
|
|
||||||
if (error.response.status === 404) {
|
|
||||||
self.$router.push("/dberror");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.error = error.response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addAnotherObservation: function(newTitle) {
|
|
||||||
this.scenarios.push({
|
|
||||||
title: newTitle,
|
|
||||||
monitoringRating: 0,
|
|
||||||
monitoringStrengths: "",
|
|
||||||
monitoringImprovements: "",
|
|
||||||
controlProceduralRating: 0,
|
|
||||||
controlProceduralStrengths: "",
|
|
||||||
controlProceduralImprovements: "",
|
|
||||||
controlRating: 0,
|
|
||||||
controlStrengths: "",
|
|
||||||
controlImprovements: "",
|
|
||||||
conservatismRating: 0,
|
|
||||||
conservatismStrengths: "",
|
|
||||||
conservatismImprovements: "",
|
|
||||||
teamworkCommunicationsRating: 0,
|
|
||||||
teamworkCommunicationsStrengths: "",
|
|
||||||
teamworkCommunicationsImprovements: "",
|
|
||||||
teamworkLeadershipRating: 0,
|
|
||||||
teamworkLeadershipStrengths: "",
|
|
||||||
teamworkLeadershipImprovements: "",
|
|
||||||
teamworkWorkloadRating: 0,
|
|
||||||
teamworkWorkloadStrengths: "",
|
|
||||||
teamworkWorkloadImprovements: "",
|
|
||||||
knowledgeRating: 0,
|
|
||||||
knowledgeStrengths: "",
|
|
||||||
knowledgeImprovements: ""
|
|
||||||
});
|
|
||||||
},
|
|
||||||
actuallySubmit() {
|
|
||||||
var self = this;
|
|
||||||
var payload = {
|
|
||||||
person: self.participant,
|
|
||||||
scenarios: self.scenarios
|
|
||||||
};
|
|
||||||
Vue.axios.post("/grpob/submit", payload).catch(function(error) {
|
|
||||||
self.error = error;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
setupSession: function() {
|
|
||||||
var self = this;
|
|
||||||
self.stompclient = webstomp.over(
|
|
||||||
new SockJS(`http://${window.location.host}/websocket`, {
|
|
||||||
heartbeat: false
|
|
||||||
})
|
|
||||||
);
|
|
||||||
self.stompclient.connect([], function() {
|
|
||||||
self.stompclient.subscribe("/ws/status", function(incomingData) {
|
|
||||||
var data = JSON.parse(incomingData.body);
|
|
||||||
if (data.status === "complete") {
|
|
||||||
self.complete = true;
|
|
||||||
self.stompclient.disconnect();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
attemptReconnect: function() {
|
|
||||||
var self = this;
|
|
||||||
Vue.axios
|
|
||||||
.get(`/grpob/participant/${self.participant}`)
|
|
||||||
.then(function(response) {
|
|
||||||
self.scenarios = response.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.strength {
|
|
||||||
background-color: honeydew;
|
|
||||||
}
|
|
||||||
.afi {
|
|
||||||
background-color: mistyrose;
|
|
||||||
}
|
|
||||||
.bottom-buffer {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -27,15 +27,6 @@
|
|||||||
>
|
>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<!--
|
|
||||||
<b-row class="my-3" align-h="center">
|
|
||||||
<b-col lg="6" sm="12">
|
|
||||||
<b-button class="scale-in-center" size="lg" to="/groupsession" block
|
|
||||||
><v-icon name="users" /> Start a Group Session</b-button
|
|
||||||
>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
-->
|
|
||||||
</b-container>
|
</b-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,125 +1,121 @@
|
|||||||
<template>
|
<template>
|
||||||
<b-container fluid>
|
<b-container fluid>
|
||||||
<b-container
|
<b-container v-if="type != null && whom != null && site != null && tutors != null" fluid style="padding-left: 130px;">
|
||||||
v-if="type != null && whom != null && site != null && tutors != null"
|
|
||||||
fluid
|
|
||||||
style="padding-left: 130px;"
|
|
||||||
>
|
|
||||||
<h3>
|
<h3>
|
||||||
<v-icon name="tag" scale="1.5" />
|
<v-icon name="tag" scale="1.5" />
|
||||||
{{ type }} / {{ whom }}
|
{{ type }} / {{ whom }}
|
||||||
</h3>
|
</h3>
|
||||||
<b-container class="sidebar">
|
<b-container class="sidebar">
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[0] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Monitoring.svg"
|
src="../assets/Monitoring.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[0] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[0] < warningBound && totals[0] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[0] }}</div>
|
<div class="image-centered-text" v-if="totals[0] > 0">
|
||||||
|
{{ totals[0] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[1] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Control.svg"
|
src="../assets/Control.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[1] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[1] < warningBound && totals[1] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[1] }}</div>
|
<div class="image-centered-text" v-if="totals[1] > 0">
|
||||||
|
{{ totals[1] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[2] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Control.svg"
|
src="../assets/Control.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[2] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[2] < warningBound && totals[2] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[2] }}</div>
|
<div class="image-centered-text" v-if="totals[2] > 0">
|
||||||
|
{{ totals[2] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[3] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Conservatism.svg"
|
src="../assets/Conservatism.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[3] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[3] < warningBound && totals[3] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[3] }}</div>
|
<div class="image-centered-text" v-if="totals[3] > 0">
|
||||||
|
{{ totals[3] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[4] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[4] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[4] < warningBound && totals[4] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[4] }}</div>
|
<div class="image-centered-text" v-if="totals[4] > 0">
|
||||||
|
{{ totals[4] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[5] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[5] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[5] < warningBound && totals[5] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[5] }}</div>
|
<div class="image-centered-text" v-if="totals[5] > 0">
|
||||||
|
{{ totals[5] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[6] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[6] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[6] < warningBound && totals[6] > 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[6] }}</div>
|
<div class="image-centered-text" v-if="totals[6] > 0">
|
||||||
|
{{ totals[6] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row
|
<b-row align-v="center" class="sidebar-vert-padding">
|
||||||
align-v="center"
|
|
||||||
class="sidebar-vert-padding"
|
|
||||||
v-if="totals[7] > 0"
|
|
||||||
>
|
|
||||||
<b-col class="centered-image">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Knowledge.svg"
|
src="../assets/Knowledge.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: totals[7] < warningBound }"
|
v-bind:class="{
|
||||||
|
scorewarning: totals[7] < warningBound && totals[7] != 0,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">{{ totals[7] }}</div>
|
<div class="image-centered-text" v-if="totals[7] > 0">
|
||||||
|
{{ totals[7] }}
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
@ -140,27 +136,49 @@
|
|||||||
></b-form-input>
|
></b-form-input>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="1">
|
<b-col cols="1">
|
||||||
<b-button v-on:click="deleteObservation(index)" variant="danger"
|
<b-button
|
||||||
|
v-on:click="deleteObservation(index)"
|
||||||
|
variant="outline-danger"
|
||||||
><b>Delete</b></b-button
|
><b>Delete</b></b-button
|
||||||
>
|
>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row>
|
<b-row cols="2">
|
||||||
<b-col cols="6" class="border">
|
<div
|
||||||
|
v-for="(entry, entryIndex) in item.entries"
|
||||||
|
v-bind:key="entryIndex"
|
||||||
|
>
|
||||||
|
<b-col class="border">
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col cols="4">
|
<b-col cols="4">
|
||||||
<h5>Monitoring</h5>
|
<b-form-select
|
||||||
|
v-model="entry.type"
|
||||||
|
:options="entryTypeOptions"
|
||||||
|
v-on:change="updateTotals()"
|
||||||
|
class="my-3"
|
||||||
|
></b-form-select>
|
||||||
|
<div class="d-flex">
|
||||||
|
<b-button
|
||||||
|
class="mr-2"
|
||||||
|
variant="outline-danger"
|
||||||
|
size="sm"
|
||||||
|
v-on:click="deleteEntry(index, entryIndex)"
|
||||||
|
v-b-tooltip.hover
|
||||||
|
title="Delete this entry"
|
||||||
|
>X</b-button
|
||||||
|
>
|
||||||
<score-selector
|
<score-selector
|
||||||
:score-value="item.monitoringRating"
|
:score-value="entry.rating"
|
||||||
v-on:newselection="
|
v-on:newselection="
|
||||||
item.monitoringRating = $event;
|
entry.rating = $event;
|
||||||
updateTotals();
|
updateTotals();
|
||||||
"
|
"
|
||||||
></score-selector>
|
></score-selector>
|
||||||
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="8">
|
<b-col cols="8">
|
||||||
<b-form-textarea
|
<b-form-textarea
|
||||||
v-model="item.monitoringStrengths"
|
v-model="entry.strengths"
|
||||||
placeholder="Strengths"
|
placeholder="Strengths"
|
||||||
:rows="1"
|
:rows="1"
|
||||||
:max-rows="2"
|
:max-rows="2"
|
||||||
@ -168,7 +186,7 @@
|
|||||||
class="strength"
|
class="strength"
|
||||||
></b-form-textarea>
|
></b-form-textarea>
|
||||||
<b-form-textarea
|
<b-form-textarea
|
||||||
v-model="item.monitoringImprovements"
|
v-model="entry.AFIs"
|
||||||
placeholder="AFIs"
|
placeholder="AFIs"
|
||||||
:rows="1"
|
:rows="1"
|
||||||
:max-rows="2"
|
:max-rows="2"
|
||||||
@ -178,235 +196,15 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="6" class="border">
|
</div>
|
||||||
<b-row>
|
<b-col cols="6" align-self="center">
|
||||||
<b-col cols="4">
|
<b-button
|
||||||
<h5>Control Procedural</h5>
|
v-on:click="addAnotherEntry(item)"
|
||||||
<score-selector
|
size="lg"
|
||||||
:score-value="item.controlProceduralRating"
|
style="font-size: 200%"
|
||||||
v-on:newselection="
|
class="py-4 my-4"
|
||||||
item.controlProceduralRating = $event;
|
> + </b-button
|
||||||
updateTotals();
|
>
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlProceduralStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlProceduralImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Control</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.controlRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.controlRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.controlImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Conservatism</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.conservatismRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.conservatismRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.conservatismStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.conservatismImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Teamwork Communications</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkCommunicationsRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkCommunicationsRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkCommunicationsStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkCommunicationsImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Teamwork Leadership</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkLeadershipRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkLeadershipRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkLeadershipStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkLeadershipImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Teamwork Workload</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.teamworkWorkloadRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.teamworkWorkloadRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkWorkloadStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.teamworkWorkloadImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="6" class="border">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="4">
|
|
||||||
<h5>Knowledge</h5>
|
|
||||||
<score-selector
|
|
||||||
:score-value="item.knowledgeRating"
|
|
||||||
v-on:newselection="
|
|
||||||
item.knowledgeRating = $event;
|
|
||||||
updateTotals();
|
|
||||||
"
|
|
||||||
></score-selector>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="8">
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.knowledgeStrengths"
|
|
||||||
placeholder="Strengths"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="strength"
|
|
||||||
></b-form-textarea>
|
|
||||||
<b-form-textarea
|
|
||||||
v-model="item.knowledgeImprovements"
|
|
||||||
placeholder="AFIs"
|
|
||||||
:rows="1"
|
|
||||||
:max-rows="2"
|
|
||||||
no-resize
|
|
||||||
class="afi"
|
|
||||||
></b-form-textarea>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
@ -479,68 +277,46 @@ export default {
|
|||||||
scenarios: [
|
scenarios: [
|
||||||
{
|
{
|
||||||
title: "",
|
title: "",
|
||||||
monitoringRating: null,
|
entries: [
|
||||||
monitoringStrengths: "",
|
{
|
||||||
monitoringImprovements: "",
|
type: null,
|
||||||
controlProceduralRating: null,
|
rating: 0,
|
||||||
controlProceduralStrengths: "",
|
strengths: "",
|
||||||
controlProceduralImprovements: "",
|
AFIs: "",
|
||||||
controlRating: null,
|
},
|
||||||
controlStrengths: "",
|
],
|
||||||
controlImprovements: "",
|
},
|
||||||
conservatismRating: null,
|
|
||||||
conservatismStrengths: "",
|
|
||||||
conservatismImprovements: "",
|
|
||||||
teamworkCommunicationsRating: null,
|
|
||||||
teamworkCommunicationsStrengths: "",
|
|
||||||
teamworkCommunicationsImprovements: "",
|
|
||||||
teamworkLeadershipRating: null,
|
|
||||||
teamworkLeadershipStrengths: "",
|
|
||||||
teamworkLeadershipImprovements: "",
|
|
||||||
teamworkWorkloadRating: null,
|
|
||||||
teamworkWorkloadStrengths: "",
|
|
||||||
teamworkWorkloadImprovements: "",
|
|
||||||
knowledgeRating: null,
|
|
||||||
knowledgeStrengths: "",
|
|
||||||
knowledgeImprovements: ""
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
totals: [0, 0, 0, 0, 0, 0, 0, 0],
|
totals: [0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
warningBound: 2.5,
|
warningBound: 2.5,
|
||||||
submitPassword: null
|
submitPassword: null,
|
||||||
|
entryTypeOptions: [
|
||||||
|
{ value: "monitoring", text: "Monitoring" },
|
||||||
|
{ value: "controlProcedural", text: "Control Procedural" },
|
||||||
|
{ value: "control", text: "Control" },
|
||||||
|
{ value: "conservatism", text: "Conservatism" },
|
||||||
|
{ value: "teamworkCommunications", text: "Teamwork Communications" },
|
||||||
|
{ value: "teamworkLeadership", text: "Teamwork Leadership" },
|
||||||
|
{ value: "teamworkWorkload", text: "Teamwork Workload" },
|
||||||
|
{ value: "knowledge", text: "Knowledge" },
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(["type", "whom", "site", "tutors"])
|
...mapState(["type", "whom", "site", "tutors"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
addAnotherObservation: function() {
|
addAnotherObservation: function() {
|
||||||
this.scenarios.push({
|
this.scenarios.push({
|
||||||
title: "",
|
title: "",
|
||||||
monitoringRating: null,
|
entries: [
|
||||||
monitoringStrengths: "",
|
{
|
||||||
monitoringImprovements: "",
|
type: null,
|
||||||
controlProceduralRating: null,
|
rating: 0,
|
||||||
controlProceduralStrengths: "",
|
strengths: "",
|
||||||
controlProceduralImprovements: "",
|
AFIs: "",
|
||||||
controlRating: null,
|
},
|
||||||
controlStrengths: "",
|
],
|
||||||
controlImprovements: "",
|
|
||||||
conservatismRating: null,
|
|
||||||
conservatismStrengths: "",
|
|
||||||
conservatismImprovements: "",
|
|
||||||
teamworkCommunicationsRating: null,
|
|
||||||
teamworkCommunicationsStrengths: "",
|
|
||||||
teamworkCommunicationsImprovements: "",
|
|
||||||
teamworkLeadershipRating: null,
|
|
||||||
teamworkLeadershipStrengths: "",
|
|
||||||
teamworkLeadershipImprovements: "",
|
|
||||||
teamworkWorkloadRating: null,
|
|
||||||
teamworkWorkloadStrengths: "",
|
|
||||||
teamworkWorkloadImprovements: "",
|
|
||||||
knowledgeRating: null,
|
|
||||||
knowledgeStrengths: "",
|
|
||||||
knowledgeImprovements: ""
|
|
||||||
});
|
});
|
||||||
Vue.nextTick(function() {
|
Vue.nextTick(function() {
|
||||||
window.scrollTo(
|
window.scrollTo(
|
||||||
@ -556,43 +332,42 @@ export default {
|
|||||||
}
|
}
|
||||||
this.updateTotals();
|
this.updateTotals();
|
||||||
},
|
},
|
||||||
|
addAnotherEntry: function(scenario) {
|
||||||
|
scenario.entries.push({
|
||||||
|
type: null,
|
||||||
|
rating: 0,
|
||||||
|
strengths: "",
|
||||||
|
AFIs: "",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteEntry: function(scenarioIndex, entryIndex) {
|
||||||
|
this.$delete(this.scenarios[scenarioIndex].entries, entryIndex);
|
||||||
|
if (this.scenarios[scenarioIndex].entries.length === 0) {
|
||||||
|
this.addAnotherEntry(this.scenarios[scenarioIndex]);
|
||||||
|
}
|
||||||
|
this.updateTotals();
|
||||||
|
},
|
||||||
updateTotals: function() {
|
updateTotals: function() {
|
||||||
var iTotals = [0, 0, 0, 0, 0, 0, 0, 0];
|
var iTotals = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
var counts = [0, 0, 0, 0, 0, 0, 0, 0];
|
var counts = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||||
this.scenarios.forEach(function(element) {
|
var indices = {
|
||||||
if (element.monitoringRating) {
|
monitoring: 0,
|
||||||
iTotals[0] += parseInt(element.monitoringRating);
|
controlProcedural: 1,
|
||||||
counts[0] += 1;
|
control: 2,
|
||||||
}
|
conservatism: 3,
|
||||||
if (element.controlProceduralRating) {
|
teamworkCommunications: 4,
|
||||||
iTotals[1] += parseInt(element.controlProceduralRating);
|
teamworkLeadership: 5,
|
||||||
counts[1] += 1;
|
teamworkWorkload: 6,
|
||||||
}
|
knowledge: 7,
|
||||||
if (element.controlRating) {
|
};
|
||||||
iTotals[2] += parseInt(element.controlRating);
|
this.scenarios.forEach(function(scenario) {
|
||||||
counts[2] += 1;
|
scenario.entries.forEach(function(entry) {
|
||||||
}
|
if (entry.type !== null && entry.rating > 0) {
|
||||||
if (element.conservatismRating) {
|
iTotals[indices[entry.type]] += parseInt(entry.rating);
|
||||||
iTotals[3] += parseInt(element.conservatismRating);
|
counts[indices[entry.type]] += 1;
|
||||||
counts[3] += 1;
|
|
||||||
}
|
|
||||||
if (element.teamworkCommunicationsRating) {
|
|
||||||
iTotals[4] += parseInt(element.teamworkCommunicationsRating);
|
|
||||||
counts[4] += 1;
|
|
||||||
}
|
|
||||||
if (element.teamworkLeadershipRating) {
|
|
||||||
iTotals[5] += parseInt(element.teamworkLeadershipRating);
|
|
||||||
counts[5] += 1;
|
|
||||||
}
|
|
||||||
if (element.teamworkWorkloadRating) {
|
|
||||||
iTotals[6] += parseInt(element.teamworkWorkloadRating);
|
|
||||||
counts[6] += 1;
|
|
||||||
}
|
|
||||||
if (element.knowledgeRating) {
|
|
||||||
iTotals[7] += parseInt(element.knowledgeRating);
|
|
||||||
counts[7] += 1;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
for (var i = 0; i < iTotals.length; i++) {
|
for (var i = 0; i < iTotals.length; i++) {
|
||||||
if (counts[i] !== 0) {
|
if (counts[i] !== 0) {
|
||||||
Vue.set(this.totals, i, (iTotals[i] / counts[i]).toFixed(1));
|
Vue.set(this.totals, i, (iTotals[i] / counts[i]).toFixed(1));
|
||||||
@ -625,14 +400,155 @@ export default {
|
|||||||
},
|
},
|
||||||
handleSubmit() {
|
handleSubmit() {
|
||||||
var form = document.getElementById("submission-form");
|
var form = document.getElementById("submission-form");
|
||||||
|
var notObservedValue = 0
|
||||||
if (form.checkValidity()) {
|
if (form.checkValidity()) {
|
||||||
let axiosConfig = {
|
let axiosConfig = {
|
||||||
auth: {
|
auth: {
|
||||||
username: "admin",
|
username: "admin",
|
||||||
password: this.submitPassword
|
password: this.submitPassword,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var transformedScenarioData = [];
|
||||||
|
this.scenarios.forEach(function(scenario) {
|
||||||
|
var monitoring = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "monitoring"
|
||||||
|
);
|
||||||
|
var controlProcedural = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "controlProcedural"
|
||||||
|
);
|
||||||
|
var control = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "control"
|
||||||
|
);
|
||||||
|
var conservatism = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "conservatism"
|
||||||
|
);
|
||||||
|
var teamworkCommunications = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "teamworkCommunications"
|
||||||
|
);
|
||||||
|
var teamworkLeadership = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "teamworkLeadership"
|
||||||
|
);
|
||||||
|
var teamworkWorkload = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "teamworkWorkload"
|
||||||
|
);
|
||||||
|
var knowledge = scenario.entries.filter(
|
||||||
|
(entry) => entry.type === "knowledge"
|
||||||
|
);
|
||||||
|
transformedScenarioData.push({
|
||||||
|
title: scenario.title,
|
||||||
|
monitoringRating:
|
||||||
|
monitoring.length > 0
|
||||||
|
? monitoring.length > 1
|
||||||
|
? monitoring.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / monitoring.length
|
||||||
|
: monitoring[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
monitoringStrengths: monitoring
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join("; "),
|
||||||
|
monitoringImprovements: monitoring
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join("; "),
|
||||||
|
controlProceduralRating:
|
||||||
|
controlProcedural.length > 0
|
||||||
|
? controlProcedural.length > 1
|
||||||
|
? controlProcedural.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / controlProcedural.length
|
||||||
|
: controlProcedural[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
controlProceduralStrengths: controlProcedural
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join("; "),
|
||||||
|
controlProceduralImprovements: controlProcedural
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join("; "),
|
||||||
|
controlRating:
|
||||||
|
control.length > 0
|
||||||
|
? control.length > 1
|
||||||
|
? control.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / control.length
|
||||||
|
: control[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
controlStrengths: control
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join("; "),
|
||||||
|
controlImprovements: control.map((entry) => entry.AFIs).join("; "),
|
||||||
|
conservatismRating:
|
||||||
|
conservatism.length > 0
|
||||||
|
? conservatism.length > 1
|
||||||
|
? conservatism.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / conservatism.length
|
||||||
|
: conservatism[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
conservatismStrengths: conservatism
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
conservatismImprovements: conservatism
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
teamworkCommunicationsRating:
|
||||||
|
teamworkCommunications.length > 0
|
||||||
|
? teamworkCommunications.length > 1
|
||||||
|
? teamworkCommunications.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / teamworkCommunications.length
|
||||||
|
: teamworkCommunications[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
teamworkCommunicationsStrengths: teamworkCommunications
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
teamworkCommunicationsImprovements: teamworkCommunications
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join(";"),
|
||||||
|
teamworkLeadershipRating:
|
||||||
|
teamworkLeadership.length > 0
|
||||||
|
? teamworkLeadership.length > 1
|
||||||
|
? teamworkLeadership.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / teamworkLeadership.length
|
||||||
|
: teamworkLeadership[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
teamworkLeadershipStrengths: teamworkLeadership
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
teamworkLeadershipImprovements: teamworkLeadership
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join(";"),
|
||||||
|
teamworkWorkloadRating:
|
||||||
|
teamworkWorkload.length > 0
|
||||||
|
? teamworkWorkload.length > 1
|
||||||
|
? teamworkWorkload.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / teamworkWorkload.length
|
||||||
|
: teamworkWorkload[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
teamworkWorkloadStrengths: teamworkWorkload
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
teamworkWorkloadImprovements: teamworkWorkload
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join(";"),
|
||||||
|
knowledgeRating:
|
||||||
|
knowledge.length > 0
|
||||||
|
? knowledge.length > 1
|
||||||
|
? knowledge.reduce(
|
||||||
|
(a, b) => parseInt(a.rating) + parseInt(b.rating)
|
||||||
|
) / knowledge.length
|
||||||
|
: knowledge[0].rating
|
||||||
|
: notObservedValue,
|
||||||
|
knowledgeStrengths: knowledge
|
||||||
|
.map((entry) => entry.strengths)
|
||||||
|
.join(";"),
|
||||||
|
knowledgeImprovements: knowledge
|
||||||
|
.map((entry) => entry.AFIs)
|
||||||
|
.join(";"),
|
||||||
|
});
|
||||||
|
});
|
||||||
Vue.axios
|
Vue.axios
|
||||||
.post(
|
.post(
|
||||||
"/observation",
|
"/observation",
|
||||||
@ -641,8 +557,8 @@ export default {
|
|||||||
tutors: this.tutors,
|
tutors: this.tutors,
|
||||||
person: this.whom,
|
person: this.whom,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
observed: this.scenarios.map(x => x.title).join(", "),
|
observed: this.scenarios.map((x) => x.title).join("; "),
|
||||||
scenarios: JSON.parse(JSON.stringify(this.scenarios))
|
scenarios: JSON.parse(JSON.stringify(transformedScenarioData)),
|
||||||
},
|
},
|
||||||
axiosConfig
|
axiosConfig
|
||||||
)
|
)
|
||||||
@ -654,8 +570,8 @@ export default {
|
|||||||
}
|
}
|
||||||
this.clearPassword();
|
this.clearPassword();
|
||||||
form.classList.add("was-validated");
|
form.classList.add("was-validated");
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -698,7 +614,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 160px; /* Set the width of the sidebar */
|
width: 8%; /* Set the width of the sidebar */
|
||||||
position: fixed; /* Fixed Sidebar (stay in place on scroll) */
|
position: fixed; /* Fixed Sidebar (stay in place on scroll) */
|
||||||
z-index: 1; /* Stay on top */
|
z-index: 1; /* Stay on top */
|
||||||
top: 10%; /* Stay at the top */
|
top: 10%; /* Stay at the top */
|
||||||
|
@ -148,102 +148,99 @@
|
|||||||
</b-row>
|
</b-row>
|
||||||
<br />
|
<br />
|
||||||
<b-row class="mb-2">
|
<b-row class="mb-2">
|
||||||
<b-col class="centered-image" v-if="observation.monitoring">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Monitoring.svg"
|
src="../assets/Monitoring.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: observation.monitoring < 2.5 }"
|
v-bind:class="{ scorewarning: observation.monitoring < 2.5 && observation.monitoring > 0}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.monitoring > 0">
|
||||||
{{ observation.monitoring.toFixed(1) }}
|
{{ observation.monitoring.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col
|
<b-col
|
||||||
class="centered-image"
|
class="centered-image"
|
||||||
v-if="observation.controlProcedural"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="../assets/Control.svg"
|
src="../assets/Control.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{
|
v-bind:class="{
|
||||||
scorewarning: observation.controlProcedural < 2.5
|
scorewarning: observation.controlProcedural < 2.5 && observation.controlProcedural > 0
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.controlProcedural > 0">
|
||||||
{{ observation.controlProcedural.toFixed(1) }}
|
{{ observation.controlProcedural.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="centered-image" v-if="observation.control">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Control.svg"
|
src="../assets/Control.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: observation.control < 2.5 }"
|
v-bind:class="{ scorewarning: observation.control < 2.5 && observation.control > 0}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.control > 0">
|
||||||
{{ observation.control.toFixed(1) }}
|
{{ observation.control.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="centered-image" v-if="observation.conservatism">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Conservatism.svg"
|
src="../assets/Conservatism.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{
|
v-bind:class="{
|
||||||
scorewarning: observation.conservatism < 2.5
|
scorewarning: observation.conservatism < 2.5 && observation.conservatism > 0
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.conservatism > 0">
|
||||||
{{ observation.conservatism.toFixed(1) }}
|
{{ observation.conservatism.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col
|
<b-col
|
||||||
class="centered-image"
|
class="centered-image"
|
||||||
v-if="observation.teamworkCommunications"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{
|
v-bind:class="{
|
||||||
scorewarning: observation.teamworkCommunications < 2.5
|
scorewarning: observation.teamworkCommunications < 2.5 && observation.teamworkCommunications > 0
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.teamworkCommunications > 0">
|
||||||
{{ observation.teamworkCommunications.toFixed(1) }}
|
{{ observation.teamworkCommunications.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col
|
<b-col
|
||||||
class="centered-image"
|
class="centered-image"
|
||||||
v-if="observation.teamworkLeadership"
|
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{
|
v-bind:class="{
|
||||||
scorewarning: observation.teamworkLeadership < 2.5
|
scorewarning: observation.teamworkLeadership < 2.5 && observation.teamworkLeadership > 0
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.teamworkLeadership > 0">
|
||||||
{{ observation.teamworkLeadership.toFixed(1) }}
|
{{ observation.teamworkLeadership.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="centered-image" v-if="observation.teamworkWorkload">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Teamwork.svg"
|
src="../assets/Teamwork.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{
|
v-bind:class="{
|
||||||
scorewarning: observation.teamworkWorkload < 2.5
|
scorewarning: observation.teamworkWorkload < 2.5 && observation.teamworkWorkload > 0
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.teamworkWorkload > 0">
|
||||||
{{ observation.teamworkWorkload.toFixed(1) }}
|
{{ observation.teamworkWorkload.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col class="centered-image" v-if="observation.knowledge">
|
<b-col class="centered-image">
|
||||||
<img
|
<img
|
||||||
src="../assets/Knowledge.svg"
|
src="../assets/Knowledge.svg"
|
||||||
class="image-opacity"
|
class="image-opacity"
|
||||||
v-bind:class="{ scorewarning: observation.knowledge < 2.5 }"
|
v-bind:class="{ scorewarning: observation.knowledge < 2.5 && observation.knowledge > 0 }"
|
||||||
/>
|
/>
|
||||||
<div class="image-centered-text">
|
<div class="image-centered-text" v-if="observation.knowledge > 0">
|
||||||
{{ observation.knowledge.toFixed(1) }}
|
{{ observation.knowledge.toFixed(1) }}
|
||||||
</div>
|
</div>
|
||||||
</b-col>
|
</b-col>
|
||||||
|
Loading…
Reference in New Issue
Block a user