diff --git a/backend/dao/activitiesDao.js b/backend/dao/activitiesDao.js
index c63cee1..76e2740 100644
--- a/backend/dao/activitiesDao.js
+++ b/backend/dao/activitiesDao.js
@@ -4,14 +4,13 @@ const daoHelper = require('./daoHelper.js');
class activitiesDao {
+ /**
+ * @param {import('better-sqlite3').Database} dbConnection - The database connection
+ */
constructor(dbConnection) {
this._conn = dbConnection;
}
- getConnection() {
- return this._conn;
- }
-
loadById(id) {
var sql = 'SELECT * FROM activities WHERE id=? ORDER BY date DESC';
var statement = this._conn.prepare(sql);
diff --git a/backend/dao/plantsDao.js b/backend/dao/plantsDao.js
index b3009eb..6e7692a 100644
--- a/backend/dao/plantsDao.js
+++ b/backend/dao/plantsDao.js
@@ -2,16 +2,19 @@
const helper = require('../helper.js');
const daoHelper = require('./daoHelper.js');
+
+/**
+ * Data Access Object for plants
+ */
class plantsDao {
+ /**
+ * @param {import('better-sqlite3').Database} dbConnection - The database connection
+ */
constructor(dbConnection) {
this._conn = dbConnection;
}
- getConnection() {
- return this._conn;
- }
-
loadById(plant_id) {
var sql = 'SELECT * FROM plants WHERE plant_id=?';
var statement = this._conn.prepare(sql);
@@ -24,8 +27,25 @@ class plantsDao {
return result;
}
+ /**
+ * Loads all plants from the DB
+ * Ignores plants that have been composted
+ * @returns {json[]}
+ */
loadAll() {
- var sql = 'SELECT * from plants order by name';
+ var sql = 'SELECT * FROM plants WHERE composted IS NULL ORDER BY name';
+ var statement = this._conn.prepare(sql);
+ var result = statement.all();
+ var arrayResult = daoHelper.guaranteeArray(result);
+ return arrayResult;
+ }
+
+ /**
+ * Loads all composted plants from the DB
+ * @returns {json[]}
+ */
+ loadAllComposted() {
+ var sql = 'SELECT * FROM plants WHERE composted IS NOT NULL ORDER BY name';
var statement = this._conn.prepare(sql);
var result = statement.all();
var arrayResult = daoHelper.guaranteeArray(result);
@@ -58,10 +78,10 @@ class plantsDao {
return this.loadById(result.lastInsertRowid);
}
- update(plant_id, name, species_name, image, watering_interval, watering_interval_offset) {
- var sql = 'UPDATE plants SET name=?, species_name=?, image=?, watering_interval=?, watering_interval_offset=? WHERE plant_id=?';
+ update(plant_id, name, species_name, image, watering_interval, watering_interval_offset, composted) {
+ var sql = 'UPDATE plants SET name=?, species_name=?, image=?, watering_interval=?, watering_interval_offset=?, composted=? WHERE plant_id=?';
var statement = this._conn.prepare(sql);
- var params = [name, species_name, image, watering_interval, watering_interval_offset, plant_id];
+ var params = [name, species_name, image, watering_interval, watering_interval_offset, composted, plant_id];
var result = statement.run(params);
if (result.changes != 1){
diff --git a/backend/dao/templateDao.js b/backend/dao/templateDao.js
index 53ee64b..5024c8b 100644
--- a/backend/dao/templateDao.js
+++ b/backend/dao/templateDao.js
@@ -5,14 +5,13 @@ const helper = require('../helper.js');
class templateDao {
+ /**
+ * @param {import('better-sqlite3').Database} dbConnection - The database connection
+ */
constructor(dbConnection) {
this._conn = dbConnection;
}
- getConnection() {
- return this._conn;
- }
-
loadById(id) {
// Code goes here
diff --git a/backend/dbMigrationTool.js b/backend/dbMigrationTool.js
index 76b7995..defa4c0 100644
--- a/backend/dbMigrationTool.js
+++ b/backend/dbMigrationTool.js
@@ -1,17 +1,46 @@
/**
* Checks if the DB is from an earlier version and needs to be upgraded
- * @param {*} dbConnection the connection to the DB
+ * @param {import('better-sqlite3').Database} dbConnection the connection to the DB
* @returns {boolean} false if the DB is fine and true if it needs to be migrated
*/
module.exports.dbNeedsMigration = function(dbConnection) {
- // TODO implement this when necessary
+ // ============== Check requirement for v1.0.1 ==============
+ const columnExists = dbConnection.prepare(`
+ PRAGMA table_info(plants)
+ `).all().some(col => col.name === 'composted');
+
+ if(!columnExists)
+ {
+ return true;
+ }
+
+ // ============== Check requirement for v.. ==============
+ // ...
+
+ // All good
return false;
}
/**
* Migrates the DB to the current schema
- * @param {*} dbConnection the connection to the DB
+ * @param {import('better-sqlite3').Database} dbConnection the connection to the DB
*/
module.exports.migrateDB = function(dbConnection) {
- // TODO implement this when necessary
+ // ============== Upgrade from v1.0.0 to v1.1.0 ==============
+ // Add 'composted' column to 'plants' table if it does not exist
+ const columnExists = dbConnection.prepare(`
+ PRAGMA table_info(plants)
+ `).all().some(col => col.name === 'composted');
+
+ if (!columnExists) {
+ dbConnection.prepare(`
+ ALTER TABLE plants ADD COLUMN composted DATE DEFAULT NULL
+ `).run();
+ console.log("Column 'composted' added to 'plants' table.");
+ } else {
+ console.log("Column 'composted' already exists in 'plants' table.");
+ }
+
+ // ============== Upgrade from v1.0.0 to v.. ==============
+ // ...
}
\ No newline at end of file
diff --git a/backend/package.json b/backend/package.json
index 4525d30..1ca65c6 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -1,6 +1,6 @@
{
"name": "plant_manager",
- "version": "0.1.0",
+ "version": "1.1.0",
"description": "Backend for PlantManager with fileupload and sqlite",
"main": "server.js",
"scripts": {
diff --git a/backend/server.js b/backend/server.js
index a5f6333..df61ab9 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -73,7 +73,7 @@ try
// ====== BINDING ENDPOINTS ======
const TOPLEVELPATH = '/api';
let serviceRouter;
- console.log('Binding enpoints, top level Path at ' + TOPLEVELPATH);
+ console.log('Binding endpoints, top level Path at ' + TOPLEVELPATH);
serviceRouter = require('./services/plants.js');
app.use(TOPLEVELPATH, serviceRouter);
diff --git a/backend/services/plants.js b/backend/services/plants.js
index 567e03c..5bf0686 100644
--- a/backend/services/plants.js
+++ b/backend/services/plants.js
@@ -104,6 +104,26 @@ serviceRouter.get('/plants/get/:id', function(request, response) {
}
});
+serviceRouter.get('/plants/composted', function(request, response) {
+ console.log('Service plants: Client requested all records');
+
+ const plantDaoInstance = new plantsDao(request.app.locals.dbConnection);
+ const activitiesDaoInstance = new activitiesDao(request.app.locals.dbConnection);
+ try {
+ var plantArr = plantDaoInstance.loadAllComposted();
+ // foreach Schleife über alle plant JSON, diese werden dabei erweitert
+ plantArr.forEach(plant => {
+ extendPlantJSON(plant,activitiesDaoInstance);
+ });
+
+ console.log('Service plants: Records loaded, count= ' + plantArr.length);
+ response.status(200).json(plantArr);
+ } catch (ex) {
+ console.error('Service plants: Error loading all records. Exception occured: ' + ex.message);
+ response.status(400).json({ 'fehler': true, 'nachricht': ex.message });
+ }
+});
+
serviceRouter.get('/plants/all', function(request, response) {
console.log('Service plants: Client requested all records');
@@ -185,6 +205,7 @@ serviceRouter.post('/plants', function(request, response) {
serviceRouter.put('/plants', function(request, response) {
console.log('Service plants: Client requested update of existing plant');
+ // TODO Replace this madness with a validation Framework like express validator
const plantDaoInstance = new plantsDao(request.app.locals.dbConnection);
var errorMsgs=[];
if (helper.isUndefined(request.body.plant_id)) {
@@ -194,29 +215,35 @@ serviceRouter.put('/plants', function(request, response) {
} else if (request.body.plant_id <= 0) {
errorMsgs.push('plant_id has to be a bigger number than 0');
}
+
if (helper.isUndefined(request.body.name)) {
errorMsgs.push('name missing');
- }
+ }
+
if (helper.isUndefined(request.body.species_name)) {
errorMsgs.push('species_name missing');
}
+
if (helper.isUndefined(request.body.watering_interval)) {
errorMsgs.push('watering_interval missing');
} else if (!helper.isNumeric(request.body.watering_interval)) {
errorMsgs.push('watering_interval has to be a number');
} else if (request.body.watering_interval <= 0) {
- errorMsgs.push('watering_interval has to be a bigger number than 0');
+ errorMsgs.push('watering_interval has to be a number bigger than 0');
}
+
if (helper.isUndefined(request.body.watering_interval_offset)) {
errorMsgs.push('watering_interval_offset missing');
} else if (!helper.isNumeric(request.body.watering_interval_offset)) {
errorMsgs.push('watering_interval has to be a number');
}
+
if (errorMsgs.length > 0) {
console.log('Service plants: Update not possible, data missing or invalid');
response.status(400).json({ 'fehler': true, 'nachricht': 'Function not possible. Missing or invalid data: ' + helper.concatArray(errorMsgs) });
return;
}
+
try {
// check if the plant even exists
if(!plantDaoInstance.exists(request.body.plant_id))
@@ -230,7 +257,7 @@ serviceRouter.put('/plants', function(request, response) {
let image = plantDaoInstance.loadById(request.body.plant_id).image;
// update the plant
- var obj = plantDaoInstance.update(request.body.plant_id,request.body.name, request.body.species_name,image,request.body.watering_interval,request.body.watering_interval_offset);
+ var obj = plantDaoInstance.update(request.body.plant_id,request.body.name, request.body.species_name,image,request.body.watering_interval,request.body.watering_interval_offset,request.body.composted);
console.log('Service plants: Record updated, plant_id=' + request.body.plant_id);
response.status(200).json(obj);
} catch (ex) {
diff --git a/docs/API.md b/docs/API.md
index 3425f61..792abc5 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -79,6 +79,11 @@ Alle HTTP Aufrufe sind englisch und kleingeschrieben!
- GET
- `…/api/plants/all`
- Liefert alle JSON Objekte vom Typ Plant
+ - Pflanzen die kompostiert wurden werden nicht berücksichtigt
+- GET
+ - `…/api/plants/composted`
+ - Liefert alle JSON Objekte vom Typ Plant
+ - Nur Pflanzen die kompostiert wurden werden berücksichtigt
- GET
- `…/api/plants/exists/[id]`
- Prüft nach, ob ein Objekt vom Typ Plant unter dieser [id] existiert
@@ -110,7 +115,8 @@ Beispiel:
"watering_interval_calculated": 11,
"days_since_watering": 10,
"days_until_watering": 1,
- "repotted": "2023-03-14"
+ "repotted": "2023-03-14",
+ "composted": null
}
```
### Attribute
@@ -178,6 +184,12 @@ Beispiel:
- Durch Backend berechneter Wert
- Datum des letzten Umtopfen
- Wenn noch nie umgetopft Datum des hinzufügens
+- composted
+ - Typ: Text(Datum)
+ - Pflichtfeld bei: nie
+ - Nullbar: ja
+ - Datum des soften-löschens
+ - Wenn die Pflanze noch nicht soft-gelöscht wurde null
## Activities
- GET
diff --git a/frontend/.dockerignore b/frontend/.dockerignore
index 12e03c3..3ca437f 100644
--- a/frontend/.dockerignore
+++ b/frontend/.dockerignore
@@ -3,4 +3,5 @@ npm-debug.log
Dockerfile
.dockerignore
jsconfig.json
-eslint.config.mjs
\ No newline at end of file
+eslint.config.mjs
+*/dev/*
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
index f88f368..bf2cfb4 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "plant_manager_frontend",
- "version": "1.0.0",
+ "version": "1.1.0",
"description": "Frontend for PlantManager",
"main": "client.js",
"scripts": {
diff --git a/frontend/public/css/style.css b/frontend/public/css/style.css
index 8774651..7f35275 100644
--- a/frontend/public/css/style.css
+++ b/frontend/public/css/style.css
@@ -91,6 +91,10 @@ main {
background-color: white !important;
}
+.list-group-item {
+ background-color: white !important;
+}
+
.text-water {
color: var(--qd-color-water);
}
diff --git a/frontend/public/detailseite_pflanze.html b/frontend/public/detailseite_pflanze.html
index c881906..87aef9d 100644
--- a/frontend/public/detailseite_pflanze.html
+++ b/frontend/public/detailseite_pflanze.html
@@ -42,6 +42,9 @@