Compare commits

...

14 Commits

Author SHA1 Message Date
Roman Schenk 00db602904 Merge branch 'dev' into feature_gardenplan-model_M2 2022-11-06 18:22:33 +01:00
giavaphi 5e206ace39 Merge pull request #51 from schrom01/feature_taskList_m2
TaskListModel
2022-11-06 17:50:30 +01:00
schrom01 b7d08944a6 added new Methods to filter List by Crop ID 2022-11-06 17:29:21 +01:00
David Guler f6ec411b7a doc: design class diagram "dcd" 2022-11-05 09:50:35 +01:00
schrom01 38288f8561 finished Tests for TaskListModel 2022-11-04 14:20:58 +01:00
giavaphi a43a23427c Merge pull request #50 from schrom01/feature_json-gardenplan_M2 2022-11-04 12:12:43 +01:00
gulerdav fb0c50a715 Merge branch 'dev' into feature_json-gardenplan_M2 2022-11-03 14:31:23 +01:00
David Guler 83bc011870 #26 added fixed test files, made Crop class testable 2022-11-03 14:28:26 +01:00
David Guler ce93531ab8 Made tests pass for plants not in zone 2022-11-03 14:25:25 +01:00
schrom01 eea530931e created Some TaskListModelTests 2022-10-31 21:01:25 +01:00
Elias Csomor 82eab6d5cd continue GardenPlan tests 2022-10-31 11:21:45 +01:00
Elias Csomor 5411ac69ae initial GardenPlan tests 2022-10-31 11:15:27 +01:00
Elias Csomor 146c43d5d9 extended tests for Zones 2022-10-31 10:07:06 +01:00
Elias Csomor 160880d4f5 removed useless quotes, corrected typo 2022-10-31 09:42:39 +01:00
13 changed files with 560 additions and 27 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

View File

@ -0,0 +1,144 @@
<diagram program="umletino" version="15.0.0"><zoom_level>10</zoom_level><element><id>UMLClass</id><coordinates><x>482</x><y>360</y><w>100</w><h>30</h></coordinates><panel_attributes>Main</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1082</x><y>380</y><w>370</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
PlantDatabase
--
+ getPlantList(zone: HardinessZone): List&lt;Plant&gt;
+ getPlantById(zone: HardinessZone id: long): Optional&lt;Plant&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>672</x><y>710</y><w>220</w><h>40</h></coordinates><panel_attributes>TaskListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1132</x><y>110</y><w>300</w><h>180</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
Plant
--
+ id: long
+ name: String
+ description: String
+ spacing: int
+ lifecycle: List&lt;GrowthPhase&gt;
--
+ calculateStartDate(harvestDate: Date): Date
+ generateTasks()
</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>942</x><y>710</y><w>210</w><h>120</h></coordinates><panel_attributes>TaskListModel
--
- tasks: ListProperty&lt;Task&gt;
- taskDb: TaskDatabase
--
+ getTask(id: long): Optional&lt;Task&gt;
+ saveTask(task: Task)
+ removeTask(task: Task)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>690</y><w>250</w><h>120</h></coordinates><panel_attributes>GardenPlanModel
--
- tasks: ListProperty&lt;Crop&gt;
- gardenPlan: GardenPlan
--
+ plantAsCrop(planting: UserPlanting)
+ removePlanting(planting: UserPlanting)</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>72</x><y>680</y><w>180</w><h>80</h></coordinates><panel_attributes>GardenPlanController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1362</x><y>830</y><w>490</w><h>240</h></coordinates><panel_attributes>Task
--
+ id: long
+ name: String {readOnly}
+ description: String {readOnly}
+ startDate: Date {readOnly}
+ isOptional: boolean {readOnly}
- interval: int
- endDate: Date
--
+ Task(name: String, description: String, startDate: String, isReadOnly: boolean): Task
+ withInterval(interval: int): Task
+ withEndDate(endDate: Date): Task
+ withId(id: long): Task
--
+ getInterval(): Optional&lt;int&gt;
+ getEndDate(): Optional&lt;Date&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>902</x><y>930</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
TaskDatabase
--
+ getTaskList(start: Date, end: Date): List&lt;Task&gt;
+ saveTask(Task task) throws ??Exception
+ removeTask(Task task) throws ??Exception</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>442</x><y>440</y><w>170</w><h>80</h></coordinates><panel_attributes>MainWindowController</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>962</x><y>1310</y><w>210</w><h>70</h></coordinates><panel_attributes>NotificationService
--
- taskDb: TaskDatabase
--
+ tick()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>1310</y><w>240</w><h>90</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
WeatherProvider
--
+ getWeatherForecast: WeatherForecast</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1392</x><y>1150</y><w>400</w><h>100</h></coordinates><panel_attributes>WeatherService
--
- weatherPovider: WeatherProvider
- taskDb: TaskDatabase
--
+ WeatherService(provider: WeatherProvider, taskDb: TaskDatabase)
- updateTasks()</panel_attributes><additional_attributes></additional_attributes></element><element><id>UMLClass</id><coordinates><x>1502</x><y>110</y><w>260</w><h>150</h></coordinates><panel_attributes>&lt;&lt;Record&gt;&gt;
GrowthPhase
--
+ startDate: MonthDay
+ endDate: MonthDay
+ type: GrowthPhaseType
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1422</x><y>180</y><w>100</w><h>40</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Text</id><coordinates><x>152</x><y>130</y><w>190</w><h>70</h></coordinates><panel_attributes>Note:
--
{final, readOnly} omitted on public data fields in &lt;&lt;Record&gt;&gt; Entities for clarity
style=wordwrap</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1172</x><y>970</y><w>210</w><h>50</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns &gt;</panel_attributes><additional_attributes>190;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>1032</x><y>820</y><w>140</w><h>130</h></coordinates><panel_attributes>lt=&lt;-
m1=1 external database
m2=1 internal list
accesses</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>322</x><y>1120</y><w>240</w><h>150</h></coordinates><panel_attributes>Crop
--
- cropId: Long
+ plantId: long {final, readOnly}
+ startDate: LocalDate {final, readOnly}
+ area: Double
--
+ withId(long): Crop
+ withArea(double): Crop
--
+ getCropId(): Optional&lt;Long&gt;</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1262</x><y>280</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns</panel_attributes><additional_attributes>10;10;10;100</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1632</x><y>290</y><w>130</w><h>100</h></coordinates><panel_attributes>&lt;&lt;Enumeration&gt;&gt;
GrowthPhaseType
--
SOW
PLANT
HARVEST</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1582</x><y>250</y><w>70</w><h>120</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;
m1=1
m2=*</panel_attributes><additional_attributes>50;90;10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>720</y><w>80</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>60;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>432</x><y>1020</y><w>110</w><h>120</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
returns / saves</panel_attributes><additional_attributes>10;100;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>242</x><y>710</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>302</x><y>890</y><w>280</w><h>140</h></coordinates><panel_attributes>&lt;&lt;Interface&gt;&gt;
GardenPlan
--
+ getPlantings(): List&lt;UserPlanting&gt;
+ addPlanting(plantId: long, startDate)
+ savePlanting(planting: UserPlanting)
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>422</x><y>800</y><w>80</w><h>110</h></coordinates><panel_attributes>lt=&lt;.
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;90;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1522</x><y>1240</y><w>30</w><h>90</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;70;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1062</x><y>1060</y><w>80</w><h>270</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
accesses</panel_attributes><additional_attributes>10;10;10;250</additional_attributes></element><element><id>Relation</id><coordinates><x>1142</x><y>1060</y><w>270</w><h>170</h></coordinates><panel_attributes>lt=&lt;-
m1=1
m2=1
adds Tasks</panel_attributes><additional_attributes>10;10;10;140;250;140</additional_attributes></element><element><id>UMLClass</id><coordinates><x>822</x><y>1140</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonTaskDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>912</x><y>1060</y><w>30</w><h>100</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;10;80</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1562</x><y>430</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonPlantDatabase</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1442</x><y>440</y><w>140</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>10;10;120;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>32</x><y>930</y><w>200</w><h>50</h></coordinates><panel_attributes>JsonGardenPlan</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>222</x><y>940</y><w>100</w><h>30</h></coordinates><panel_attributes>lt=&lt;&lt;-</panel_attributes><additional_attributes>80;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>510</y><w>220</w><h>220</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>200;200;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>172</x><y>510</y><w>340</w><h>190</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;170;320;10</additional_attributes></element><element><id>UMLClass</id><coordinates><x>582</x><y>250</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListController</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>522</x><y>380</y><w>30</w><h>80</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>10;60;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>602</x><y>300</y><w>80</w><h>210</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>60;10;10;190</additional_attributes></element><element><id>UMLClass</id><coordinates><x>732</x><y>390</y><w>160</w><h>60</h></coordinates><panel_attributes>PlantListModel</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>732</x><y>280</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.</panel_attributes><additional_attributes>70;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>882</x><y>400</y><w>220</w><h>70</h></coordinates><panel_attributes>lt=&lt;-
m1=1\nexternal\ndatabase
m2=1\ninternal\nlist
accesses &gt;</panel_attributes><additional_attributes>200;20;10;20</additional_attributes></element><element><id>Relation</id><coordinates><x>582</x><y>0</y><w>190</w><h>50</h></coordinates><panel_attributes>lt=-
m1=0..n
m2=0..1
teaches to &gt;</panel_attributes><additional_attributes>10;20;170;20</additional_attributes></element><element><id>UMLClass</id><coordinates><x>1412</x><y>530</y><w>420</w><h>200</h></coordinates><panel_attributes>TaskTemplate
--
+ name: String {readOnly}
+ description: String {readOnly}
+ relativeStartDate: int {readOnly}
- interval: Integer
- relativeEndDate: Integer
- isOptional: boolean
--
+ Task(name: String, description: String, startDate: String): TaskTemplate
+ setRelativeEndDate(relativeEndDate: int)
+ setInterval(interval: Integer)
+ generateTask(realStartDate: LocalDate): Task
</panel_attributes><additional_attributes></additional_attributes></element><element><id>Relation</id><coordinates><x>1752</x><y>180</y><w>130</w><h>490</h></coordinates><panel_attributes>lt=&lt;-&gt;&gt;&gt;&gt;&gt;
m1=*
m2=1</panel_attributes><additional_attributes>80;460;110;460;110;10;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>1602</x><y>720</y><w>90</w><h>130</h></coordinates><panel_attributes>lt=&lt;.
m1=*
m2=1
generates</panel_attributes><additional_attributes>10;110;10;10</additional_attributes></element><element><id>Relation</id><coordinates><x>562</x><y>770</y><w>400</w><h>40</h></coordinates><panel_attributes>lt=&lt;-
delegates generating Tasks</panel_attributes><additional_attributes>380;20;10;20</additional_attributes></element></diagram>

View File

@ -20,7 +20,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
public class JsonGardenPlan implements GardenPlan { public class JsonGardenPlan implements GardenPlan {
private final URL dataSource = getClass().getResource("user-crops.json"); private final URL dataSource;
private IdProvider idProvider; private IdProvider idProvider;
private Map<Long, Crop> cropMap = Collections.emptyMap(); private Map<Long, Crop> cropMap = Collections.emptyMap();
@ -40,7 +40,21 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#getCrops() * Default constructor
*/
public JsonGardenPlan() {
this.dataSource = getClass().getResource("user-crops.json");
}
/**
* Constructor to use a specified {@link URL} as a {@link #dataSource}
* @param dataSource A {@link URL} to the file to be used as a data source
*/
public JsonGardenPlan(URL dataSource) {
this.dataSource = dataSource;
}
/**
* {@inheritDoc}
*/ */
@Override @Override
public List<Crop> getCrops() throws IOException { public List<Crop> getCrops() throws IOException {
@ -51,7 +65,7 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#getCropById(long) * {@inheritDoc}
*/ */
@Override @Override
public Optional<Crop> getCropById(long id) throws IOException { public Optional<Crop> getCropById(long id) throws IOException {
@ -62,7 +76,7 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#saveCrop(Crop) * {@inheritDoc}
* *
* Saves a crop to the database. * Saves a crop to the database.
* If no {@link Crop#cropId} is set, one will be generated and * If no {@link Crop#cropId} is set, one will be generated and
@ -79,7 +93,7 @@ public class JsonGardenPlan implements GardenPlan {
} }
/** /**
* @see GardenPlan#removeCrop(Crop) * {@inheritDoc}
*/ */
@Override @Override
public void removeCrop(Crop crop) throws IOException { public void removeCrop(Crop crop) throws IOException {

View File

@ -89,15 +89,16 @@ public class JsonPlantDatabase implements PlantDatabase {
List<Plant> result; List<Plant> result;
result = mapper.readerForListOf(Plant.class).readValue(dataSource); result = mapper.readerForListOf(Plant.class).readValue(dataSource);
for (Plant plant : result) {
plant.inZone(currentZone);
}
// Turn list into a HashMap with structure id => Plant
plantMap = result.stream() plantMap = result.stream()
// Remove plants not in the current zone
.filter(plant -> {
plant.inZone(currentZone);
return !plant.lifecycle().isEmpty();
})
// Create Hashmap from results
.collect(HashMap::new, .collect(HashMap::new,
(res, plant) -> res.put(plant.id(), plant), (res, plant) -> res.put(plant.id(), plant),
(existing, replacement) -> { }); (existing, replacement) -> {});
} }
} }
} }

View File

@ -9,8 +9,10 @@ import ch.zhaw.gartenverwaltung.types.*;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class TaskListModel { public class TaskListModel {
@ -77,6 +79,10 @@ public class TaskListModel {
taskDatabase.removeTask(task); taskDatabase.removeTask(task);
} }
private List<Task> filterListByCrop(List<Task> taskList, Long cropId) {
return taskList.stream().filter(task -> task.getCropId() == cropId).collect(Collectors.toList());
}
/** /**
* Method to get all Tasks * Method to get all Tasks
* @return List of all Tasks * @return List of all Tasks
@ -86,6 +92,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX); return getFilteredTaskList(LocalDate.MIN, LocalDate.MAX);
} }
/**
* Method to get all Tasks for specific Crop
* @return List of all Tasks for with given CropID
* @throws IOException If the database cannot be accessed
*/
public List<Task> getTaskListForCrop(Long cropId) throws IOException {
return filterListByCrop(getTaskList(), cropId);
}
/** /**
* Method to get all Tasks which are today or in future * Method to get all Tasks which are today or in future
* @return List of Tasks * @return List of Tasks
@ -95,6 +110,15 @@ public class TaskListModel {
return getFilteredTaskList(LocalDate.now(), LocalDate.MAX); return getFilteredTaskList(LocalDate.now(), LocalDate.MAX);
} }
/**
* Method to get all Tasks which are today or in future for specific Crop
* @return List of Tasks with given crop ID
* @throws IOException If the database cannot be accessed
*/
public List<Task> getFutureTasksForCrop(Long cropId) throws IOException {
return filterListByCrop(getFutureTasks(), cropId);
}
/** /**
* Method to get all Tasks which are today or in past * Method to get all Tasks which are today or in past
* @return List of Tasks * @return List of Tasks
@ -105,17 +129,40 @@ public class TaskListModel {
} }
/** /**
* Method to get an Array of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today. * Method to get all Tasks which are today or in past for specifc crop
* @return Array with length 7 (List<Task>[]) * @return List of Tasks with given grop id
* @throws IOException If the database cannot be accessed * @throws IOException If the database cannot be accessed
*/ */
public List<Task>[] getTasksUpcomingWeek() throws IOException { public List<Task> getPastTasksForCrop(Long cropId) throws IOException {
List<Task>[] listArray = new List[7]; return filterListByCrop(getPastTasks(), cropId);
}
/**
* Method to get an List of 7 Tasklists for the next 7 days. Index 0 is Tasklist for Today.
* @return List with length 7 (List<List<Task>>)
* @throws IOException If the database cannot be accessed
*/
public List<List<Task>> getTasksUpcomingWeek() throws IOException {
List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) { for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i); LocalDate date = LocalDate.now().plusDays(i);
listArray[i] = taskDatabase.getTaskList(date, date); dayTaskList.add(taskDatabase.getTaskList(date, date));
} }
return listArray; return dayTaskList;
}
/**
* Method to get an List of 7 Tasklists for the next 7 days. (Filtered Index 0 is Tasklist for Today.
* @return List with length 7 (List<List<Task>>)
* @throws IOException If the database cannot be accessed
*/
public List<List<Task>> getTasksUpcomingWeekForCrop(Long cropId) throws IOException {
List<List<Task>> dayTaskList = new ArrayList<>();
for(int i = 0; i < 7; i++) {
LocalDate date = LocalDate.now().plusDays(i);
dayTaskList.add(filterListByCrop(taskDatabase.getTaskList(date, date), cropId));
}
return dayTaskList;
} }
/** /**

View File

@ -1,6 +1,7 @@
package ch.zhaw.gartenverwaltung.types; package ch.zhaw.gartenverwaltung.types;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
public class Crop { public class Crop {
@ -41,6 +42,24 @@ public class Crop {
public LocalDate getStartDate() { return startDate; } public LocalDate getStartDate() { return startDate; }
public double getArea() { return area; } public double getArea() { return area; }
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other instanceof Crop otherCrop) {
return Objects.equals(this.cropId, otherCrop.cropId) &&
plantId == otherCrop.plantId &&
startDate != null && startDate.equals(otherCrop.startDate) &&
area == otherCrop.area;
}
return false;
}
@Override
public int hashCode() {
int startCode = startDate != null ? startDate.hashCode() : 0;
return (int) plantId ^ (startCode << 16);
}
@Override @Override
public String toString() { public String toString() {
return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]", return String.format("Crop [ cropId: %d, plantId: %d, startDate: %s, area: %f ]",

View File

@ -21,11 +21,11 @@ public class Task {
* default constructor * default constructor
* (used by Json deserializer) * (used by Json deserializer)
*/ */
public Task(){ public Task(long cropId){
name= ""; name= "";
description= ""; description= "";
startDate = LocalDate.now(); startDate = LocalDate.now();
// this.cropId = cropId; this.cropId = cropId;
} }
public Task(String name, String description, LocalDate startDate, long cropId) { public Task(String name, String description, LocalDate startDate, long cropId) {

View File

@ -31,7 +31,7 @@
"name": "Germinate", "name": "Germinate",
"relativeStartDate": -14, "relativeStartDate": -14,
"relativeEndDate": null, "relativeEndDate": null,
"description": "\"Take an egg carton and fill it with soil. Put the seedling deep enaugh so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.\"", "description": "Take an egg carton and fill it with soil. Put the seedling deep enough so its half covered with soil. Keep it in 10-15 * Celsius with lots of light.",
"interval": null, "interval": null,
"isOptional": false "isOptional": false
} }
@ -53,7 +53,7 @@
"name": "hilling", "name": "hilling",
"relativeStartDate": 0, "relativeStartDate": 0,
"relativeEndDate": null, "relativeEndDate": null,
"description": "\"When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.\"", "description": "When the plants are 20 cm tall, begin hilling the potatoes by gently mounding the soil from the center of your rows around the stems of the plant. Mound up the soil around the plant until just the top few leaves show above the soil. Two weeks later, hill up the soil again when the plants grow another 20 cm.",
"interval": 21, "interval": 21,
"isOptional": false "isOptional": false
} }

View File

@ -0,0 +1,105 @@
package ch.zhaw.gartenverwaltung.io;
import ch.zhaw.gartenverwaltung.types.Crop;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JsonGardenPlanTest {
private GardenPlan testDatabase;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* Files to isolate the test-units
*/
private final URL dbDataSource = this.getClass().getResource("user-crops.json");
private final URL testFile = this.getClass().getResource("test-user-crops.json");
@BeforeEach
void connectToDb() throws URISyntaxException, IOException {
assertNotNull(testFile);
assertNotNull(dbDataSource);
Files.copy(Path.of(testFile.toURI()), Path.of(dbDataSource.toURI()), StandardCopyOption.REPLACE_EXISTING);
testDatabase = new JsonGardenPlan(dbDataSource);
}
@Test
@DisplayName("Check if results are retrieved completely.")
void getCropsNotEmpty() {
List<Crop> testList;
try {
testList = testDatabase.getCrops();
} catch (IOException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(3, testList.size());
List<Long> plantIds = testList.stream().map(Crop::getPlantId).collect(Collectors.toList());
List<Long> expected = Arrays.asList(1L, 1L, 0L);
Assertions.assertEquals(expected, plantIds);
}
@Test
@DisplayName("Check whether single access works.")
void getCropById() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(1);
assertTrue(testCrop.isPresent());
Assertions.assertEquals(1, testCrop.get().getPlantId());
}
@Test
@DisplayName("Check for a nonexisting crop.")
void getCropByIdMustFail() throws IOException {
Optional<Crop> testCrop = testDatabase.getCropById(99);
Assertions.assertFalse(testCrop.isPresent());
}
@Test
@DisplayName("Add new Crop.")
void addNewCrop() {
Crop crop = new Crop(3L, LocalDate.parse("2023-02-22", formatter));
try {
testDatabase.saveCrop(crop);
assertTrue(crop.getCropId().isPresent());
Optional<Crop> testCrop = testDatabase.getCropById(crop.getCropId().get());
assertTrue(testCrop.isPresent());
Assertions.assertEquals(crop, testCrop.get());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Test
@DisplayName("Remove crop")
void removeCrop(){
try {
Optional<Crop> crop = testDatabase.getCropById(2L);
Assertions.assertTrue(crop.isPresent());
testDatabase.removeCrop(crop.get());
crop = testDatabase.getCropById(2L);
Assertions.assertFalse(crop.isPresent());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -8,7 +8,6 @@ import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -16,7 +15,6 @@ import java.util.stream.Collectors;
public class JsonPlantDatabaseTest { public class JsonPlantDatabaseTest {
PlantDatabase testDatabase; PlantDatabase testDatabase;
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
@BeforeEach @BeforeEach
void connectToDb() { void connectToDb() {
@ -26,7 +24,7 @@ public class JsonPlantDatabaseTest {
@Test @Test
@DisplayName("Check if results are retrieved completely") @DisplayName("Check if results are retrieved completely")
void getPlantList() { void getPlantListNotEmpty() {
List<Plant> testList; List<Plant> testList;
try { try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_8A); testList = testDatabase.getPlantList(HardinessZone.ZONE_8A);
@ -40,12 +38,26 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals(expected,names); Assertions.assertEquals(expected,names);
} }
@Test
@DisplayName("Check if results are retrieved correctly when empty")
void getPlantListEmpty() {
List<Plant> testList;
try {
testList = testDatabase.getPlantList(HardinessZone.ZONE_1A);
} catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e);
}
Assertions.assertEquals(0, testList.size());
}
@Test @Test
@DisplayName("Check whether single access works.") @DisplayName("Check whether single access works.")
void getPlantById() { void getPlantByIdAndZone() {
Optional<Plant> testPlant; Optional<Plant> testPlant;
try { try {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,1); testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
} catch (IOException | HardinessZoneNotSetException e) { } catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -53,12 +65,23 @@ public class JsonPlantDatabaseTest {
Assertions.assertEquals("Early Carrot", testPlant.get().name()); Assertions.assertEquals("Early Carrot", testPlant.get().name());
} }
@Test
@DisplayName("Check whether single access respects zone correctly.")
void getPlantByIdAndWrongZone() throws HardinessZoneNotSetException, IOException {
Optional<Plant> testPlant = testDatabase.getPlantById(HardinessZone.ZONE_1A, 1);
Assertions.assertFalse(testPlant.isPresent());
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 1);
Assertions.assertTrue(testPlant.isPresent());
Assertions.assertEquals("Early Carrot", testPlant.get().name());
}
@Test @Test
@DisplayName("Check for a nonexisting plant.") @DisplayName("Check for a nonexisting plant.")
void getPlantByIdMustFail() { void getPlantByIdMustFail() {
Optional<Plant> testPlant; Optional<Plant> testPlant;
try { try {
testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A,99); testPlant = testDatabase.getPlantById(HardinessZone.ZONE_8A, 99);
} catch (IOException | HardinessZoneNotSetException e) { } catch (IOException | HardinessZoneNotSetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@ -0,0 +1,158 @@
package ch.zhaw.gartenverwaltung.taskList;
import ch.zhaw.gartenverwaltung.io.*;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Task;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.time.LocalDate;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class TaskListModelTest {
TaskDatabase taskDatabase;
PlantDatabase plantDatabase;
List<Task> exampleTaskList;
Map<Long, Plant> examplePlantMap;
TaskListModel model;
@BeforeEach
void setUp() throws IOException {
createExampleTaskList();
taskDatabase = mockTaskDatabase(exampleTaskList);
plantDatabase = mockPlantDatabase(examplePlantMap);
model = new TaskListModel(taskDatabase, plantDatabase);
}
private TaskDatabase mockTaskDatabase(List<Task> exampleTaskList) throws IOException {
TaskDatabase taskDatabase = mock(JsonTaskDatabase.class);
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.MAX)).thenReturn(exampleTaskList);
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.MAX)).thenReturn((exampleTaskList.subList(1, 4)));
List<Task> pastTasks = new ArrayList<>();
pastTasks.add(exampleTaskList.get(0));
pastTasks.add(exampleTaskList.get(2));
pastTasks.add(exampleTaskList.get(4));
when(taskDatabase.getTaskList(LocalDate.MIN, LocalDate.now())).thenReturn(pastTasks);
when(taskDatabase.getTaskList(LocalDate.now(), LocalDate.now())).thenReturn(List.of(exampleTaskList.get(2)));
when(taskDatabase.getTaskList(LocalDate.now().plusDays(1L), LocalDate.now().plusDays(1L))).thenReturn(List.of(exampleTaskList.get(1)));
when(taskDatabase.getTaskList(LocalDate.now().plusDays(2L), LocalDate.now().plusDays(2L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(3L), LocalDate.now().plusDays(3L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(4L), LocalDate.now().plusDays(4L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(5L), LocalDate.now().plusDays(5L))).thenReturn(List.of());
when(taskDatabase.getTaskList(LocalDate.now().plusDays(6L), LocalDate.now().plusDays(6L))).thenReturn(List.of());
return taskDatabase;
}
private PlantDatabase mockPlantDatabase(Map<Long, Plant> examplePlantMap) {
return new PlantDatabase() {
@Override
public List<Plant> getPlantList(HardinessZone zone) {
return null;
}
@Override
public Optional<Plant> getPlantById(HardinessZone zone, long id) {
return Optional.ofNullable(examplePlantMap.get(id));
}
};
}
void createExampleTaskList() {
exampleTaskList = new ArrayList<>();
exampleTaskList.add(new Task("name", "description", LocalDate.now().minusDays(1), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.now().plusDays(1), 2L));
exampleTaskList.add(new Task("name", "description", LocalDate.now(), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(9019, 5, 5), 1L));
exampleTaskList.add(new Task("name", "description", LocalDate.of(2019, 5, 5), 2L));
}
void createExamplePlantMap() {
examplePlantMap = new HashMap<>();
}
@Test
void addTask() throws IOException {
Task taskToAdd = new Task("name", "description", LocalDate.now(), 1L);
model.addTask(taskToAdd);
verify(taskDatabase, times(1)).saveTask(taskToAdd);
}
@Test
void removeTask() throws IOException {
Task taskToRemove = new Task("name", "description", LocalDate.now(), 1L);
model.removeTask(taskToRemove);
verify(taskDatabase, times(1)).removeTask(taskToRemove);
}
@Test
void getTaskList() throws IOException {
List<Task> listToCheck = model.getTaskList();
assertEquals(5, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
assertEquals(exampleTaskList.get(1), listToCheck.get(3));
assertEquals(exampleTaskList.get(3), listToCheck.get(4));
}
@Test
void getFutureTasks() throws IOException {
List<Task> listToCheck = model.getFutureTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(2), listToCheck.get(0));
assertEquals(exampleTaskList.get(1), listToCheck.get(1));
assertEquals(exampleTaskList.get(3), listToCheck.get(2));
}
@Test
void getPastTasks() throws IOException {
List<Task> listToCheck = model.getPastTasks();
assertEquals(3, listToCheck.size());
assertEquals(exampleTaskList.get(4), listToCheck.get(0));
assertEquals(exampleTaskList.get(0), listToCheck.get(1));
assertEquals(exampleTaskList.get(2), listToCheck.get(2));
}
@Test
void getTasksUpcomingWeek() throws IOException {
List<List<Task>> dayList = model.getTasksUpcomingWeek();
assertEquals(7, dayList.size());
//Check day 0
assertEquals(1, dayList.get(0).size());
assertEquals(exampleTaskList.get(2), dayList.get(0).get(0));
//Check day 1
assertEquals(1, dayList.get(1).size());
assertEquals(exampleTaskList.get(1), dayList.get(1).get(0));
//Check day 2
assertEquals(0, dayList.get(2).size());
//Check day 3
assertEquals(0, dayList.get(3).size());
//Check day 4
assertEquals(0, dayList.get(4).size());
//Check day 5
assertEquals(0, dayList.get(5).size());
//Check day 6
assertEquals(0, dayList.get(6).size());
}
@Test
void removeTasksForCrop() throws IOException {
model.removeTasksForCrop(1L);
verify(taskDatabase, times(1)).removeTasksForCrop(1L);
}
}

View File

@ -0,0 +1,20 @@
[
{
"cropId": 0,
"plantId": 1,
"startDate": "2023-02-25",
"area": 0.5
},
{
"cropId": 1,
"plantId": 1,
"startDate": "2023-03-01",
"area": 0.5
},
{
"cropId": 2,
"plantId": 0,
"startDate": "2023-03-25",
"area": 1.5
}
]

View File

@ -0,0 +1,2 @@
[
]