commit 0017e5efda2b4ac58ef27ed9cc192d8d9ae3eab2 Author: Miguel Coronado Date: Tue Mar 20 17:16:44 2012 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..56fb545 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build.properties diff --git a/Web40SOJason.mas2j b/Web40SOJason.mas2j new file mode 100644 index 0000000..1749156 --- /dev/null +++ b/Web40SOJason.mas2j @@ -0,0 +1,19 @@ +/* + Jason Project + + -- create on March 19, 2012 +*/ + +MAS web40sojason { + infrastructure: Centralised + + environment: es.upm.dit.gsi.sojason.SOEnvironment + + agents: + userAgent; + travelAgent; + commonSenseAgent; + nluAgent; + + aslSourcePath: "src/asl"; +} \ No newline at end of file diff --git a/bin/build.xml b/bin/build.xml new file mode 100644 index 0000000..9a9fe68 --- /dev/null +++ b/bin/build.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/classes/es/upm/dit/gsi/jason/utils/CollectionUtils.class b/bin/classes/es/upm/dit/gsi/jason/utils/CollectionUtils.class new file mode 100644 index 0000000..7378c3d Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/jason/utils/CollectionUtils.class differ diff --git a/bin/classes/es/upm/dit/gsi/jason/utils/NotationUtils.class b/bin/classes/es/upm/dit/gsi/jason/utils/NotationUtils.class new file mode 100644 index 0000000..207ff1a Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/jason/utils/NotationUtils.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/SOEnvironment.class b/bin/classes/es/upm/dit/gsi/sojason/SOEnvironment.class new file mode 100644 index 0000000..06c7060 Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/SOEnvironment.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/SOModel.class b/bin/classes/es/upm/dit/gsi/sojason/SOModel.class new file mode 100644 index 0000000..442a264 Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/SOModel.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/Web40Model.class b/bin/classes/es/upm/dit/gsi/sojason/Web40Model.class new file mode 100644 index 0000000..be50755 Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/Web40Model.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/beans/ErrorReport.class b/bin/classes/es/upm/dit/gsi/sojason/beans/ErrorReport.class new file mode 100644 index 0000000..637208f Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/beans/ErrorReport.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/beans/Journey.class b/bin/classes/es/upm/dit/gsi/sojason/beans/Journey.class new file mode 100644 index 0000000..9c712ab Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/beans/Journey.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/beans/NLUTravel.class b/bin/classes/es/upm/dit/gsi/sojason/beans/NLUTravel.class new file mode 100644 index 0000000..a2ffa2e Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/beans/NLUTravel.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/beans/Perceptable.class b/bin/classes/es/upm/dit/gsi/sojason/beans/Perceptable.class new file mode 100644 index 0000000..6a8207e Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/beans/Perceptable.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/beans/TravelType.class b/bin/classes/es/upm/dit/gsi/sojason/beans/TravelType.class new file mode 100644 index 0000000..daef72a Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/beans/TravelType.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/services/WebServiceConnector.class b/bin/classes/es/upm/dit/gsi/sojason/services/WebServiceConnector.class new file mode 100644 index 0000000..17b618f Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/services/WebServiceConnector.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.class b/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.class new file mode 100644 index 0000000..641e13e Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUModel.class b/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUModel.class new file mode 100644 index 0000000..e527e36 Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/services/nlu/NLUModel.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.class b/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.class new file mode 100644 index 0000000..115401c Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.class differ diff --git a/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.class b/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.class new file mode 100644 index 0000000..d2f0481 Binary files /dev/null and b/bin/classes/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.class differ diff --git a/conf/cities.xml b/conf/cities.xml new file mode 100644 index 0000000..accfdcf --- /dev/null +++ b/conf/cities.xml @@ -0,0 +1,152 @@ + + + +Renfe city codes +37200 +13200 +56312 +LISBO +15100 +83005 +51400 +31412 +20300 +23004 +60911 +85031 +50102 +65318 +54413 +50300 +82100 +67200 +61307 +51405 +03100 +CUENC +83002 +50500 +94428 +11014 +60911 +94401 +54413 +15009 +11208 +85444 +50500 +11600 +21010 +14223 +92102 +35400 +35400 +20309 +85410 +31412 +03213 +ZARAG +11014 +04307 +SALAM +60905 +60911 +15211 +37500 +06006 +85031 +PTE G +83111 +94401 +94707 +BARCE +56312 +VALEN +65300 +04307 +69110 +03213 +37606 +51405 +65300 +85200 +03100 +87004 +10600 +87546 +78400 +GIJON +55020 +Guixar=22308 +81100 +78301 +37500 +87011 +71400 +85410 +11511 +81202 +83111 +14100 +54413 +31412 +13200 +06006 +87011 +MADRI +78301 +GUADA +64100 +61200 +35206 +83002 +SEGOV +85444 +87011 +69110 +54400 +87004 +15100 +65300 +51400 +60400 +30200 +31400 +ANTEQ +87173 +11511 +80100 +60902 +65304 +83008 +10400 +80100 +05000 +GIJON +22100 +83002 +82100 +37300 +65304 +74200 +11511 +11602 +83111 +20200 +43019 +79315 +60400 +87011 +54413 +11208 +15009 +11200 +11600 +10500 +70600 +79300 +51003 +51300 +87034 +65300 + diff --git a/lib/jackson-all-1.8.8.jar b/lib/jackson-all-1.8.8.jar new file mode 100644 index 0000000..0b1cba0 Binary files /dev/null and b/lib/jackson-all-1.8.8.jar differ diff --git a/lib/json-lib-2.4-jdk15.jar b/lib/json-lib-2.4-jdk15.jar new file mode 100644 index 0000000..68d4f3b Binary files /dev/null and b/lib/json-lib-2.4-jdk15.jar differ diff --git a/lib/jsoup-1.6.1.jar b/lib/jsoup-1.6.1.jar new file mode 100644 index 0000000..87126a4 Binary files /dev/null and b/lib/jsoup-1.6.1.jar differ diff --git a/src/asl/commonSenseAgent.asl b/src/asl/commonSenseAgent.asl new file mode 100644 index 0000000..9219e22 --- /dev/null +++ b/src/asl/commonSenseAgent.asl @@ -0,0 +1,8 @@ +// Agent userAgent in project Web40 + +/* Initial beliefs and rules */ + +/* Initial goals */ + + +/* Plans */ diff --git a/src/asl/nluAgent.asl b/src/asl/nluAgent.asl new file mode 100644 index 0000000..f160774 --- /dev/null +++ b/src/asl/nluAgent.asl @@ -0,0 +1,40 @@ +// Agent nluAgent in project Web40SOJason + +/* Initial beliefs and rules */ + +/* Initial goals */ + +/* Plans */ + +@in_msg ++user_msg(Msg, Query) : true + <- sendNLU(Query, Msg); + -user_msg(Msg, Query). // clear the memory + + +/* Tell the user agent what the NLU system understood */ ++price(Terms, Price)[query(Query), domain(travel)] : true + <- .send(userAgent, tell, price(Terms, Price)[query(Query)], domain(travel)); + .print("Percibido: price ",Terms, " ", Price ). + ++date(Terms, Day, Month, Year)[query(Query), domain(travel)] : true + <- .send(userAgent, tell, date(Terms, Day, Month, Year)[query(Query), domain(travel)]); + .print("Percibido: date ",Terms, " ", Day, " ", Month, " ", Year). + ++time(Terms, Hours, Minutes)[query(Query), domain(travel)] : true + <- .send(userAgent, tell, time(Terms, Hours, Minutes)[query(Query), domain(travel)]); + .print("Percibido: time ",Terms, " ", Hours, " ", Minutes). + ++location(Terms, Place)[query(Query), domain(travel)] : true + <- .send(userAgent, tell, location(Terms, Place)[query(Query), domain(travel)]); + .print("Percibido: location ",Terms, " ", Place). + ++type(Terms)[query(Query), domain(travel)] : true + <- .send(userAgent, tell, type(Terms)[query(Query), domain(travel)]); + .print("Percibido: type ",Terms). + +@sendFindTravel ++done[query(Query), domain(Domain)] : true + <- .wait(1000); // wait until all other information is sent + .print("Percepcion completada"); + .send(userAgent, achieve, find(Domain, Query)). \ No newline at end of file diff --git a/src/asl/travelAgent.asl b/src/asl/travelAgent.asl new file mode 100644 index 0000000..7a6b517 --- /dev/null +++ b/src/asl/travelAgent.asl @@ -0,0 +1,60 @@ +// Agent travelAgent in project Web40 + +/* Initial beliefs and rules */ +canFindTravel(Query) + :- location(from,_)[query(Query)] & + location(to,_)[query(Query)] & + date(departure,_,_,_)[query(Query)]. + +/* Initial goals */ +contact(userAgent). +my_service(travel). +my_service(train). + +/************** Plans *****************/ + +/* Introduce myself to the user agent */ +@introduce_myself ++my_service(Domain) + : contact(Agent) & .my_name(Me) + <- .send(Agent, tell, service(Me, Domain)). + +@introduction_rety ++my_service(Domain) : not contact(Agent) + <- -+my_service(Domain). + + +/* Find travel plans */ +@findTravel1 ++!find(travel, Query) : not canFindTravel(Query) & not delay(Query) + <- .print("Not enought data. Lets wait some time"); + .wait(3000); + +delay(Query); + !find(travel, Query). + +@findTravel2 ++!find(travel, Query) : not canFindTravel(Query) & delay(Query) + <- -delay(Query); + .print("Not enought data. Lets ask!"). + +@findTravel3 ++!find(travel, Query) : canFindTravel(Query) + <- ?location(to, To); + ?location(from, From); + ?date(departure, Day, Month, Year); + findTravel(From, To, Day, Month, Year); + .print("ok"). + +@findTravelFailureRety +-!find(travel, Query) : not error(Msg, Query)<- !findTravel(Query). + +@findTravelFailureError +-!find(travel, Query) : error(Msg, Query) + <- .print("Problema al encontrar viajes:", Msg); + !findTravel(Query). + + +/* log results */ +@log_the_journey ++journey(From, To, Departure, Arrival, Fares) : true + <- .print("Travel found: From ", From,"<", Departure, "> to ", To, "<", Arrival, "> for ", Fares). \ No newline at end of file diff --git a/src/asl/userAgent.asl b/src/asl/userAgent.asl new file mode 100644 index 0000000..ee6cc48 --- /dev/null +++ b/src/asl/userAgent.asl @@ -0,0 +1,56 @@ +// Agent userAgent in project Web40 + +/* Initial beliefs and rules */ +new_query(Query) :- .random(R) & Query = (1000*R)+1. + +!start. + +/* Initial goals */ + +/******* Plans ***************************/ + +/* Wait for service introduction (temporal plan, to erase) */ ++!start : true + <- .wait(1000); + +user_msg("I want to travel from Madrid to Cuenca in the morning that costs no more than 200€ and dinner in a romantic restaurant"). + + +/* Ask the nlu agent */ ++user_msg(Msg) : new_query(Query) + <- .send(nluAgent, tell, user_msg(Msg, Query) ). + +/* Log the received data */ ++price(Terms, Price)[query(Query), domain(Domain)] : true + <- .print("Percibido: price ",Terms, " ", Price ); + +data(price(Terms), Query, Domain). + ++date(Terms, Day, Month, Year)[query(Query), domain(Domain)] : true + <- .print("Percibido: date ",Terms, " ", Day, " ", Month, " ", Year); + +data(date(Terms, Day, Month, Year), Query, Domain). + ++time(Terms, Hours, Minutes)[query(Query), domain(Domain)] : true + <- .print("Percibido: time ",Terms, " ", Hours, " ", Minutes); + +data(time(Terms, Hours, Minutes), Query, Domain). + ++location(Terms, Place)[query(Query), domain(Domain)] : true + <- .print("Percibido: location ",Terms, " ", Place); + +data(location(Terms, Place), Query, Domain). + ++type(Terms)[query(Query), domain(Domain)] : true + <- .print("Percibido: type ",Terms). + +/* find travel */ +/*@find_travel ++!find(travel, Query) : true + <- .println("lets find travel ", Query); + .findall(Name, service(Name, travel), List); + .send(List, achieve, find(travel, Query)). +*/ + +@do_search ++!find(Domain, Query) : true + <- .print("Perform find ", Domain, " ", Query); + .findall(Name, service(Name, Domain), AgList); + .findall(Atom[query(Query)], data(Atom, Query, Domain), DataList); + .send(AgList, tell, DataList); + .send(AgList, achieve, find(Domain, Query)). \ No newline at end of file diff --git a/src/java/es/upm/dit/gsi/jason/utils/CollectionUtils.java b/src/java/es/upm/dit/gsi/jason/utils/CollectionUtils.java new file mode 100644 index 0000000..b4cb3ce --- /dev/null +++ b/src/java/es/upm/dit/gsi/jason/utils/CollectionUtils.java @@ -0,0 +1,87 @@ +/** + * + */ +package es.upm.dit.gsi.jason.utils; + +import jason.asSyntax.Literal; + +import java.util.Collection; +import java.util.LinkedList; + +/** + * + * Project: Web40SOJason + * Package: es.upm.dit.gsi.jason.utils + * Class: CollectionUtils + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Mar 9, 2012 + * + */ +public abstract class CollectionUtils { + + /** + * This wraps a Literal in a collection + * @param literal The literal + * @return Collection containing the literal given + */ + public static Collection wrapList(Literal literal) { + Collection res = new LinkedList(); + res.add(literal); + return res; + } + + /** + * This wraps a Literal in a collection + * @param literal The string that represents a literal + * @return Collection containing the literal given + */ + public static Collection wrapList(String literal) { + Collection res = new LinkedList(); + res.add(Literal.parseLiteral(literal)); + return res; + } + + /** + * This + * @param collection + * @return + */ + public static String[] toStringArray (Collection collection){ + String[] strArray = new String[collection.size()]; + + int index = 0; + for(Object obj : collection){ + if(obj == null) { + strArray[index] = "null"; + } + else { + strArray[index] = obj.toString(); + } + index++; + } + + return strArray; + } + + /** + * + * @param items + * @return + */ + public static String[] toStringArray (Object[] items){ + String[] strArray = new String[items.length]; + + for(int index = 0; index < items.length; index++){ + Object obj = items[index]; + if(obj == null){ + strArray[index] = "null"; + } + else{ + strArray[index] = items[index].toString(); + } + } + + return strArray; + } +} diff --git a/src/java/es/upm/dit/gsi/jason/utils/NotationUtils.java b/src/java/es/upm/dit/gsi/jason/utils/NotationUtils.java new file mode 100644 index 0000000..d8ca7c7 --- /dev/null +++ b/src/java/es/upm/dit/gsi/jason/utils/NotationUtils.java @@ -0,0 +1,83 @@ +package es.upm.dit.gsi.jason.utils; + + +/** + * This Utils class is used to validate string according to the Jason atom + * notation criteria, and transform an invalid notation into a valid one + * and vice versa. + * + * This is useful in some context where the agents need to interact with an + * uncontrollable environment such as the Web. + * + * In Jason notation, white-spaces are not allowed, neither, words that starts + * with capital letter. + * + * @author gsi.dit.upm.es + * + */ +public class NotationUtils { + + /** + * @param toCheck + * @return + */ + public static boolean isValidAtom (String toCheck) { + String lowerCase = toCheck.toLowerCase(); + return !toCheck.contains(" ") && !toCheck.contains(",") && toCheck.equals(lowerCase); + } + + /** + * + * @param toCheck + * @return + */ + public static boolean isCompactable (String toCheck) { + return true; + } + + /** + * + * @param str + * @return + */ + public static String compact(String str) { + + if (isValidAtom (str)) {return str;} + if (!isCompactable(str)) {return null;} + + str = str.replace("_", "___"); + str = str.replace(" ", "_"); + str = str.replace("ñ", "n"); + return str.toLowerCase(); + } + + /** + * + * @param str + * @return + */ + public static String uncompact(String str) { + str = str.replace("___", "#"); + str = str.replace("_", " "); + str = str.replace("#", " "); + return str; + } + + /** + *

This removes the quotation mark from the string given. If that + * string has no quotation marks it returned trimmed.

+ * + *

The quotation marks are only removed from the beginning and the + * end of the string, so any quotation mark inserted in the middle of + * the string will be kept.

+ * + * @return the string without the quotation marks + */ + public static String removeQuotation (String str) { + String message = str.trim(); + if(message.startsWith("\"")) message = message.substring(1); + if(message.endsWith("\"")) message = message.substring(0, message.length()-1); + return message; + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/SOEnvironment.java b/src/java/es/upm/dit/gsi/sojason/SOEnvironment.java new file mode 100644 index 0000000..9ae340e --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/SOEnvironment.java @@ -0,0 +1,94 @@ +package es.upm.dit.gsi.sojason; +// Environment code for project Web40SOJason + +import jason.asSyntax.Literal; +import jason.asSyntax.Structure; +import jason.asSyntax.Term; +import jason.environment.Environment; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.logging.Logger; + +/** + * This represents a Software Oriented Environment. + * It overrides the getPercepts method so every time the agent perceives + * it checks the model to update the percepts for the particular agent. + * + * Project: Web40SOJason + * Package: es.upm.dit.gsi.sojason + * Class: SOEnvironment + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Mar 9, 2012 + * + */ +public class SOEnvironment extends Environment { + + /** The logger */ + private Logger logger = Logger.getLogger("Web40SOJason." + SOEnvironment.class.getName()); + + /** The model */ + public Web40Model model; + + /** Called before the MAS execution with the args informed in .mas2j */ + @Override + public void init(String[] args) { + super.init(args); + try { + this.model = new Web40Model(); + } catch (IOException e) { + addPercept(Literal.parseLiteral("error(\"Could not inatantiate the model\")")); + e.printStackTrace(); + } + } + + @Override + public boolean executeAction(String agName, Structure action) { + logger.info("executing: " + action + " (" + agName + ")"); + + // select the external action + boolean result = false; + String functor = action.getFunctor(); + List terms = action.getTerms(); + + if (functor.equals("sendNLU")) { + result = this.model.sendNlu(agName, terms); + } + else if (functor.equals("findTravel")) { + result = this.model.findTravel(agName, terms); + return true; + } + else { + logger.info(action + " was not implemented."); + } + + return result; + } + + /** Called before the end of MAS execution */ + @Override + public void stop() { + super.stop(); + } + + /** */ + @Override + public List getPercepts(String agName) { + updatePerceptsForAg(agName); + return super.getPercepts(agName); + } + + /** + * + * @param agName + */ + public void updatePerceptsForAg (String agName) { + clearPercepts(agName); + Collection literals = model.getDataFromInbox(agName); + for(Literal literal : literals){ + addPercept(agName, literal); + } + } +} diff --git a/src/java/es/upm/dit/gsi/sojason/SOModel.java b/src/java/es/upm/dit/gsi/sojason/SOModel.java new file mode 100644 index 0000000..4df52c2 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/SOModel.java @@ -0,0 +1,106 @@ +/** + * + */ +package es.upm.dit.gsi.sojason; + +import jason.asSyntax.Literal; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * + * + * Project: Web40SOJason + * Package: es.upm.dit.gsi.sojason + * Class: SOModel + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Mar 9, 2012 + * + */ +abstract public class SOModel { + + /** + * This contains the data that will be + */ + private Map> serviceDataInbox; + + /** + * Constructor. Just initializes attributes. + */ + public SOModel(){ + this.serviceDataInbox = Collections.synchronizedMap(new HashMap>()); + } + + /** + *

This puts data into the serviceDataInbox for a + * particular agent. The data loaded will be appended into the already + * existing data if any.

+ * + *

The data from the serviceDataInbox is removed as + * described in the documentation of {@link #getDataFromInbox(String)}

+ * + * @param agName The name of the agent. + * @param serviceData The service-data. + */ + public void setDataInbox (String agName, Collection serviceData){ + + synchronized (serviceDataInbox) { + if (!this.serviceDataInbox.containsKey(agName)) { + Set set = new HashSet(); + set.addAll(serviceData); // create a set and add all the collection + this.serviceDataInbox.put(agName, set); + return; + } + + // There is no data in the inbox for the agent given + Set set = this.serviceDataInbox.get(agName); + set.addAll(serviceData); + this.serviceDataInbox.put(agName, set); + } + + } + + /** + *

This provides a different way to call the method + * {@linkplain #setDataInbox(String, Collection)} with a single literal + * instead of a collection of literals.

+ * + * @param agName the name of the agent. + * @param literal the literal. + */ + public void setDataInbox (String agName, Literal literal) { + Set set = new HashSet(); + set.add(literal); + setDataInbox(agName, set); + } + + /** + *

This gets from the serviceDataInbox the data stored for + * the agent given. It will remove the data from the inbox, so two consequent + * invocations of this method will return different results, actually, if no + * new data is put, the second invocation will return no data.

+ * + *

So, it is important to point out this method empties the + * serviceDataInbox.

+ * + *

This method never returns null to avoid null pointer

+ * + * @param agName the name of the agent who data will be retrieved from + * the inbox + * @return The data retrieved + */ + public Collection getDataFromInbox (String agName) { + if (!this.serviceDataInbox.containsKey(agName)){ + return new HashSet(); + } +// return this.serviceDataInbox.get(agName); + return this.serviceDataInbox.remove(agName); + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/Web40Model.java b/src/java/es/upm/dit/gsi/sojason/Web40Model.java new file mode 100644 index 0000000..bcdcadc --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/Web40Model.java @@ -0,0 +1,98 @@ +package es.upm.dit.gsi.sojason; + +import jason.asSyntax.Literal; +import jason.asSyntax.Term; + +import java.io.IOException; +import java.util.Collection; +import java.util.logging.Logger; + +import es.upm.dit.gsi.jason.utils.CollectionUtils; +import es.upm.dit.gsi.sojason.services.nlu.NLUConnector; +import es.upm.dit.gsi.sojason.services.travel.RenfeScrapper; + +/** + * + * + * Project: Web40 + * Package: es.upm.dit.gsi.qa + * Class: Web40Model + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Feb 29, 2012 + * + */ +public class Web40Model extends SOModel{ + + /** */ + public final static String NLU_SERVICE_URL = "http://46.4.52.82:3333/nlu"; + /** */ + private NLUConnector nluConnector; + /** */ + private RenfeScrapper renfeScrapper; + /** */ + private Logger logger = Logger.getLogger("Web40SOJason." + Web40Model.class.getName()); + + /** Constructor + * @throws IOException */ + public Web40Model () throws IOException { + super(); + this.nluConnector = new NLUConnector(NLU_SERVICE_URL); + this.renfeScrapper = new RenfeScrapper(); + } + + /** + * This calls the NLU service + * + * Internally this modifies the model so it reports the agent + * + * @param agName the name of the agent that will be reported with the + * results of the call. + * @param terms The parameters + * @return + */ + public boolean sendNlu (String agName, Collection params) { + + logger.info("Entering sendNLU..."); + try{ + String[] strParams = CollectionUtils.toStringArray(params); + Collection serviceData = nluConnector.call(strParams); + if(serviceData == null){ + logger.info("Could not complete action sendNLU: no service data found"); + return false; + } + + // put data into mailbox + this.setDataInbox(agName, serviceData); + } + catch (Exception e){ + logger.info("Could not complete action sendNLU:" + e.getMessage()); + return false; + } + + logger.info("NLU call completed successfully"); + return true; + } + + /** + * + * @param agName + * @param terms + * @return + */ + public boolean findTravel (String agName, Collection params) { + + try{ + String[] strParams = CollectionUtils.toStringArray(params); + Collection serviceData = renfeScrapper.call(strParams); + if(serviceData == null){ return false; } + + // put data into mailbox + this.setDataInbox(agName, serviceData); + } + catch (Exception e){ return false; } + return true; + + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/beans/ErrorReport.java b/src/java/es/upm/dit/gsi/sojason/beans/ErrorReport.java new file mode 100644 index 0000000..f09603a --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/beans/ErrorReport.java @@ -0,0 +1,38 @@ +/** + * + */ +package es.upm.dit.gsi.sojason.beans; + +import jason.asSyntax.Literal; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import es.upm.dit.gsi.jason.utils.NotationUtils; + +/** + * @author miguel + * + */ +public class ErrorReport extends HashMap implements Perceptable { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /* (non-Javadoc) + * @see es.upm.dit.gsi.qa.beans.Perceptable#toPercepts() + */ + public List toPercepts() { + + List res = new LinkedList(); + for (String key : this.keySet()){ + if(!NotationUtils.isCompactable(key)) continue; + res.add(Literal.parseLiteral("error(" + NotationUtils.compact(key) + ", \"" + get(key) + "\")")); + } + return res; + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/beans/Journey.java b/src/java/es/upm/dit/gsi/sojason/beans/Journey.java new file mode 100644 index 0000000..003bd86 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/beans/Journey.java @@ -0,0 +1,217 @@ +/** + * + */ +package es.upm.dit.gsi.sojason.beans; + +import jason.asSyntax.Literal; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import es.upm.dit.gsi.jason.utils.NotationUtils; + +/** + * @author miguel + * + */ +public class Journey implements Perceptable{ + + /** The departure time of the journey */ + private String departureTime; + + /** The arrival time of the journey */ + private String arrivalTime; + + /** + * The duration of the journey. This is not simply the difference of the + * departure and arrival time because of timezone considerations. + */ + private String duration; + + /** The origin */ + private String origin; + + /** The destination */ + private String destination; + + /** The fee map that contains the different available fee */ + private Map fares; + + /** + * @return the departureTime + */ + public String getDepartureTime() { + return departureTime; + } + + /** + * @param departureTime the departureTime to set + */ + public void setDepartureTime(String departureTime) { + this.departureTime = departureTime; + } + + /** + * @return the arrivalTime + */ + public String getArrivalTime() { + return arrivalTime; + } + + /** + * @param arrivalTime the arrivalTime to set + */ + public void setArrivalTime(String arrivalTime) { + this.arrivalTime = arrivalTime; + } + + /** + * @return the duration + */ + public String getDuration() { + return duration; + } + + /** + * @param duration the duration to set + */ + public void setDuration(String duration) { + this.duration = duration; + } + + /** + * @return the oringin + */ + public String getOringin() { + return origin; + } + + /** + * @param oringin the oringin to set + */ + public void setOrigin(String origin) { + this.origin = origin; + } + + /** + * @return the destination + */ + public String getDestination() { + return destination; + } + + /** + * @param destination the destination to set + */ + public void setDestination(String destination) { + this.destination = destination; + } + + /** + * @return the fares + */ + public Map getFares() { + return fares; + } + + /** + * @param fares the fares to set + */ + public void setFares(Map fares) { + this.fares = fares; + } + + /** Textual representation of the journey. Use for debuging purposes inly.*/ + public String toString() { + + String toString = "From: "; + toString = toString.concat(origin); + toString = toString.concat(" ("); + toString = toString.concat(departureTime); + toString = toString.concat(") to: "); + toString = toString.concat(destination); + toString = toString.concat(" ("); + toString = toString.concat(arrivalTime); + toString = toString.concat(") in "); + toString = toString.concat(duration); + toString = toString.concat(" for "); + if(fares != null) + toString = toString.concat(fares.toString()); + else + toString += null; + return toString; + } + + /** + * journey(madrid, ciudad_real, 10.15, 11.5, [fare(turista, 22.5), fare(preferente, 35)] + * journey(madrid, ciudad_real, time(10,15), time(11,5), [fare(turista, 22.5), fare(preferente, 35)] + * + * @return + */ + public List toPercepts() { + +// if (this.fares.size() == 0){ return null; } +// +// String percept = "journey("; +// percept = percept.concat(this.origin); +// percept = percept.concat(", "); +// percept = percept.concat(this.destination); +// percept = percept.concat(", "); +// percept = percept.concat(this.departureTime); +// percept = percept.concat(", "); +// percept = percept.concat(this.arrivalTime); +// +// percept = percept.concat(", ["); +// for(String fareName : fares.keySet()) { +// percept = percept.concat("fare("); +// percept = percept.concat(fareName); +// percept = percept.concat(", "); +// percept = percept.concat(fares.get(fareName)); +// percept = percept.concat("), "); +// } +// percept = percept.substring(0, percept.lastIndexOf(",")); +// percept = percept.concat("])"); +// +// LinkedList ret = new LinkedList(); +// ret.add(Literal.parseLiteral(percept)); +// +// return ret; + + if (this.fares.size() == 0){ return null; } + + String percept = "journey("; + percept = percept.concat(NotationUtils.compact(this.origin)); + percept = percept.concat(", "); + percept = percept.concat(NotationUtils.compact(this.destination)); + + percept = percept.concat(", time("); + String digits[] = this.departureTime.split("[\\x2E\\x3A]"); // [.:] + percept = percept.concat(digits[0]); + percept = percept.concat(", "); + percept = percept.concat(digits[1]); + + percept = percept.concat("), time("); + digits = this.arrivalTime.split("[\\x2E\\x3A]"); // [.:] + percept = percept.concat(digits[0]); + percept = percept.concat(", "); + percept = percept.concat(digits[1]); + + percept = percept.concat("), ["); + for(String fareName : fares.keySet()) { + percept = percept.concat("fare("); + percept = percept.concat(NotationUtils.compact(fareName)); + percept = percept.concat(", "); + percept = percept.concat(fares.get(fareName)); + percept = percept.concat("), "); + } + percept = percept.substring(0, percept.lastIndexOf(",")); + percept = percept.concat("])"); + + LinkedList ret = new LinkedList(); + ret.add(Literal.parseLiteral(percept)); + + return ret; + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/beans/NLUTravel.java b/src/java/es/upm/dit/gsi/sojason/beans/NLUTravel.java new file mode 100644 index 0000000..511a5cf --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/beans/NLUTravel.java @@ -0,0 +1,347 @@ +package es.upm.dit.gsi.sojason.beans; + +import jason.asSyntax.Literal; + +import java.util.LinkedList; +import java.util.List; + +import es.upm.dit.gsi.jason.utils.NotationUtils; +import es.upm.dit.gsi.sojason.services.nlu.NLUModel; + +public class NLUTravel implements Perceptable { + + /** */ + public final static String TRAVEL_DOMAIN = "travel"; + + /** The departure date. */ + private String departureDate = null; + /** The return date. It may be null. */ + private String returnDate = null; + + /** The maximum price the user is willing to pay */ + private double priceMin = -1; + /** The minimum price */ + private double priceMax = -1; + /** The currency */ + private String currency = null; + + /** The departure location */ + private String locationFrom = null; + /** The destination location */ + private String locationTo = null; + + /** The preferred departure Time */ + private String departureTime = null; + /** the preferred return Time*/ + private String returnTime = null; + + /** the number of people is traveling */ + private int number = 1; + + /** */ + private TravelType type = null; + /** */ + private boolean scales = false; + + /** The unique id for the dialog. It is used to group dialog entries */ + private String queryId = null; + + /** + * @return the departureDate + */ + public String getDepartureDate() { + return departureDate; + } + + /** + * @param departureDate the departureDate to set + */ + public void setDepartureDate(String departureDate) { + this.departureDate = departureDate; + } + + /** + * @return the returnDate + */ + public String getReturnDate() { + return returnDate; + } + + /** + * @param returnDate the returnDate to set + */ + public void setReturnDate(String returnDate) { + this.returnDate = returnDate; + } + + /** + * @return the priceMin + */ + public double getPriceMin() { + return priceMin; + } + + /** + * @param priceMin the priceMin to set + */ + public void setPriceMin(double priceMin) { + this.priceMin = priceMin; + } + + /** + * @param priceMax the priceMax to set + */ + public void setPriceMin(String priceMin) { + if (priceMin == null){ return; } + + try{ + this.priceMin = Double.parseDouble(priceMin); + } + catch (NumberFormatException nfe){ + // Do nothing + } + } + + /** + * @return the priceMax + */ + public double getPriceMax() { + return priceMax; + } + + /** + * @param priceMax the priceMax to set + */ + public void setPriceMax(double priceMax) { + this.priceMax = priceMax; + } + + /** + * @param priceMax the priceMax to set + */ + public void setPriceMax(String priceMax) { + if (priceMax == null){ return; } + + try{ + this.priceMax = Double.parseDouble(priceMax); + } + catch (NumberFormatException nfe){ + // Do nothing + } + } + + /** + * @return the currency + */ + public String getCurrency() { + return currency; + } + + /** + * @param currency the currency to set + */ + public void setCurrency(String currency) { + this.currency = currency; + } + + /** + * @return the locationFrom + */ + public String getLocationFrom() { + return locationFrom; + } + + /** + * @param locationFrom the locationFrom to set + */ + public void setLocationFrom(String locationFrom) { + this.locationFrom = locationFrom; + } + + /** + * @return the locationTo + */ + public String getLocationTo() { + return locationTo; + } + + /** + * @param locationTo the locationTo to set + */ + public void setLocationTo(String locationTo) { + this.locationTo = locationTo; + } + + /** + * @return the departureTime + */ + public String getDepartureTime() { + return departureTime; + } + + /** + * @param departureTime the departureTime to set + */ + public void setDepartureTime(String departureTime) { + this.departureTime = departureTime; + } + + /** + * @return the returnReturn + */ + public String getReturnTime() { + return returnTime; + } + + /** + * @param returnReturn the returnReturn to set + */ + public void setReturnTime(String returnTime) { + this.returnTime = returnTime; + } + + /** + * @return the type + */ + public TravelType getType() { + return type; + } + + /** + * @param type the type to set + */ + public void setType(TravelType type) { + this.type = type; + } + + /** + * @return the scales + */ + public boolean isScales() { + return scales; + } + + /** + * @param scales the scales to set + */ + public void setScales(boolean scales) { + this.scales = scales; + } + + /** + * @return the number + */ + public int getNumber() { + return number; + } + + /** + * @param number the number to set + */ + public void setNumber(int number) { + this.number = number; + } + + public void setNumber(String number){ + if(number == null){ return; } + try{ + this.number = Integer.parseInt(number); + } + catch(NumberFormatException nfe){ + // Ok + } + } + + /** + * @return the queryId + */ + public String getQueryId() { + return queryId; + } + + /** + * @param queryId the queryId to set + */ + public void setQueryId(String queryId) { + this.queryId = queryId; + } + + + public String toString() { + + String toString = queryId + number; + toString += "ticket(s) from: "; + toString += locationFrom; + toString += " to: "; + toString += locationTo; + toString += " the day "; + toString += departureDate; + toString += " at "; + toString += departureTime; + toString += " returning the day "; + toString += returnTime; + toString += " at "; + toString += returnTime; + toString += " for "; + toString += priceMax + "->" + priceMin; + toString += " "; + toString += currency; + return toString; + } + + /** + * + */ + public List toPercepts() { + + List list = new LinkedList(); + + if(departureDate != null){ + list.add(NLUModel.getLiteralDateDeparture(queryId, TRAVEL_DOMAIN, "03", "04", "2012")); + } + + if(returnDate != null){ + list.add(NLUModel.getLiteralDateReturn(queryId, TRAVEL_DOMAIN, "03", "04", "2012")); + } + + if(departureTime != null){ + list.add(NLUModel.getLiteralTimeDeparture(queryId, TRAVEL_DOMAIN, "09", "00")); + } + + if(returnTime != null){ + list.add(NLUModel.getLiteralTimeReturn(queryId, TRAVEL_DOMAIN, "09", "00")); + } + + if(priceMax >= 0){ + list.add(NLUModel.getLiteralPriceMax(queryId, TRAVEL_DOMAIN, priceMax)); + } + + if(priceMin >= 0){ + list.add(NLUModel.getLiteralPriceMin(queryId, TRAVEL_DOMAIN, priceMin)); + } + + if(currency != null){ + list.add(NLUModel.getLiteralCurrency(queryId, TRAVEL_DOMAIN, NotationUtils.compact(currency))); + } + + if(locationFrom != null){ + list.add(NLUModel.getLiteralLocationFrom(queryId, TRAVEL_DOMAIN, NotationUtils.compact(locationFrom))); + } + + if(locationTo != null){ + list.add(NLUModel.getLiteralLocationTo(queryId, TRAVEL_DOMAIN, NotationUtils.compact(locationTo))); + } + + if(scales){ + list.add(Literal.parseLiteral("scales[query(" + queryId + "), domain(" + TRAVEL_DOMAIN + ")]")); + } + else{ + list.add(Literal.parseLiteral("~scales[query(" + queryId + "), domain(" + TRAVEL_DOMAIN + ")]")); + } + + list.add(Literal.parseLiteral("done[query(" + queryId + "), domain(" + TRAVEL_DOMAIN + ")]")); + + return list; + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/beans/Perceptable.java b/src/java/es/upm/dit/gsi/sojason/beans/Perceptable.java new file mode 100644 index 0000000..baae0ea --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/beans/Perceptable.java @@ -0,0 +1,14 @@ +package es.upm.dit.gsi.sojason.beans; +import jason.asSyntax.Literal; + +import java.util.List; + +/** + * @author miguel + * + */ +public interface Perceptable { + + public List toPercepts(); + +} diff --git a/src/java/es/upm/dit/gsi/sojason/beans/TravelType.java b/src/java/es/upm/dit/gsi/sojason/beans/TravelType.java new file mode 100644 index 0000000..493904c --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/beans/TravelType.java @@ -0,0 +1,11 @@ +package es.upm.dit.gsi.sojason.beans; + +/** + * This defines the different means of transport. + * @author gsi.dit.upm.es + */ +public enum TravelType { + flight, + train, + coach +} diff --git a/src/java/es/upm/dit/gsi/sojason/services/WebServiceConnector.java b/src/java/es/upm/dit/gsi/sojason/services/WebServiceConnector.java new file mode 100644 index 0000000..6d60a96 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/services/WebServiceConnector.java @@ -0,0 +1,57 @@ +/** + * + */ +package es.upm.dit.gsi.sojason.services; + +import jason.asSyntax.Literal; + +import java.util.Collection; + +/** + * This interface defines a standard way to connect to a web service in + * the definition of an external action in Jason. + * + * Project: Web40SOJason + * Package: es.upm.dit.gsi.sojason.services + * Class: WebServiceConnector + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Feb 27, 2012 + * + */ +public interface WebServiceConnector { + + /** + * This calls the service including in the request the parameters given. + * The URL of the service, the method to use and any other particularity + * of the transaction to connect to the service must be determined in the + * implementation of the method. + * + * @param params The list of parameters to include in the service + * request. Due to this is not a Dictionary + * the order of the parameters it is important and will be + * determined by the implementation of the extendee + * classes. + */ + public Collection call(String... params); + + /** + * This validates the set of parameters provided. Typically, this method + * should use some regex exprsesions to check whether a parameter is valid + * or not, due to the nature of the parameter cannot be checked because of + * the type of the parameters has been unified to String. + * + * @param params The list of parameters to validate + */ + public boolean validateParams(String... params); + + /** + * This generates a set of error Literals that describes the + * errors committed when trying to call the given service with the set of + * parameters given. + * + * @param params The list of parameters + */ +// public Set checkForErrors(String... params); + +} diff --git a/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.java b/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.java new file mode 100644 index 0000000..dc82ad1 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUConnector.java @@ -0,0 +1,163 @@ +package es.upm.dit.gsi.sojason.services.nlu; + +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_CURRENCY_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_DATES_DEPART_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_DATES_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_DATES_RETURN_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_DOMAINS_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_FROM_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_TO_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_LOCATIONS_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_MAX_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_MIN_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_NUMBER_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_PRICE_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_TIME_DEPART_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_TIME_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_TIME_RETURN_NODENAME; +import static es.upm.dit.gsi.sojason.services.nlu.NLUModel.JSON_TRAVEL_NODENAME; +import jason.asSyntax.Literal; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Collection; +import java.util.logging.Logger; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.map.ObjectMapper; + +import es.upm.dit.gsi.jason.utils.NotationUtils; +import es.upm.dit.gsi.sojason.Web40Model; +import es.upm.dit.gsi.sojason.beans.NLUTravel; +import es.upm.dit.gsi.sojason.services.WebServiceConnector; + +/** + * Project: Web40SOJason + * Package: es.upm.dit.gsi.sojason.services.nlu + * Class: NLUConnector + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Feb 27, 2012 + * + */ +public class NLUConnector implements WebServiceConnector{ + + /** The url of the service */ + private String serviceUrl; + /** */ + private Logger logger = Logger.getLogger("Web40SOJason." + NLUConnector.class.getName()); + + + /** Constructor */ + public NLUConnector(String serviceUrl) { + this.serviceUrl = serviceUrl; + } + + /** + * + */ + public Collection call(String... params) { + + /* Are parameters correct */ + if (!validateParams(params)){ + logger.info("Parameters are not valid:" + Arrays.toString(params)); + return null; + } + + try { + // Prepare the request + String urlRequest = prepareRequest(params[0], params[1]); + + URL url = new URL(urlRequest); + URLConnection connection = url.openConnection(); + connection.connect(); + + // Parse the data received (using Jackson lib) + ObjectMapper mapper = new ObjectMapper(); + JsonNode rootNode = mapper.readValue(connection.getInputStream(), JsonNode.class); // src can be a File, URL, InputStream etc + JsonNode travelNode = rootNode.with(JSON_DOMAINS_NODENAME).get(JSON_TRAVEL_NODENAME); + + NLUTravel travel = new NLUTravel(); + travel.setDepartureDate(travelNode.with(JSON_DATES_NODENAME).get(JSON_DATES_DEPART_NODENAME).getTextValue()); + travel.setReturnDate(travelNode.with(JSON_DATES_NODENAME).get(JSON_DATES_RETURN_NODENAME).getTextValue()); + + travel.setCurrency(travelNode.with(JSON_PRICE_NODENAME).get(JSON_CURRENCY_NODENAME).getTextValue()); + travel.setPriceMax(travelNode.with(JSON_PRICE_NODENAME).get(JSON_MAX_NODENAME).getTextValue()); + travel.setPriceMin(travelNode.with(JSON_PRICE_NODENAME).get(JSON_MIN_NODENAME).getTextValue()); + + travel.setLocationFrom(travelNode.with(JSON_LOCATIONS_NODENAME).get(JSON_FROM_NODENAME).getTextValue()); + travel.setLocationTo(travelNode.with(JSON_LOCATIONS_NODENAME).get(JSON_TO_NODENAME).getTextValue()); + + travel.setNumber(travelNode.get(JSON_NUMBER_NODENAME).getTextValue()); + + travel.setReturnTime(travelNode.with(JSON_TIME_NODENAME).get(JSON_TIME_RETURN_NODENAME).getTextValue()); + travel.setDepartureTime(travelNode.with(JSON_TIME_NODENAME).get(JSON_TIME_DEPART_NODENAME).getTextValue()); + + travel.setQueryId(params[0]); + +// System.out.println(travel); + + return travel.toPercepts(); + + } catch (MalformedURLException e) { +// return CollectionUtils.wrapList("error(malformed_url, \"The given url is not valid\")"); + logger.info("MalformedURLException:" + e.getMessage()); return null; + } catch (UnsupportedEncodingException e) { +// return CollectionUtils.wrapList("error(undupported_encodig, \"The encoding given is not supported\")"); + logger.info("UnsupportedEncodingException:" + e.getMessage()); return null; + } catch (IOException e) { +// return CollectionUtils.wrapList("error(io_exception, \"Someio exception ocurr\")"); + logger.info("IOException:" + e.getMessage()); return null; + } + + } + + /** + * This generates a String used as http GET request to access the service + * including the parameters given by the user + * + * @param queryid + * @param message + * @return the url service string (utf-8 encoded) + * @throws UnsupportedEncodingException + */ + String prepareRequest(String queryid, String message) throws UnsupportedEncodingException { + String urlRequest = this.serviceUrl; + urlRequest = urlRequest.concat("?text="); + message = NotationUtils.removeQuotation(message); + urlRequest = urlRequest.concat(URLEncoder.encode(message, "utf-8")); +// urlRequest = urlRequest.concat(URLEncoder.encode("&query_id=", "utf-8")); + urlRequest = urlRequest.concat("&query_id="); + urlRequest = urlRequest.concat(URLEncoder.encode(queryid, "utf-8")); + + logger.info(urlRequest); + return urlRequest ; + } + + /** + * This validates the parameters received. The + * {@link NLUConnector#call(String...)} method expects to receive two + * parameters of the nature and characteristics described below: + * + *
    + *
  • The first parameter is que query id. It is an alphanumeric string + * which normally will contain numbers, but other non-digit characters + * are permitted. No alphanumeric values are not allowed
  • + *
  • + *
+ * + */ + public boolean validateParams(String... params) { + if (params.length != 2){ + return false; + } + // TODO: check other things + return true; + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUModel.java b/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUModel.java new file mode 100644 index 0000000..d9998e3 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/services/nlu/NLUModel.java @@ -0,0 +1,92 @@ +package es.upm.dit.gsi.sojason.services.nlu; +import jason.asSyntax.Literal; + +/** + * + * + * Project: Web40 + * Package: es.upm.dit.gsi.qa.services.nlu + * Class: + * + * @author Miguel Coronado (miguelcb@dit.upm.es) + * @version Feb 28, 2012 + * + */ +public class NLUModel { + + + public final static String JSON_DOMAINS_NODENAME = "domains"; + + public final static String JSON_TRAVEL_NODENAME = "travel"; + + public final static String JSON_DATES_NODENAME = "dates"; + + public final static String JSON_DATES_DEPART_NODENAME = "depart"; + + public final static String JSON_DATES_RETURN_NODENAME = "return"; + + public final static String JSON_PRICE_NODENAME = "price"; + + public final static String JSON_CURRENCY_NODENAME = "currency"; + + public final static String JSON_MAX_NODENAME = "max"; + + public final static String JSON_MIN_NODENAME = "min"; + + public final static String JSON_LOCATIONS_NODENAME = "locations"; + + public final static String JSON_FROM_NODENAME = "from"; + + public final static String JSON_TO_NODENAME = "to"; + + public final static String JSON_NUMBER_NODENAME = "number"; + + public final static String JSON_TIME_NODENAME = "time"; + + public final static String JSON_TIME_DEPART_NODENAME = "depart"; + + public final static String JSON_TIME_RETURN_NODENAME = "return"; + + + + public static Literal getLiteralPriceMin (String query, String domain, double price) { + return Literal.parseLiteral("price(min," + price + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralPriceMax (String query, String domain, double price) { + return Literal.parseLiteral("price(max," + price + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralCurrency (String query, String domain, String currency) { + return Literal.parseLiteral("currency(" + currency + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralLocationFrom (String query, String domain, String place) { + return Literal.parseLiteral("location(from," + place + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralLocationTo (String query, String domain, String place) { + return Literal.parseLiteral("location(to," + place + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralDateDeparture (String query, String domain, String day, String month, String year) { + return Literal.parseLiteral("date(departure," + day + "," + month + "," + year + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralDateReturn (String query, String domain, String day, String month, String year) { + return Literal.parseLiteral("date(return," + day + "," + month + "," + year + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralTimeDeparture (String query, String domain, String hour, String min) { + return Literal.parseLiteral("time(departure," + hour + "," + min + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralTimeReturn (String query, String domain, String hour, String min) { + return Literal.parseLiteral("time(return," + hour + "," + min + ")[query("+ query +"),domain("+ domain +")]"); + } + + public static Literal getLiteralType (String query, String domain, String type) { + return Literal.parseLiteral("type(" + type + ")[query("+ query +"),domain("+ domain +")]"); + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.java b/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.java new file mode 100644 index 0000000..05cb220 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeScrapper.java @@ -0,0 +1,163 @@ +package es.upm.dit.gsi.sojason.services.travel; + +import jason.asSyntax.Literal; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import es.upm.dit.gsi.sojason.beans.Journey; +import es.upm.dit.gsi.sojason.beans.Perceptable; +import es.upm.dit.gsi.sojason.services.WebServiceConnector; + +/** + * + * @author miguel + * + */ +public class RenfeScrapper implements WebServiceConnector { + + /** + * It contains all the information about the Renfe + * web service URL convenion. + */ + RenfeServiceConvenion queryGenerator; + + /** + * Constructor + * @throws IOException if there is any error while loading the station ids. + */ + public RenfeScrapper() throws IOException { + this.queryGenerator = new RenfeServiceConvenion(); + } + + + public Collection call(String... params) { + + if(!validateParams(params)){ + return null; + } + try { + List schedule = getSchedule ( params[0].toString(), + params[1].toString(), + params[2].toString(), + params[3].toString(), + params[4].toString()); + + // prepare response + Collection res = new LinkedList(); + for (Perceptable travel : schedule){ + res.addAll(travel.toPercepts()); + } + return res; + + } catch (IOException e) { +// return CollectionUtils.wrapList("error(io_exception, \"Someio exception ocurr\")"); + return null; + } + + } + + public boolean validateParams(String... params) { + if(params.length != 5){ + return false; + } + return true; + } + + /** + * TODO: filter by time + * + * + * @param origin + * @param destination + * @param day + * @param month + * @param year + * @return + * @throws IOException + */ + public List getSchedule (String origin, String destination, + String day, String month, String year) throws IOException { + + // The list with the journeys that matches the given criteria + List retList = new LinkedList(); + + // Get the html + String queryUrl = ""; + try{ + queryUrl = queryGenerator.generateQuery(origin, destination, day, month, year); + } + catch(IllegalArgumentException iae){ + retList.add( queryGenerator.reportParamErrors(origin, destination, day, month, year) ); + return retList; + } + Document doc = Jsoup.connect(queryUrl).get(); + + // Get the rows of the schedule table + Elements rows = doc.select("table#row > tbody > tr"); + // Each row has the information of a different journey + for (Element row : rows) { + Elements cells = row.getElementsByTag("td"); + if(cells.size() > 2){ + + // get and fill the journey information + Journey journey = new Journey(); + journey.setOrigin(origin); + journey.setDestination(destination); + journey.setDepartureTime(cells.get(1).text()); + journey.setArrivalTime(cells.get(2).text()); + journey.setDuration(cells.get(3).text()); + + // the fee map for the particular journey + Map feeMap = new HashMap(); + // get the fares + Elements feeRows = cells.get(4).select("tbody tr"); + + /* According to Renfe's website we select the following sublist */ + int fromIndex = 1; // skip the header row + /* They present 2 set of fares (Internet and station) so we skip + * the header rows and divide by 2 to get the amount of fares to + * parse */ + int toIndex = 1+(feeRows.size()-2)/2; + + for(Element feeRow : feeRows.subList(fromIndex, toIndex)) { + Elements feeCells = feeRow.getElementsByTag("td"); +// String feeName = feeCells.get(1).text().toLowerCase().replace(" ", "").replace(":", "").replace("ñ", "n"); + String feeName = feeCells.get(1).text().replace(":", ""); + String price = feeCells.get(2).text().replace(",", "."); + // Set the fee + feeMap.put(feeName, price); + } + + // Set the fares + journey.setFares(feeMap); + retList.add(journey); + } + } + + return retList; + } + + /** + * try it + * @param args + * @throws IOException + */ + public static void main(String [] args) throws IOException{ + RenfeScrapper rs = new RenfeScrapper(); + List list = rs.getSchedule ("Madrid", "ciudad real", "16", "04", "2012"); + for(Perceptable journey : list){ + System.out.println(journey); + } + } + +} diff --git a/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.java b/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.java new file mode 100644 index 0000000..c16d170 --- /dev/null +++ b/src/java/es/upm/dit/gsi/sojason/services/travel/RenfeServiceConvenion.java @@ -0,0 +1,210 @@ +/** + * + */ +package es.upm.dit.gsi.sojason.services.travel; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.regex.Pattern; + +import es.upm.dit.gsi.jason.utils.NotationUtils; +import es.upm.dit.gsi.sojason.beans.ErrorReport; + +/** + * This class describes the + * @author gsi.dit.upm.es + * @version 1.0 + */ +public class RenfeServiceConvenion { + + public final static String SERVICE_URL = "http://horarios.renfe.com/HIRRenfeWeb/buscar.do"; + public final static String ORIGIN_PARAM = "O"; + public final static String DESTINATION_PARAM = "D"; + public final static String YEAR_PARAM = "AF"; + public final static String MONTH_PARAM = "MF"; + public final static String DAY_PARAM = "DF"; + public final static String DEFAULT_PATH_TO_CITY_CODES_FILE = "conf/cities.xml"; + + private Properties cityCodes; + + /** + * + * @param cityCodesFile + * @throws IOException + */ + public RenfeServiceConvenion (File cityCodesFile) throws IOException { + this.cityCodes = new Properties(); + this.cityCodes.loadFromXML(new FileInputStream(cityCodesFile)); + } + + /** + * Default constructor + * @throws IOException + */ + public RenfeServiceConvenion () throws IOException { + this(new File(DEFAULT_PATH_TO_CITY_CODES_FILE)); + } + + + /** + * + * @param origin + * @param destination + * @param day + * @param month + * @param year + * @return + */ + public String generateQuery (String origin, String destination, + String day, String month, String year){ + + origin = NotationUtils.uncompact(origin); + destination = NotationUtils.uncompact(destination); + + if(!validateParams(origin, destination, day, month, year)){ + throw new IllegalArgumentException(); + } + + String res = SERVICE_URL; + res = res.concat("?"); + res = res.concat(ORIGIN_PARAM); + res = res.concat("="); + res = res.concat(this.cityCodes.getProperty(origin.toLowerCase())); + res = res.concat("&"); + res = res.concat(DESTINATION_PARAM); + res = res.concat("="); + res = res.concat(this.cityCodes.getProperty(destination.toLowerCase())); + res = res.concat("&"); + res = res.concat(YEAR_PARAM); + res = res.concat("="); + res = res.concat(year); + res = res.concat("&"); + res = res.concat(MONTH_PARAM); + res = res.concat("="); + res = res.concat(month); + res = res.concat("&"); + res = res.concat(DAY_PARAM); + res = res.concat("="); + res = res.concat(day); + // 'concat' is faster than '+' operator + + return res; + } + + /** + * + * @param origin + * @param destination + * @param day + * @param month + * @param year + * @throws IllegalArgumentException + */ + protected boolean validateParams(String origin, String destination, String day, + String month, String year) { + + if(!Pattern.matches("\\d{4}", year)){ + return false; + } + if(!Pattern.matches("\\d{1,2}", month)){ + return false; + } + if(!Pattern.matches("\\d{1,2}", day)){ + return false; + } + + int monthI = Integer.parseInt(month); + int dayI = Integer.parseInt(day); + int monthNumberOfDays[] = {31,29,31,30,31,30,31,31,30,31,30,31}; + if(monthI < 1 || monthI > 12){ + return false; + } + if(dayI < 1 || dayI > monthNumberOfDays[monthI-1]){ + return false; + } + + if(origin == null || !this.cityCodes.containsKey(origin.toLowerCase())) { + return false; + } + + if(destination == null || !this.cityCodes.containsKey(destination.toLowerCase())) { + return false; + } + + return true; + } + + protected ErrorReport reportParamErrors (String origin, String destination, String day, + String month, String year) { + + ErrorReport errors = new ErrorReport(); + + if(!Pattern.matches("\\d{4}", year)){ + errors.put("year", "invalid format"); + } + if(!Pattern.matches("\\d{1,2}", month)){ + errors.put("month", "invalid format"); + } + if(!Pattern.matches("\\d{1,2}", day)){ + errors.put("day", "invalid format"); + } + + int monthI = Integer.parseInt(month); + int dayI = Integer.parseInt(day); + int monthNumberOfDays[] = {31,29,31,30,31,30,31,31,30,31,30,31}; + if(monthI < 1 || monthI > 12){ + errors.put("month", "out of range"); + monthI = 1; // this lets check the day + } + if(dayI < 1 || dayI > monthNumberOfDays[monthI-1]){ + errors.put("day", "out of range"); + } + + if(origin == null || !this.cityCodes.containsKey(origin.toLowerCase())) { + errors.put("origin", "no such location"); + } + + if(destination == null || !this.cityCodes.containsKey(destination.toLowerCase())) { + errors.put("destination", "no such location"); + } + + return errors; + + } + + /** + * + * @param arga + * @throws IOException + */ + public static void main(String [] arga) throws IOException{ + +// RenfeServiceConvenion rsc = new RenfeServiceConvenion(); +// Properties newProperties = new Properties(); +// +// for (Object key : rsc.cityCodes.keySet() ){ +// String keyStr = (String)key; +// keyStr = keyStr.toLowerCase(); +// newProperties.put(keyStr, rsc.cityCodes.get(key)); +// if (keyStr.contains("á") || +// keyStr.contains("é") || +// keyStr.contains("í") || +// keyStr.contains("ó") || +// keyStr.contains("ú")) { +// +// keyStr = keyStr.replace("á", "a").replace("é", "e").replace("í", "i").replace("ó", "o").replace("ú", "u"); +// newProperties.put(keyStr, rsc.cityCodes.get(key)); +// } +// } +// +// newProperties.storeToXML(new FileOutputStream(DEFAULT_PATH_TO_CITY_CODES_FILE), "comment"); + + RenfeServiceConvenion rsc = new RenfeServiceConvenion(); + String res = rsc.generateQuery("Madrid", "Ciudad Real", "15", "02", "2012"); + System.out.println(res); + + } + +}