diff --git a/API/Classes/Base/ModelVersionClass.py b/API/Classes/Base/ModelVersionClass.py
new file mode 100644
index 000000000..cf8120bb4
--- /dev/null
+++ b/API/Classes/Base/ModelVersionClass.py
@@ -0,0 +1,79 @@
+import logging
+
+from Classes.Base.CustomExceptionClass import CustomException
+
+CURRENT_MODEL_VERSION = "5.0"
+
+
+class ModelVersionError(CustomException):
+ def __init__(self, message, case=None, detected_version=None, expected_version=None):
+ payload = {
+ "status_code": "version_mismatch",
+ "case": case,
+ "detected_version": detected_version,
+ "expected_version": expected_version or CURRENT_MODEL_VERSION,
+ }
+ CustomException.__init__(self, message, status_code=409, payload=payload)
+
+
+def get_model_version(genData):
+ if not isinstance(genData, dict):
+ return None
+
+ version = genData.get("modelVersion")
+ if version is None:
+ version = genData.get("osy-version")
+
+ if version is None:
+ return None
+
+ version = str(version).strip()
+ return version or None
+
+
+def stamp_model_version(genData, version=CURRENT_MODEL_VERSION):
+ if genData is None:
+ return genData
+
+ version = str(version).strip() or CURRENT_MODEL_VERSION
+ genData["modelVersion"] = version
+ genData["osy-version"] = version
+ return genData
+
+
+def validate_model_version(genData, case=None):
+ detected_version = get_model_version(genData)
+
+ if detected_version == CURRENT_MODEL_VERSION:
+ return detected_version
+
+ if case:
+ case_label = f"Model {case}"
+ else:
+ case_label = "Selected model"
+
+ if detected_version is None:
+ message = (
+ f"{case_label} is missing schema version metadata. "
+ f"Open the model configuration page and click Update model "
+ f"before generating data or running the solver."
+ )
+ else:
+ message = (
+ f"{case_label} uses schema version {detected_version}, but the current backend "
+ f"expects {CURRENT_MODEL_VERSION}. Open the model configuration page and click "
+ f"Update model before generating data or running the solver."
+ )
+
+ logging.warning(
+ "Model version mismatch for case '%s': detected=%s expected=%s",
+ case,
+ detected_version,
+ CURRENT_MODEL_VERSION,
+ )
+ raise ModelVersionError(
+ message,
+ case=case,
+ detected_version=detected_version,
+ expected_version=CURRENT_MODEL_VERSION,
+ )
diff --git a/API/Classes/Case/ImportTemplate.py b/API/Classes/Case/ImportTemplate.py
index 3ae0033e5..b6d1f7b23 100644
--- a/API/Classes/Case/ImportTemplate.py
+++ b/API/Classes/Case/ImportTemplate.py
@@ -5,6 +5,7 @@
from Classes.Base import Config
from Classes.Case.CaseClass import Case
from Classes.Base.FileClass import File
+from Classes.Base.ModelVersionClass import stamp_model_version
class ImportTemplate():
def __init__(self,template):
@@ -814,6 +815,7 @@ def importProcess(self, data):
genData["osy-scenarios"] = self.defaultScenario(True)
genData["osy-constraints"] = []
genData["osy-years"] = yearsArray
+ stamp_model_version(genData, version)
casename = genData['osy-casename']
diff --git a/API/Classes/Case/OsemosysClass.py b/API/Classes/Case/OsemosysClass.py
index 5ef3a5e53..d9e5e13ce 100644
--- a/API/Classes/Case/OsemosysClass.py
+++ b/API/Classes/Case/OsemosysClass.py
@@ -4,6 +4,7 @@
import shutil
from Classes.Base import Config
from Classes.Base.FileClass import File
+from Classes.Base.ModelVersionClass import validate_model_version
class Osemosys():
def __init__(self, case):
@@ -11,6 +12,7 @@ def __init__(self, case):
self.PARAMETERS = File.readParamFile(Path(Config.DATA_STORAGE, 'Parameters.json'))
self.VARIABLES = File.readParamFile(Path(Config.DATA_STORAGE, 'Variables.json'))
self.genData = File.readFile(Path(Config.DATA_STORAGE,case,'genData.json'))
+ validate_model_version(self.genData, case)
self.resData = File.readFile( Path(Config.DATA_STORAGE, case,'view', 'resData.json'))
#Case.__init__(self, case)
diff --git a/API/Routes/Case/CaseRoute.py b/API/Routes/Case/CaseRoute.py
index d95482f41..3e05f4853 100644
--- a/API/Routes/Case/CaseRoute.py
+++ b/API/Routes/Case/CaseRoute.py
@@ -5,6 +5,7 @@
import pandas as pd
from Classes.Base import Config
from Classes.Base.FileClass import File
+from Classes.Base.ModelVersionClass import stamp_model_version
from Classes.Case.CaseClass import Case
from Classes.Case.UpdateCaseClass import UpdateCase
from Classes.Case.ImportTemplate import ImportTemplate
@@ -248,6 +249,7 @@ def updateData():
def saveCase():
try:
genData = request.json['data']
+ stamp_model_version(genData)
casename = genData['osy-casename']
case = session.get('osycase', None)
diff --git a/API/Routes/Upload/UploadRoute.py b/API/Routes/Upload/UploadRoute.py
index 3aef38b26..ed6d885ce 100644
--- a/API/Routes/Upload/UploadRoute.py
+++ b/API/Routes/Upload/UploadRoute.py
@@ -9,6 +9,7 @@
from Classes.Base import Config
from Classes.Base.FileClass import File
+from Classes.Base.ModelVersionClass import stamp_model_version
upload_api = Blueprint('UploadRoute', __name__)
@@ -138,6 +139,12 @@ def updateStorageSet(casename):
File.writeFile( genData, genDataPath)
+def stampCurrentModelVersion(casename):
+ genDataPath = Path(Config.DATA_STORAGE, casename, 'genData.json')
+ genData = File.readParamFile(genDataPath)
+ stamp_model_version(genData)
+ File.writeFile(genData, genDataPath)
+
def updateViewDefintions(casename):
viewDataPath = Path(Config.DATA_STORAGE,casename,'view','viewDefinitions.json')
viewDefExisting = File.readParamFile(viewDataPath)
@@ -371,6 +378,7 @@ def uploadCaseUnchunked_old():
elif name == '5.0':
zf.extractall(os.path.join(Config.EXTRACT_FOLDER))
updateViewDefintions(casename)
+ stampCurrentModelVersion(casename)
msg.append({
"message": "Model " + casename +" have been uploaded!",
"status_code": "success",
@@ -511,6 +519,7 @@ def handle_full_zip(file, filepath=None):
elif name == '5.0':
zf.extractall(os.path.join(Config.EXTRACT_FOLDER))
updateViewDefintions(casename)
+ stampCurrentModelVersion(casename)
msg.append({
"message": "Model " + casename +" have been uploaded!",
"status_code": "success",
@@ -652,4 +661,4 @@ def uploadXls():
except(IOError):
raise IOError
except OSError:
- raise OSError
\ No newline at end of file
+ raise OSError
diff --git a/API/app.py b/API/app.py
index 458aad034..bc70e8888 100644
--- a/API/app.py
+++ b/API/app.py
@@ -27,6 +27,7 @@
#import json
from Classes.Base import Config
+from Classes.Base.CustomExceptionClass import CustomException
# from API.Classes.Base.SyncS3 import SyncS3
from Routes.Upload.UploadRoute import upload_api
from Routes.Case.CaseRoute import case_api
@@ -97,11 +98,11 @@ def add_headers(response):
#response.headers['Content-Type'] = 'application/javascript'
return response
-# @app.errorhandler(CustomException)
-# def handle_invalid_usage(error):
-# response = jsonify(error.to_dict())
-# response.status_code = error.status_code
-# return response
+@app.errorhandler(CustomException)
+def handle_invalid_usage(error):
+ response = jsonify(error.to_dict())
+ response.status_code = error.status_code
+ return response
#entry point to frontend
@app.route("/", methods=['GET'])
diff --git a/WebAPP/App/Controller/AddCase.js b/WebAPP/App/Controller/AddCase.js
index e3e542e1c..6d2f998ea 100644
--- a/WebAPP/App/Controller/AddCase.js
+++ b/WebAPP/App/Controller/AddCase.js
@@ -229,6 +229,7 @@ export default class AddCase {
});
let POSTDATA = {
+ "modelVersion": "5.0",
"osy-version": "5.0",
"osy-casename": casename,
"osy-desc": desc,
@@ -1144,4 +1145,3 @@ export default class AddCase {
-
diff --git a/WebAPP/App/Controller/Home.js b/WebAPP/App/Controller/Home.js
index d3448eea5..5c300a8e0 100644
--- a/WebAPP/App/Controller/Home.js
+++ b/WebAPP/App/Controller/Home.js
@@ -85,12 +85,11 @@ export default class Home {
//Sidebar.Load(casename, model.genData, model.PARAMETERS);
Osemosys.getData(casename, 'genData.json')
.then(genData => {
- //console.log('genData ', genData["osy-version"])
+ let modelVersion = parseFloat(genData.modelVersion || genData["osy-version"] || 0);
Home.refreshPage(casename);
Message.smallBoxInfo("Case selection", casename + " is selected!", 3000);
- if(parseFloat(genData["osy-version"]) < 4.5){
- //console.log('manje od 4.5')
- Message.bigBoxWarning("Warning", "You have selected a model created in a earlier version of this UI. In order to update to the current version click Update model on the configuration page.", 10000);
+ if(modelVersion < 5.0){
+ Message.bigBoxWarning("Warning", "You have selected a model created with an older schema version. Open Model configuration and click Update model before generating data or running the solver.", 10000);
}
})
});
diff --git a/WebAPP/App/Controller/LegacyImport.js b/WebAPP/App/Controller/LegacyImport.js
index bceb3179e..2e5e1e1b9 100644
--- a/WebAPP/App/Controller/LegacyImport.js
+++ b/WebAPP/App/Controller/LegacyImport.js
@@ -117,6 +117,7 @@ export default class LegacyImport {
}
let POSTDATA = {
+ "modelVersion": "5.0",
"osy-version": "5.0",
"osy-casename": casename,
"osy-desc": desc,
@@ -154,4 +155,4 @@ export default class LegacyImport {
$('#definition').toggle('slow');
});
}
-}
\ No newline at end of file
+}
diff --git a/WebAPP/Classes/Base.Class.js b/WebAPP/Classes/Base.Class.js
index 40befa648..3cae1027e 100644
--- a/WebAPP/Classes/Base.Class.js
+++ b/WebAPP/Classes/Base.Class.js
@@ -2,6 +2,13 @@ import { Message } from "./Message.Class.js";
import { Html } from "./Html.Class.js";
import { SyncS3 } from "./SyncS3.Class.js";
+function getApiError(xhr, error) {
+ if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
+ return xhr.responseJSON.message;
+ }
+ return error;
+}
+
export class Base {
static HEROKU = 0;
static AWS_SYNC = 0;
@@ -29,8 +36,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -50,8 +56,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -73,8 +78,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -93,9 +97,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
-
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -114,8 +116,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -134,8 +135,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -154,9 +154,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- //custom exception
- if (xhr.responseJSON && xhr.responseJSON.message) { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -175,9 +173,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- //custom exception
- if (xhr.responseJSON && xhr.responseJSON.message) { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -196,9 +192,7 @@ export class Base {
resolve(result);
},
error: function (xhr, status, error) {
- //custom exception
- if (error == 'UNKNOWN') { error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -219,9 +213,7 @@ export class Base {
resolve(result);
},
error: function(xhr, status, error) {
- //custom exception
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
diff --git a/WebAPP/Classes/Osemosys.Class.js b/WebAPP/Classes/Osemosys.Class.js
index 602802e44..ffb5b0651 100644
--- a/WebAPP/Classes/Osemosys.Class.js
+++ b/WebAPP/Classes/Osemosys.Class.js
@@ -1,5 +1,12 @@
import { Base } from "./Base.Class.js";
+function getApiError(xhr, error) {
+ if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
+ return xhr.responseJSON.message;
+ }
+ return error;
+}
+
export class Osemosys {
static getParamFile(dataJson='Parameters.json') {
@@ -16,8 +23,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -37,8 +43,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -57,8 +62,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -80,8 +84,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -100,8 +103,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -120,8 +122,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -143,8 +144,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -166,8 +166,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -186,8 +185,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -209,8 +207,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -233,8 +230,7 @@ export class Osemosys {
},
error: function(xhr, status, error) {
console.log("xhr, status, error ", xhr, status, error )
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -257,8 +253,7 @@ export class Osemosys {
},
error: function(xhr, status, error) {
console.log("xhr, status, error ", xhr, status, error )
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -281,8 +276,7 @@ export class Osemosys {
},
error: function(xhr, status, error) {
console.log("xhr, status, error ", xhr, status, error )
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -304,8 +298,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -327,8 +320,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -347,8 +339,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -466,8 +457,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -486,8 +476,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -521,8 +510,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -542,8 +530,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -563,8 +550,7 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
@@ -583,10 +569,9 @@ export class Osemosys {
resolve(result);
},
error: function(xhr, status, error) {
- if(error == 'UNKNOWN'){ error = xhr.responseJSON.message }
- reject(error);
+ reject(getApiError(xhr, error));
}
});
});
}
-}
\ No newline at end of file
+}