Introduction

This project puts myCBR into Spring.io and provides the created REST calls through a Swagger API

How to

  • Build and install the newest myCBR-SDK
  • First clone the project
  • To build it go into its root folder and run:
mvn clean install
  • In order to deploy the Spring app, run:
java -jar ./target/mycbr-rest-example-1.0-SNAPSHOT.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

Functionalities

  • 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

Customization to other myCBR projects

  • 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

REST API

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.

Semantics

/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.

Not yet implemented..

/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);
   ...
}

Examples

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()