This project puts myCBR into Spring.io and provides the created REST calls through a Swagger API
- Build and install the newest myCBR-SDK
- Clone this project
- Add myCBR to the API
- To build it go into its root folder and run:
Next you will have to download and build the Rest API. As the Rest API uses the myCBR SDK you need to install is using the following command:
cd ../mycbr-rest mvn install:install-file -Dfile=../mycbr-sdk/target/myCBR-x.x-SNAPSHOT.jar -DpomFile=../mycbr-sdk/pom.xml -DlocalRepositoryPath=lib/no/ntnu/mycbr/mycbr-sdk/
For these calls we assume that both the mycbr-sdk and mycbr-rest are located in the same folder:
root |__ mycbr-sdk |__pom.xml |__src |__target |__myCBR-x.x-SNAPSHOT.jar |__ mycbr-rest |__pom.xml |__src |__target |__mycbr-rest-x.x-SNAPSHOT.jar |__lib/no/ntnu/mycbr/mycbr-sdk/
mvn clean install or mvn clean install -DskipTests=True
- In order to deploy the Spring app, run:
java -DMYCBR.PROJECT.FILE={path to the project file} -jar ./target/mycbr-rest-2.0.jar
- After Spring has started, you can find the API documentation here: http://localhost:8080/swagger-ui.html#!/
- Greeting controller is the sample I used to build the app
- CBR Controller contains the myCBR bits
- ctrl + C shuts down the server
- The goal is to provide the entire retrieval, however, so far only the model is working ## GET Requests
- /case provides the case content
- /concepts provides all concept names in the project
- /casebase provides the name(s) of the case bases associated with the project
- /retieval provides the similarity-based retrieval either by specifying symbols or an id of existing cases
- /attributes provides a list of attributes and their value types
- /values provides the list of allowed values for SymbolDesc attributes and min/max for IntegerDesc/DoubleDesc/FloatDesc ## POST Requests
- /retrieval allows you to post a query with a number of attributes,
e.g.:
{"Doors": 4, "Model": "e_300_diesel", "Manufacturer": "mercedes-benz","Color":"yellow, blue"}
- Currently only Symbol, Integer, Double and Float attributes are supported.
- Currently only Multiple Symbol attributes are supported
- the number of returned cases can be set, use -1 for returning all
- The app requires a myCBR project, which should be put into mycbr-rest-example/src/main/resources
- In order to detect the project file, CBREngine.java has to be adapted:
- private static String projectName = “used\_cars\_flat.prj” should be changed to the project file’s name
- The rest of the API is independent from the project, hence only the parameters such as case base and comcept names have to be adapted to the new project
The REST API created for the mycbr backend is trying to adhere to the REST semantics.
- POST: POST operations should be used when the REST api user does not know exactly where the item in question should be located. Typically due to undefined behaviour or bad documentation. This operation shold return a URL that contains an ID for that item, that also gives the user the location the REST server moved the item to.
- PUT: Instead PUT should be used to put an item at the exact location. This operation should typically contain a ID/name given by the REST api user. It should return a failure if the item already exists.
- UPDATE: Both of the above URLs (returned by REST server on the first instance, and specified by the user on the second) should accept UPDATE operations on the item in question.
- DELETE: this also goes for deleting items from that URL.
Some concepts in mycbr is not so easy to represent in REST, such as one-to-many relations such as those between casebases and concepts. One concept could have one or more casebases for segmenting instances of that concept into more case bases.
Also recursive attributes, such as concepts that are attributes of other concepts, which could result in circular dependencies, which should be restricted on the REST server. Right now directed acyclic graph of concepts are supported, and DELETE operations are now supported for those types of structures.
However not everything representable in mycbr has been implemented to that standard yet.
/instances /casebases /concepts /concepts/{conceptID}/amalgamationFunction /concepts/{conceptID}/attribute /concepts/{conceptID}/attribute/{attributeID} /concepts/{conceptID}/attribute/{attributeID}/similarityFunctions /concepts/{conceptID}/attribute/{attributeID}/similarityFunctions/{similarityFunctionID} /concepts/{conceptID}/instances
Semantically one can PUT and DELETE to each of these endpoints, such that a HTTP DELETE to “http://host:port/casbases” would delete all casebases. Each PUT operation has it’s own associated JSON format, e.g. for describing a instances or a similarity function of a concept or attribute. Below we describe the JSON format for each of the PUT operations.
/concepts/{conceptID}/concepts
Could be implemented in ConceptController.java like this..
@RequestMapping("/concepts/{conceptID}/**")
public void foo(@PathVariable("conceptID") int id, HttpServletRequest request) {
String restOfTheUrl = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
...
}
Using the REST api from other programming languages is quite easy, below we provide an example of how to do this in python. This is just a base class used to build unit tests for mycbr from python. More example code can be found in ./examples/
from mycbrwrapper.rest import getRequest
import unittest
__name__ = "test_base"
defaulthost = "localhost:8080"
"""
The model of the case base for the unit tests are simple
id,name,doubleattr1,doubleattr2
"""
class CBRTestCase(unittest.TestCase):
casesJSON = """{"cases" : [
{
"wind_speed": "0",
"wind_from_direction": "0",
"wind_effect": "0"
},
{
"wind_speed": "5.2",
"wind_from_direction": "279",
"wind_effect": "5.3"
},
{
"wind_speed": "2.1",
"wind_from_direction": "339",
"wind_effect": "1.05"
}
]}"""
localSimID = "testLocalSimilarityFunction"
localSimJSON = """{{
"id"="{}"
"type"="Double"
"subtype"="Polywidth"
"parameters"="4.5"
}}""".format(localSimID)
amalgamationSimID = "testAmalgmamationSimilarityFunction1"
# here type can be either of MINIMUM, MAXIMUM, WEIGHTED_SUM, EUCLIDEAN, NEURAL_NETWORK_SOLUTION_DIRECTLY,SIM_DEF;
def __init__(self, *args, **kwargs):
super(CBRTestCase, self).__init__(*args, **kwargs)
@classmethod
def getAttributeParamterJSON(cls,min,max):
return """
{{
"type": "Double",
"min": "{}",
"max": "{}"
}}
""".format(min,max)
@classmethod
def setUpClass(cls):
print("in super setupclass")
cls.createTestCaseBase()
cls.createConcept()
cls.createAttributes()
cls.createLocalSimilarityFunctions()
cls.createAmalgamationFunctions()
cls.createCases()
@classmethod
def tearDownClass(cls):
print("in super teardownclass")
cls.destroyCases()
cls.destroyLocalSimilarityFunctions()
cls.destroyAmalgamationFunctions()
cls.destroyAttributes()
cls.destroyConcept()
cls.destroyTestCaseBase()
@classmethod
def createTestCaseBase(cls, host=defaulthost):
print("in createTestCaseBase")
api = getRequest(host)
call = api.casebases
result = call.PUT("unittestCB")
print("url : {}".format(call._url))
print("result : {}".format(result))
@classmethod
def createConcept(cls, host=defaulthost):
"""
This is now working, it creates a concept.
"""
print("in createconcept")
api = getRequest(host)
call = api.concepts
result = call.PUT("testconcept")
print("url : {}".format(call._url))
print("result : {}".format(result))
@classmethod
def createAttributes(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept").attributes\
.PUT("wind_speed",params={"attributeJSON":cls.getAttributeParamterJSON(0,25)})
api.concepts("testconcept").attributes\
.PUT("wind_from_direction",params={"attributeJSON":cls.getAttributeParamterJSON(0,361)})
api.concepts("testconcept").attributes\
.PUT("wind_effect",params={"attributeJSON":cls.getAttributeParamterJSON(0,40)})
@classmethod
def createLocalSimilarityFunctions(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept").attributes("wind_speed")\
.similarityfunctions\
.PUT(cls.localSimJSON)
@classmethod
def createAmalgamationFunctions(cls, host=defaulthost):
api = getRequest(host)
call = api.concepts("testconcept").amalgamationFunctions(cls.amalgamationSimID)
result = call.PUT(params={"amalgamationFunctionType":"NEURAL_NETWORK_SOLUTION_DIRECTLY"})
print("add alg url {} result {}".format(call._url,result))
@classmethod
def createCases(cls, host=defaulthost):
print("in createcases")
api = getRequest(host)
call = api.concepts("testconcept").casebases("unittestCB").instances
call.PUT(params={'cases':cls.casesJSON})
print("url: {} ".format(call._url))
@classmethod
def destroyTestCaseBase(cls, host=defaulthost):
api = getRequest(host)
api.casebases("unittestCB").DELETE()
@classmethod
def destroyConcept(cls, host=defaulthost):
api = getRequest(host)
call = api.concepts("testconcept")
call.DELETE()
print("in delete concept url is: {}".format(call._url))
@classmethod
def destroyAttributes(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept").attributes("wind_speed").DELETE()
api.concepts("testconcept").attributes("wind_from_direction").DELETE()
api.concepts("testconcept").attributes("wind_effect").DELETE()
@classmethod
def destroyLocalSimilarityFunctions(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept").attributes("wind_speed")\
.similarityfunction(cls.localSimID)\
.DELETE()
@classmethod
def destroyAmalgamationFunctions(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept")\
.amalgamationFunctions(cls.amalgamationSimID).DELETE()
@classmethod
def destroyCases(cls, host=defaulthost):
api = getRequest(host)
api.concepts("testconcept").casebases("unittestCB").instances.DELETE()
if __name__ == "__main__":
unittest.main()