Commit b2c8b716 authored by Hallvard Trætteberg's avatar Hallvard Trætteberg
Browse files

First attempt, based on orig in wiki

parent c6aae773
# Compiled class file
*.class
# Package Files #
*.jar
# maven build folder
target/
FROM gitpod/workspace-full-vnc
USER gitpod
RUN bash -c ". /home/gitpod/.sdkman/bin/sdkman-init.sh \
&& sdk install java 12.0.2.j9-adpt \
&& sdk default java 12.0.2.j9-adpt"
image:
file: .gitpod.Dockerfile
ports:
# used by virtual desktop and vnc, supports JavaFX
- port: 6080
tasks:
- init: sdk use java 12.0.2.j9-adpt
{
"java.configuration.updateBuildConfiguration": "interactive"
}
\ No newline at end of file
# Ordinær eksamen 2019
\ No newline at end of file
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod-staging.com/#https://gitlab.stud.idi.ntnu.no/tdt4100/eksamener/ord-2019)
# Ordinær eksamen 2019
Oppgavens tema er behandling av pasienter på et akuttmottak (`TreatmentUnit`), som håndterer koblingen mellom et sett med pasienter (`Patient`) og
legene (`Doctor`) som behandler dem. Pasientene er allerede diagnostisert med et sett ulike tilstander. De behandles av leger, men
hver lege kan bare behandle et sett med tilstander. Noen pasienter kan ha grunn til å bli prioritert i behandlingskøen, og
en lege kan måtte avbryte en pågående behandling for å ta denne pasienten. Legene på mottaket behandler ulike tilstander,
så pasienter kan måtte behandles av flere leger før de er friske og kan skrives ut. Når en lege kan ta imot en pasient,
skal et informasjonspanel vise hvilken pasient som skal gå til hvilken lege.
Skjelettet til klassene og metodene endrer seg mellom deloppgavene. Deloppgavene skisserer et system som gradvis utvides:
- [Del 1](#del-1) er første trinn i systemet, og inneholder enklere logikk enn beskrevet ovenfor.
- [Del 2](#del-2) er mer ferdig, og danner basis for resten av deloppgavene.
- [Del 3](#del-3) løser prioritering av pasienter ved hjelp av arv.
- [Del 4](#del-4) løser prioritering av pasienter ved hjelp av delegering. Merk at deloppgavene 3 og 4 altså løser det samme problemet på ulike måter.
- [Del 5](#del-5) bruker lytting for å vise informasjon i et panel laget ved hjelp av FXML.
## Del 1 - første trinn av Doctor, Patient og TreatmentUnit-klassene (20%)
### Oppgave a)
Skriv ferdig `Doctor`-klassen i henhold til skjelettet, altså nødvendige innkapslingsmetoder og `isAvailable`.
`Patient` er så langt en tom klasse, du trenger ikke implementere denne.
### Oppgave b)
Skriv følgende deler av klassen `TreatmentUnit`, basert på beskrivelsen i skjelettet:
- Avgjør og implementer den interne representasjonen av pasienter og doktorer.
- `addDoctor`, `addPatient`, `getAvailableDoctors`, og `getWaitingPatients`.
- `getDoctor`: Denne finnes i to versjoner, med og uten bruk av `Predicate`. Du skal skrive begge disse versjonene.
Vær obs på at enkelte av disse metodene bør kalle `startTreatment` fra 1 c).
### Oppgave c) - TreatmentUnit: Koble pasient og doktor
Hver gang en ny pasient eller lege er lagt til, eller en lege har avsluttet en behandling, bør `TreatmentUnit` forsøke å koble en ledig lege og en pasient som skal behandles.
Implementer de to `startTreatment`-metodene og `treatmentFinished` (sistnevnte brukes ikke i denne underoppgaven, men senere).
## Del 2 - andre trinn av Doctor og Patient, samt testing (30%)
Alle pasienter har på forhånd blitt diagnostisert med en eller flere (helse)tilstander (conditions) som må behandles.
Tilsvarende har alle doktorer et sett med tilstander som de er kompetente til å behandle. En doktor kan ikke behandle pasienter den ikke har kompetanse til å behandle, og
en pasient må være i systemet helt til alle tilstander er behandlet.
### Oppgave a) - Patient
Implementer følgende deler av `Patient`-klassen i henhold til skjelettet:
- Bestem hvilke felt, konstruktører og metoder du trenger for håndtering av (helse)tilstander (conditions).
Begrunn valget av løsningen, spesielt hvilke typer du har valgt å bruke.
- `requiresTreatment`: returnerer om pasienten trenger behandling.
- Implementer støtte for å la andre klasser iterere over pasientens tilstander, på enklest mulig måte.
VIKTIG: Merk at koden du skriver i oppgave 2b og den oppgitte testkoden i oppgave 2d skal passe til dine valg.
### Oppgave 2b) - Doctor
Implementer følgende deler av `Doctor`-klassen i henhold til skjelettet:
- Avgjør intern lagring av tilstander legen kan behandle. Du trenger ikke argumentere for valget.
- Konstruktør: Tenk spesielt på innkapsling av data for å hindre endring fra utenfor klassen. Velg selv parameterliste.
- `canTreat`: For en gitt pasient skal metoden returnere andelen av dennes tilstander som doktoren kan behandle.
Hvis Jens har fem tilstander, og Dr. Who kan behandle tre av dem, så skal metoden returne tallet 0.6.
- `treat`: For en gitt pasient fjernes de tilstandene som doktoren kan behandle.
### Oppgave 2c - TreatmentUnit
Nå som pasienter har ulike tilstander, og doktorer kan behandle slike tilstander, må dette taes hensyn til i klassen `TreatmentUnit`.
En doktor kan ikke behandle pasienter den ikke har kompetanse til å behandle, og en pasient må være i systemet helt til alle tilstander er behandlet.
- Implementer endring i `TreatmentUnit` i relevante metoder. Du trenger ikke kopiere inn metoder som ikke endrer seg fra del 1, du skal heller ikke endre svar i del 1.
### Oppgave 2d - Testing
Du har fått utdelt et skjelett med halvferdige testmetoder (`TreatmentUnitTest`).
Gjør testmetodene fullstendige i henhold til kommentarene. Du vil finne dokumentasjon av testing i vedlegget nederst på siden.
### Oppgave 2e) - Sekvensdiagram (støttes ikke av gitpod)
Tegn sekvensdiagram av det som skjer mellom start sequence diagram- og end sequence diagram-kommentarene i testklassen i skjelettkoden.
Diagrammet skal inkludere testen selv, akuttmottaket, pasienten og doktoren som (i den delen av testen) deltar i behandlingen.
Du skal ikke ha med kode du legger til selv (f.eks. kall til assert-metoder), som svar på 2 d).
## Del 3
Fokus i del 3 er organisering av objektorientert kode med arv. Oppgaven kan derfor løses ved hjelp av pseudokode.
Systemet skal støtte tre ulike logikker for å knytte doktor til pasient, implementert av (minst) tre klasser relatert med arv:
- `TreatmentUnit`: Pasienter knyttes til den første (men ikke nødvendigvis beste) doktoren som kan behandle en eller flere tilstander hos pasienten.
- `PriorityTreatmentUnit`: Pasientene kan ha tilstander med varierende kritikalitet, som gjør at de prioriteres foran resten av køen når en doktor er ledig.
Gjør nødvendige antakelser om en `getPriority`-metode som brukes til prioritering.<p>Per, som har en mer alvorlig skade enn Jens, vil altså behandles raskere selv om han kommer inn senere. (Såfremt ikke Jens allerede har startet behandling.)
- `EmergencyPriorityTreatmentUnit`: Pågående pasientbehandling (selv om den er prioritert som over) skal kunne avbrytes og doktoren skal starte behandling av en ny pasient.
Dette skal skje dersom det kommer inn en ny pasient, og dennes prioritet er høyere enn hos den som er til behandling.
Du kan også her forutsette at det finnes en `getPriority`-metode i `Patient`.<p>Anta at Jens er under behandling hos doktor Who. Ida kommer til behandling, har en tilstand med høyere prioritet enn Jens, og kan kun behandles av Who.
Jens sin behandling blir derfor satt på vent, og den samme doktoren begynner behandling av Ida.
I denne deloppgaven skal du beskrive eller implementere støtte for disse egenskapene ved å bruke arv. Du finner ikke noe nytt skjelett til denne oppgaven, men
bygger på koden fra del 2. Vi ønsker altså å ende opp med tre klasser, `TreatmentUnit`, `PriorityTreatmentUnit` og `EmergencyPriorityTreatmentUnit`,
som implementerer hver sin logikk iht. over. Forskjellen dem i mellom skal være hvordan de implementerer `startTreatment`-metodene.
De skal være knyttet sammen med arv for å gjøre det enkelt for andre klasser å bytte mellom dem og for å gi god gjenbruk, men
detaljene i hvordan arvingsmekanismen brukes skal være opp til deg. Det er lov å innføre ekstra klasser og metoder i arvingshierarkiet,
hvis det gjør løsningen bedre.
Forklar med tekst og kode hvordan du vil 1) strukturere arvingshierarkiet og 2) hvilke metoder som deklareres/implementeres hvor.
Skriv også kode for metodene. Siden fokuset her er mer på objektorientert organisering av kode, vil det også gis poeng for pseudokode.
## Del 4
Mens du i del 3 brukte arv, skal du i denne delen bruke *delegering* for å oppnå det samme. Iht. delegeringsteknikken definerer vi et grensesnitt,
`DoctorAllocator`. I tillegg lager du (minst) tre hjelpeklasser tilsvarende de tre logikkene beskrevet i del 3, som implementerer grensesnittet.
Forklar hvordan koden i vedlagte `TreatmentUnit`-skjelett skal gjøres fullstendig slik at `startTreatment`-metoden bruker delegering riktig.
Forklar også med tekst og/eller kode hvordan du vil utforme hjelpeklassene som implementerer `DoctorAllocator`.
Da målet er å vise kunnskap om delegering kan dere bruke pseudokode i denne oppgaven. Det er greit å referere (pseudo)koden i del 3, hvis det er til hjelp.
## Del 5
På venterommet skal det være et informasjonspanel, som forteller ventende pasienter om hvilken doktor de skal gå til, når det er deres tur.
Panelet implementeres med JavaFX, og tanken er at kontroller-objektet (av typen `TreatmentUnitController`) skal lytte på et `TreatmentUnit`-objekt og
få beskjed når koblingen mellom doktor og pasient etableres, slik at panelet kan vise en tekst av typen «Pasient X skal gå til Doctor Y»
(du kan basere deg på at `Patient` og `Doktor` har en `toString()`-metode som gir navn, dette trenger du ikke implementere i tidligere oppgaver).
I denne delen kan du bygge videre på koden fra del 2. Du trenger altså ikke ha løst del 3 eller 4.
### Oppgave 5a) – Lyttergrensesnitt
Definer et egnet lyttergrensesnitt og forklar med tekst og kode hvordan `TreatmentUnit` må endres for å kunne fungere som observert i et observatør-observert-forhold.
### Oppgave 5b) – Controller
Fyll ut kodeskjelettet for kontrollerklassen `TreatmentUnitController`, slik at det fungerer med FXML-koden i `TreatmentUnit.fxml` og fyller rollen som observatør av `TreatmentUnit`.
Som indikert i kodeskjelettet, så kan du anta at `treatmentUnit`-variablen i kontrollerklassen settes «automagisk» i konstruktøren.
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/test-classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="ignore_optional_problems" value="true"/>
<attribute name="m2e-apt" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ord-2019</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
eclipse.preferences.version=1
org.eclipse.jdt.apt.aptEnabled=false
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
org.eclipse.jdt.core.compiler.processAnnotations=disabled
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=11
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>tdt4100-v2020</groupId>
<artifactId>ord-2019</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>13.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<includes>
<include>**/*.fxml</include>
</includes>
</resource>
</resources>
<testSourceDirectory>src</testSourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>12</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>ord2019.part5.TreatmentUnitApp</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration></configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
/**
* @author hal
*
*/
open module ord2019 {
requires javafx.base;
requires javafx.controls;
requires javafx.fxml;
requires javafx.graphics;
requires junit;
}
package ord2019.lf.part1;
/**
* A doctor has the capacity to treat one patient at a time.
*/
public class Doctor {
private Patient patient;
/**
* @return the patient this doctor is treating, or null if s/he isn't currently treating any patient.
*/
public Patient getPatient() {
return patient;
}
/**
* @return true if this doctor is currently treating a patient, otherwise false.
*/
public boolean isAvailable() {
return getPatient() == null;
}
/**
* Sets the patient that this doctor is treating, use null to indicate s/he isn't currently treating any patient.
* @param patient
*/
public void setPatient(final Patient patient) {
this.patient = patient;
}
}
package ord2019.lf.part1;
public class Patient {
}
package ord2019.lf.part1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
/**
* A class for managing a set of doctors and the patients they're treating.
* When doctors or patients arrive, it is made sure that patients are treated as soon as possible.
*/
public class TreatmentUnit {
private final Collection<Doctor> doctors = new ArrayList<>();
private final Collection<Patient> patients = new ArrayList<>();
/**
* Adds a doctor and makes sure s/he starts treating a patient, if one is waiting.
* @param doctor
*/
public void addDoctor(final Doctor doctor) {
doctors.add(doctor);
startTreatment(doctor);
}
/**
* @return the currently available doctors
*/
public Collection<Doctor> getAvailableDoctors() {
final Collection<Doctor> result = new ArrayList<>();
for (final Doctor doctor : doctors) {
if (doctor.isAvailable()) {
result.add(doctor);
}
}
return result;
// return doctors.stream().filter(Doctor::isAvailable).collect(Collectors.toList());
}
/**
* Adds a patient to this treatment unit, and makes sure treatment starts if any doctor is available.
* Otherwise the patient is queued for treatment when a doctor becomes available.
* @param patient
*/
public void addPatient(final Patient patient) {
patients.add(patient);
startTreatment(patient);
}
/**
* @param pred the predicate that the doctor must satisfy
* @return some doctor satisfying the predicate
*/
public Doctor getDoctor(final Predicate<Doctor> pred) {
for (final Doctor doctor : doctors) {
if (pred.test(doctor)) {
return doctor;
}
}
return null;
}
/**
* Find the doctor, if any, that treats the provided patient.
* @param patient
* @return the doctor treating the provided patient, or null, of the patient isn't currently being treated
*/
public Doctor getDoctor(final Patient patient) {
return getDoctor(doctor -> doctor.getPatient() == patient);
}
/**
* Find all patients that are not currently being treated
* @return the patients not currently being treated
*/
public Collection<Patient> getWaitingPatients() {
final Collection<Patient> result = new ArrayList<>();
for (final Patient patient : patients) {
if (getDoctor(patient) == null) {
result.add(patient);
}
}
return result;
}
/**
* Finds a waiting patient and sets him/her as the provided doctor's patient
* @param doctor the doctor for which a patient to treat should be found
* @return true if a patient for the provided doctor was found, false otherwise
*/
private boolean startTreatment(final Doctor doctor) {
final Collection<Patient> waitingPatients = getWaitingPatients();
if (waitingPatients.isEmpty()) {
return false;
}
doctor.setPatient(waitingPatients.iterator().next());
return true;
}
/**
* Finds an available doctor for the provided patient, and sets that doctor to treat the patient
* @param patient the patient for which a treating doctor should be found
* @return true if a doctor for the provided patient was found, false otherwise
*/
private boolean startTreatment(final Patient patient) {
final Collection<Doctor> availableDoctors = getAvailableDoctors();
if (availableDoctors.isEmpty()) {
return false;
}
final Doctor doctor = availableDoctors.iterator().next();
doctor.setPatient(patient);
return true;
}
/**
* Removes the link between doctor and patient, after treatment is finished.
* Since the patient is fully treated, s/he is removed from this treatment unit.
* Also ensure the doctor starts treating another patient.
* @param doctor the doctor that has finished treating his/her patient
*/
public void treatmentFinished(final Doctor doctor) {
if (doctor.getPatient() == null) {
throw new IllegalStateException(doctor + " has no patient!");
}
final Patient patient = doctor.getPatient();
doctor.setPatient(null);
patients.remove(patient);
startTreatment(doctor);
}
}
package ord2019.lf.part2;
import java.util.Arrays;
import java.util.Collection;
/**
* A doctor has the capacity to treat one patient at a time.
* The doctor as a list of competencies (of type String) that
* indicates what conditions s/he can treat.
*/
public class Doctor {
private final Collection<String> competencies;
/**
* Initialise this doctor with a set of competencies
* @param competencies
*/
public Doctor(final String... competencies) {
this.competencies = Arrays.asList(competencies);
}
private Patient patient;
/**
* @return the patient this doctor is treating, or null if s/he isn't currently treating any patient.
*/
public Patient getPatient() {
return patient;
}
/**
* @return true if this doctor is currently treating a patient, otherwise false.
*/
public boolean isAvailable() {
return getPatient() == null;
}
/**
* Sets the patient that this doctor is treating, use null to indicate s/he isn't currently treating any patient.
* @param patient
*/
public void setPatient(final Patient patient) {
this.patient = patient;
}
/**
* Indicates to what extent this doctor can treat the provided patient.
* The value is the number of the patient's conditions this doctor can treat
* divided by the number of conditions the patient has
* Conditions and competencies are matches using simple String comparison
* @param patient
* @return the ratio of the patient's conditions that this doctor can treat
*/
public double canTreat(final Patient patient) {
int count = 0, total = 0;
for (final String item : patient) {
total++;
if (competencies.contains(item)) {
count++;
}
}
return ((double) count) / total;
}
/**
* "Treats" the patient by removing all the patient's conditions that this doctor can treat.
*/
public void treat() {
patient.removeConditions(competencies);
}
}
package ord2019.lf.part2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
/**
* A patient has a set of conditions (of type String) that needs to be treated.
* Implements Iterable to support iterating over all conditions.
*/
public class Patient implements Iterable<String> {
private final Collection<String> conditions = new ArrayList<>();
/**
* Add conditions to this patient, to indicate what s/he should be treated for.
* @param conditions
*/
public void addConditions(final String... conditions) {
this.conditions.addAll(Arrays.asList(conditions));
}
/**
* Indicates if this patient has conditions that needs to be treated.
* @return true if this patient has conditions that needs to be treated, false otherwise.
*/
public boolean requiresTreatment( ) {
return ! conditions.isEmpty();
}
/**
* Removes conditions from this patient, to indicate that these have been treated.
* @param conditions
*/
public void removeConditions(final Collection<String> conditions) {
this.conditions.removeAll(conditions);
}
@Override
public Iterator<String> iterator() {
return conditions.iterator();
}
}
package ord2019.lf.part2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
/**
* A class for managing a set of doctors and the patients they're treating.
* When doctors or patients arrive, it is made sure that patients are treated as soon as possible.
*/
public class TreatmentUnit {