From ee3e04c3eb562ea9fa8abdac6830e898d16daa3f Mon Sep 17 00:00:00 2001 From: veronie Date: Sat, 20 Feb 2021 20:50:18 +0100 Subject: [PATCH 1/4] Implemented first version of output of thing to result line. Slightly rearranged metadata (some fields are now in ThingUserData). Filling ThingUserData when fetching by user with BggApi. Added ExportResultHandler. --- xyz.veronie.bgg.ui/Application.e4xmi | 2 + .../veronie/bgg/localdb/SqliteController.java | 21 ++- .../src/xyz/veronie/bgg/result/BggApi.java | 77 ++++++++- .../veronie/bgg/result/Recommendation.java | 8 + .../src/xyz/veronie/bgg/result/Thing.java | 163 +++++++++++++++++- .../xyz/veronie/bgg/result/ThingDetails.java | 12 +- .../bgg/result/ThingDetailsComposite.java | 74 -------- .../xyz/veronie/bgg/result/ThingMetaData.java | 29 +--- .../xyz/veronie/bgg/result/ThingUserData.java | 134 ++++++++++++++ .../bgg/ui/handlers/ExportResultHandler.java | 34 ++++ .../src/xyz/veronie/bgg/ui/parts/BatMain.java | 25 ++- .../veronie/bgg/ui/parts/ThingListPart.java | 6 - 12 files changed, 434 insertions(+), 151 deletions(-) create mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java delete mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetailsComposite.java create mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java create mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java diff --git a/xyz.veronie.bgg.ui/Application.e4xmi b/xyz.veronie.bgg.ui/Application.e4xmi index 002cfe8..0d5c9cb 100644 --- a/xyz.veronie.bgg.ui/Application.e4xmi +++ b/xyz.veronie.bgg.ui/Application.e4xmi @@ -24,12 +24,14 @@ + + diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java index fa2e1a7..b5d19e5 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java @@ -14,6 +14,7 @@ import java.util.List; import xyz.veronie.bgg.result.Thing; import xyz.veronie.bgg.result.ThingMetaData; +import xyz.veronie.bgg.result.ThingUserData; import xyz.veronie.bgg.ui.helpers.Resources; public class SqliteController { @@ -181,10 +182,16 @@ public class SqliteController { thingStatement.setString(2, metaData.getName()); thingStatement.setString(3, metaData.getImgURL()); thingStatement.setString(4, metaData.getThumbURL()); - thingStatement.setString(5, metaData.getComment()); - if(metaData.getNumPlays() != null) { - thingStatement.setInt(6, metaData.getNumPlays()); + ThingUserData userData = thing.getUserData(); + if(userData != null) { + thingStatement.setString(5, userData.getComment()); + if(userData.getNumPlays() != null) { + thingStatement.setInt(6, userData.getNumPlays()); + } else { + thingStatement.setNull(6, java.sql.Types.INTEGER); + } } else { + thingStatement.setString(5, ""); thingStatement.setNull(6, java.sql.Types.INTEGER); } thingStatement.execute(); @@ -254,10 +261,12 @@ public class SqliteController { ThingMetaData metaData = new ThingMetaData(id, res.getString(2), res.getString(3), - res.getString(4), - res.getString(5), - res.getInt(6)); + res.getString(4)); + ThingUserData userData = new ThingUserData(); + userData.setComment(res.getString(5)); + userData.setNumPlays(res.getInt(6)); Thing thing = new Thing(id, metaData); + thing.setUserData(userData); thingList.add(thing); } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java index 1785174..0e46668 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java @@ -78,6 +78,21 @@ public class BggApi { return getThings(urlStr.toString()); } + public ArrayList getThingDetails(Set ids) { + StringBuilder urlStr = new StringBuilder(); + urlStr.append(BASE_URL + "thing?id="); + + String sep = ""; + for (Integer integer : ids) { + urlStr.append(sep + integer.toString()); + if(sep.isEmpty()) { + sep = ","; + } + } + + return getThings(urlStr.toString()); + } + public ArrayList getThingsForUser(String user) throws IllegalArgumentException { ResultConfig resultConfig = configManager.getResultConfig(); @@ -230,11 +245,51 @@ public class BggApi { id, getValue(eElement, "name"), getValue(eElement, "image"), - getValue(eElement, "thumbnail"), - getValue(eElement, "comment"), - Integer.parseInt(getValue(eElement, "numplays")) + getValue(eElement, "thumbnail") ); + ThingUserData tud = new ThingUserData(); + tud.setNumPlays(Integer.valueOf(getValue(eElement, "numplays"))); + tud.setComment(getValue(eElement, "comment")); + + // rating + NodeList statsNodes = eElement.getElementsByTagName("stats"); + if(statsNodes.getLength() > 0) { + Node statsNode = statsNodes.item(0); + NodeList statsChildren = statsNode.getChildNodes(); + if(statsChildren.getLength() > 0) { + Element ratingNode = (Element)statsChildren.item(0); + String ratingValue = ratingNode.getAttribute("value"); + if(!ratingValue.equals("N/A")) { + try { + Float rating = Float.valueOf(ratingValue); + tud.setRating(rating); + } + catch(NumberFormatException e) { + System.out.println("WARN: rating value is not a float."); + } + } + } + } + + NodeList statusNodes = eElement.getElementsByTagName("status"); + if(statusNodes.getLength() > 0) { + Element statusNode = (Element)statusNodes.item(0); + tud.setOwn(attributeToBoolean(statusNode, "own")); + tud.setPrevowned(attributeToBoolean(statusNode, "prevowned")); + tud.setFortrade(attributeToBoolean(statusNode, "fortrade")); + tud.setWant(attributeToBoolean(statusNode, "want")); + tud.setWanttoplay(attributeToBoolean(statusNode, "wanttoplay")); + tud.setWanttobuy(attributeToBoolean(statusNode, "wanttobuy")); + tud.setWishlist(attributeToBoolean(statusNode, "wishlist")); + if(statusNode.hasAttribute("wishlistpriority")) { + tud.setWishlistpriority(Integer.valueOf(statusNode.getAttribute("wishlistpriority"))); + } + tud.setPreordered(attributeToBoolean(statusNode, "preordered")); + tud.setLastmodified(statusNode.getAttribute("lastmodified")); + } + Thing thing = new Thing(id, tmd); + thing.setUserData(tud); things.add(thing); } } @@ -263,10 +318,7 @@ public class BggApi { id, name, getValue(eElement, "image"), - getValue(eElement, "thumbnail"), - getValue(eElement, "comment"), - null - ); + getValue(eElement, "thumbnail")); Thing thing = new Thing(id, tmd); things.add(thing); } @@ -299,7 +351,7 @@ public class BggApi { ThingMetaData tmd = new ThingMetaData( id, eLink.getAttribute("value"), - "", "", "", null); + "", ""); Thing thing = new Thing(id, tmd); things.add(thing); } @@ -338,7 +390,7 @@ public class BggApi { ThingMetaData tmd = new ThingMetaData( id, eElement.getAttribute("objectname"), - "", "", "", null); + "", ""); Thing thing = new Thing(id, tmd); things.add(thing); } @@ -362,6 +414,13 @@ public class BggApi { } + private Boolean attributeToBoolean(Element statusNode, String attribute) { + if(statusNode.hasAttribute(attribute)) { + return statusNode.getAttribute(attribute).equals("0") ? false : true; + } + return null; + } + private void checkForErrors(Document doc) throws IllegalArgumentException { NodeList nList = doc.getElementsByTagName("error"); if(nList.getLength() > 0) { diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java new file mode 100644 index 0000000..da9a6c8 --- /dev/null +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java @@ -0,0 +1,8 @@ +package xyz.veronie.bgg.result; + +public enum Recommendation { + N, // not supported + P, // not recommended + R, // recommended + B // best +} diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java index 9308437..6131b31 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java @@ -20,10 +20,12 @@ public class Thing { private int id; private ThingMetaData metaData; + private ThingUserData userData; private ThingDetails details; // cache private Image thumbImage; + private Path imagePath; public static final String IdHeader = "ID"; public static final String ThumbHeader = "Thumbnail"; @@ -49,9 +51,19 @@ public class Thing { public void setMetaData(ThingMetaData metaData) { this.metaData = metaData; } + + public void setUserData(ThingUserData userData) { + this.userData = userData; + } + + public ThingUserData getUserData() { + return userData; + } + public ThingDetails getDetails() { return details; } + public void setDetails(ThingDetails details) { this.details = details; } @@ -63,14 +75,21 @@ public class Thing { return thumbImage; } + public String getImagePath() { + if(imagePath != null) + return imagePath.toString(); + else + return ""; + } + private void getThumbImage(int thingId) { Path tmpDir = Resources.INSTANCE.getTmpDir(); if(tmpDir != null) { - Path imageFile = Paths.get(Resources.INSTANCE.getThumbsDir().toString() + imagePath = Paths.get(Resources.INSTANCE.getThumbsDir().toString() + File.separator + String.valueOf(thingId) + ".png"); - if(Files.exists(imageFile, LinkOption.NOFOLLOW_LINKS)) { - String filename = imageFile.toString().replace("\\","/"); + if(Files.exists(imagePath, LinkOption.NOFOLLOW_LINKS)) { + String filename = imagePath.toString().replace("\\","/"); thumbImage = Resources.INSTANCE.getResourceManager().createImage( ImageDescriptor.createFromFile(null, filename)); } else { @@ -81,7 +100,7 @@ public class Thing { ImageLoader saver = new ImageLoader(); saver.data = new ImageData[] { thumbImage.getImageData() }; - saver.save(imageFile.toString(), SWT.IMAGE_PNG); + saver.save(imagePath.toString(), SWT.IMAGE_PNG); } catch (MalformedURLException e) { System.out.println("INFO: Malformed URL for ThingId " + thingId); } @@ -130,4 +149,140 @@ public class Thing { } } + + /// convert the thing into a comma-separated line for a result.txt file + /** + * The format is (without line breaks): + * id,name,designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, + * age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, + * owned,trading,wanting,wishing,userrating,image,category,mechanic,comment, + * 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, + * 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, + * description,exp,basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, + * contains,contains_name,iscontained,iscontained_name,integration,integration_name, + * numplays,price,userweight,wishpriority,expansions,domain,family,age_poll + * + * @return + */ + public String toResultTxtLine() { + StringBuilder str = new StringBuilder(); + final String emptyItem = "\"\","; + // id,name,designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, + // age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, + str.append(Integer.toString(id)).append(","); + str.append(metaData.getName()).append(","); + if(details != null) { + str.append(details.designer).append(","); + str.append(details.publisher).append(","); + str.append(details.artist).append(","); + str.append(details.yearpublished).append(","); + str.append(details.minplayers).append(","); + str.append(details.maxplayers).append(","); + str.append(details.playingtime).append(","); + str.append(details.minplaytime).append(","); + str.append(details.maxplaytime).append(","); + str.append(details.age).append(","); + str.append(details.usersrated).append(","); + str.append(details.average).append(","); + str.append(floatToResult(details.bayesaverage)).append(","); + str.append(details.rank).append(","); + str.append(details.rank_wg).append(","); + str.append(details.numcomments).append(","); + str.append(details.numweights).append(","); + str.append(floatToResult(details.averageweight)).append(","); + str.append(floatToResult(details.stddev)).append(","); + str.append(floatToResult(details.median)).append(","); + } else { + for(int i = 0; i < 20; ++i) { + str.append(emptyItem); + } + } + + // owned,trading,wanting,wishing,userrating, + if(userData != null) { + str.append(userData.getOwn()?"1":"0").append(","); + str.append(userData.getFortrade()?"1":"0").append(","); + str.append(userData.getWant()?"1":"0").append(","); + str.append(userData.getWishlist()?"1":"0").append(","); + str.append(floatToResult(userData.getRating())).append(","); + } + + // image,category,mechanic,comment, + str.append(getImagePath()).append(","); + if(details != null) { + str.append(details.category).append(","); + str.append(details.mechanic).append(","); + } else str.append(emptyItem).append(emptyItem); + if(userData != null) { + str.append(userData.getComment()).append(","); + } else str.append(emptyItem); + + // 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, + // 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, + if(details != null) { + for(Recommendation r : details.players) { + str.append(r.name()).append(","); + } + } else { + for(int i = 0; i < 20; ++i) { + str.append("N,"); + } + } + // description,exp,basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, + // contains,contains_name,iscontained,iscontained_name,integration,integration_name, + if(details != null) { + str.append(details.description).append(","); + str.append(details.expansion).append(","); + str.append(details.basegameId).append(","); + str.append(details.reimplementId).append(","); + str.append(details.reimplement_name).append(","); + str.append(details.reimplementedById).append(","); + str.append(details.reimplementedByName).append(","); + str.append(details.containsId).append(","); + str.append(details.containsName).append(","); + str.append(details.iscontained).append(","); + str.append(details.iscontained_name).append(","); + str.append(details.integration).append(","); + str.append(details.integration_name).append(","); + } else { + for(int i = 0; i < 13; ++i) { + str.append(emptyItem); + } + } + + // numplays,price,userweight,wishpriority,expansions,domain,family,age_poll + if(userData != null) { + str.append(userData.getNumPlays()).append(","); + } else str.append(emptyItem); + if(details != null) { + str.append(details.price).append(","); + } else str.append(emptyItem); + // TODO: find out how to get userweight... + str.append(emptyItem); + if(userData != null) { + str.append(userData.getWishlistpriority()).append(","); + } else str.append(emptyItem); + if(details != null) { + str.append(details.expansions).append(","); + str.append(details.domain).append(","); + str.append(details.family).append(","); + str.append(details.age_poll).append(","); + } else { + str.append(emptyItem); + str.append(emptyItem); + str.append(emptyItem); + str.append(emptyItem); + } + + return str.toString(); + } + + + private String floatToResult(Float rating) { + if(rating == null) { + return ""; + } else { + return Float.toString(rating.floatValue()); + } + } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java index c064164..ae3a9fd 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java @@ -2,7 +2,6 @@ package xyz.veronie.bgg.result; public class ThingDetails { Integer id; - String name; String designer; String publisher; String artist; @@ -23,18 +22,13 @@ public class ThingDetails { Float averageweight; Float stddev; Float median; - Boolean owned; - Boolean trading; - Boolean wanting; - Boolean wishing; - Float userrating; String image; Integer category; Integer mechanic; String comment; - Integer[] players; // 1 to 20 + Recommendation[] players; // 1 to 20 String description; - // exp, + Integer expansion; Integer basegameId; Integer reimplementId; String reimplement_name; @@ -48,8 +42,6 @@ public class ThingDetails { String integration_name; Integer numplays; Float price; - Float userweight; - Integer wishpriority; Integer expansions; String domain; String family; diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetailsComposite.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetailsComposite.java deleted file mode 100644 index 8762ba7..0000000 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetailsComposite.java +++ /dev/null @@ -1,74 +0,0 @@ -package xyz.veronie.bgg.result; - -import java.awt.Label; - -import javax.annotation.PostConstruct; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; - -public class ThingDetailsComposite extends Composite { - ThingDetails thingDetails; - private Label nameField; - private Label designerField; - private Label publisherField; - private Label yearField; - - public ThingDetailsComposite(Composite parent, int style) { - super(parent, style); - } - - @PostConstruct - public void create() { - setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); - - GridLayout layout = new GridLayout(2, false); - setLayout(layout); - - Label nameLbl = new Label("Name"); - applyLayout(nameLbl); - nameField = new Label(""); - - Label designerLbl = new Label("Designer"); - applyLayout(designerLbl); - designerField = new Label(""); - - Label publisherLbl = new Label("Publisher"); - applyLayout(publisherLbl); - publisherField = new Label(""); - - Label yearLbl = new Label("Publisher"); - applyLayout(yearLbl); - yearField = new Label(""); - - this.getParent().layout(); - } - - - private void applyLayout(Label label) { - label.setAlignment(SWT.RIGHT); - } - - public void selectedThingChanged(Thing thing) { - System.out.println("TOPIC_THING_SELECTION: " + thing); - thingDetails = thing.getDetails(); - - if(thingDetails != null) { - nameField.setText(thingDetails.name); - designerField.setText(thingDetails.designer); - publisherField.setText(thingDetails.publisher); - yearField.setText(Integer.toString(thingDetails.yearpublished)); - } else { - nameField.setText(""); - designerField.setText(""); - publisherField.setText(""); - yearField.setText(""); - - } - this.getParent().layout(); - } - - -} diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingMetaData.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingMetaData.java index 7998bca..432773d 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingMetaData.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingMetaData.java @@ -2,7 +2,6 @@ package xyz.veronie.bgg.result; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; -//import com.google.gson.Gson; public class ThingMetaData implements java.io.Serializable { private static final long serialVersionUID = -5268898737006538509L; @@ -11,21 +10,15 @@ public class ThingMetaData implements java.io.Serializable { private String name; private String imgURL; private String thumbURL; - private String comment; - private Integer numPlays; - private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); public ThingMetaData(int id, String name, - String imgURL, String thumURL, - String comment, Integer numPlays) { + String imgURL, String thumURL) { this.setId(id); this.setName(name); this.setImgURL(imgURL); this.setThumbURL(thumURL); - this.setComment(comment); - this.setNumPlays(numPlays); } @@ -78,29 +71,9 @@ public class ThingMetaData implements java.io.Serializable { this.thumbURL = thumbURL); } - public Integer getNumPlays() { - return numPlays; - } - - public void setNumPlays(Integer numPlays) { - this.numPlays = numPlays; - } - - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - propertyChangeSupport.firePropertyChange("comment", this.comment, - this.comment = comment); - } - @Override public String toString() { return String.valueOf(id); -// Gson gson = new Gson(); -// return gson.toJson(this); } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java new file mode 100644 index 0000000..5cf4e43 --- /dev/null +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java @@ -0,0 +1,134 @@ +package xyz.veronie.bgg.result; + +public class ThingUserData { + private Integer numplays; + private String comment; + + private Float rating; + private Boolean own; + private Boolean prevowned; + private Boolean fortrade; + private Boolean want; + private Boolean wanttoplay; + private Boolean wanttobuy; + private Boolean wishlist; + private Integer wishlistpriority; + private Boolean preordered; + private String lastmodified; + + //private Float weight; + + public Integer getNumPlays() { + return numplays; + } + + public void setNumPlays(Integer numPlays) { + this.numplays = numPlays; + } + + + public String getComment() { + return comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public Float getRating() { + return rating; + } + + public void setRating(Float rating) { + this.rating = rating; + } + + public Boolean getOwn() { + return own; + } + + public void setOwn(Boolean own) { + this.own = own; + } + + public Boolean getPrevowned() { + return prevowned; + } + + public void setPrevowned(Boolean prevowned) { + this.prevowned = prevowned; + } + + public Boolean getFortrade() { + return fortrade; + } + + public void setFortrade(Boolean fortrade) { + this.fortrade = fortrade; + } + + public Boolean getWant() { + return want; + } + + public void setWant(Boolean want) { + this.want = want; + } + + public Boolean getWanttoplay() { + return wanttoplay; + } + + public void setWanttoplay(Boolean wanttoplay) { + this.wanttoplay = wanttoplay; + } + + public Boolean getWanttobuy() { + return wanttobuy; + } + + public void setWanttobuy(Boolean wanttobuy) { + this.wanttobuy = wanttobuy; + } + + public Boolean getWishlist() { + return wishlist; + } + + public void setWishlist(Boolean wishlist) { + this.wishlist = wishlist; + } + + public Integer getWishlistpriority() { + return wishlistpriority; + } + + public void setWishlistpriority(Integer wishlistpriority) { + this.wishlistpriority = wishlistpriority; + } + + public Boolean getPreordered() { + return preordered; + } + + public void setPreordered(Boolean preordered) { + this.preordered = preordered; + } + + public String getLastmodified() { + return lastmodified; + } + + public void setLastmodified(String lastmodified) { + this.lastmodified = lastmodified; + } + +// public Float getWeight() { +// return weight; +// } +// +// public void setWeight(Float weight) { +// this.weight = weight; +// } + +} diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java new file mode 100644 index 0000000..935d843 --- /dev/null +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java @@ -0,0 +1,34 @@ + +package xyz.veronie.bgg.ui.handlers; + +import org.eclipse.e4.core.di.annotations.Execute; + +import xyz.veronie.bgg.result.Thing; +import xyz.veronie.bgg.result.ThingProvider; + +import java.util.List; + +import javax.inject.Inject; + +import org.eclipse.e4.core.di.annotations.CanExecute; + +public class ExportResultHandler { + + @Inject + ThingProvider thingProvider; + + @Execute + public void execute() { + List things = thingProvider.getThings(); + for (Thing thing : things) { + System.out.println(thing.toResultTxtLine()); + } + } + + + @CanExecute + public boolean canExecute() { + return thingProvider != null; + } + +} \ No newline at end of file diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java index 97c8a03..409bb72 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/BatMain.java @@ -148,6 +148,16 @@ public class BatMain { btnUndo.setEnabled(false); btnExport = new Button(buttonRow, SWT.NONE); + btnExport.addMouseListener(new MouseAdapter() { + @Override + public void mouseUp(MouseEvent e) { + ParameterizedCommand cmd = + commandService.createCommand("xyz.veronie.bgg.ui.command.export", null); + if (handlerService.canExecute(cmd)){ + handlerService.executeHandler(cmd); + } + } + }); btnExport.setImage(ResourceManager.getPluginImage("xyz.veronie.bgg.ui", "icons/export_nandeck_60x60.png")); btnExport.setToolTipText("Export for nanDeck"); btnExport.setEnabled(false); @@ -222,20 +232,7 @@ public class BatMain { TableViewerColumn colThumbnail = createTableViewerColumn(Thing.ThumbHeader, 120, 0); -// colThumbnail.setLabelProvider(new ColumnLabelProvider() { -// @Override -// public Image getImage(Object element) { -// Thing t = (Thing) element; -// Image img = t.getThumbnail(); -// return img; -// } -// -// @Override -// public String getText(Object element) { -// return ""; -// } -// -// }); + colThumbnail.setLabelProvider(new OwnerDrawLabelProvider() { @Override protected void measure(Event event, Object element) { diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/ThingListPart.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/ThingListPart.java index c2a2cb8..1b8bcf3 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/ThingListPart.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/parts/ThingListPart.java @@ -11,12 +11,9 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import xyz.veronie.bgg.result.Thing; -import xyz.veronie.bgg.result.ThingDetailsComposite; import xyz.veronie.bgg.types.EventConstants; public class ThingListPart { - private ThingDetailsComposite thingDetailsComposite; - @PostConstruct public void createControls(Composite parent) { Composite main = new Composite(parent, SWT.FILL); @@ -24,14 +21,11 @@ public class ThingListPart { GridLayout layout = new GridLayout(2, false); main.setLayout(layout); - - thingDetailsComposite = new ThingDetailsComposite(main, SWT.BORDER); } @Inject @Optional private void selectedThingChanged(@UIEventTopic(EventConstants.TOPIC_THING_SELECTION) Thing thing) { - thingDetailsComposite.selectedThingChanged(thing); } From 94ecc66ae32a2e33103ae8b4a8875bc2376b63e5 Mon Sep 17 00:00:00 2001 From: veronie Date: Sat, 27 Feb 2021 17:14:22 +0100 Subject: [PATCH 2/4] Towards result output for nandeck... --- .../src/xyz/veronie/bgg/result/IdString.java | 12 ++++++++++++ .../src/xyz/veronie/bgg/result/Recommendation.java | 9 +++++---- .../src/xyz/veronie/bgg/result/Thing.java | 1 + .../src/xyz/veronie/bgg/result/ThingDetails.java | 8 ++++---- 4 files changed, 22 insertions(+), 8 deletions(-) create mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java new file mode 100644 index 0000000..c252896 --- /dev/null +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java @@ -0,0 +1,12 @@ +package xyz.veronie.bgg.result; + +/// Holds an id and its string representation, for example for Family, Extension, Mechanic, or Category. +public class IdString { + int id; + String string; + + public IdString(int id, String string) { + this.id = id; + this.string = string; + } +} diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java index da9a6c8..2fdbede 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Recommendation.java @@ -1,8 +1,9 @@ package xyz.veronie.bgg.result; public enum Recommendation { - N, // not supported - P, // not recommended - R, // recommended - B // best + N, // (N)onplayable + P, // (P)layable, not recommended + R, // (R)ecommended + B // (B)est with... } + diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java index 6131b31..bf0423c 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java @@ -164,6 +164,7 @@ public class Thing { * * @return */ + // TODO: rewrite: order isn't important, column names are public String toResultTxtLine() { StringBuilder str = new StringBuilder(); final String emptyItem = "\"\","; diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java index ae3a9fd..c4dcd82 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java @@ -23,12 +23,12 @@ public class ThingDetails { Float stddev; Float median; String image; - Integer category; - Integer mechanic; + IdString[] category; + IdString[] mechanic; String comment; Recommendation[] players; // 1 to 20 String description; - Integer expansion; + IdString[] expansion; Integer basegameId; Integer reimplementId; String reimplement_name; @@ -44,6 +44,6 @@ public class ThingDetails { Float price; Integer expansions; String domain; - String family; + IdString[] family; Float age_poll; } From 5afbff7b2e3b9554bbb2687429ca0438aa2cbd16 Mon Sep 17 00:00:00 2001 From: veronie Date: Sun, 28 Feb 2021 17:36:29 +0100 Subject: [PATCH 3/4] Parsing of thing details from Bgg XMLAPI2 work in progress... --- .../veronie/bgg/localdb/SqliteController.java | 35 ++ .../src/xyz/veronie/bgg/result/BggApi.java | 436 ++++++++++++------ .../src/xyz/veronie/bgg/result/Thing.java | 133 +----- .../xyz/veronie/bgg/result/ThingDetails.java | 166 ++++++- .../xyz/veronie/bgg/result/ThingProvider.java | 22 + .../xyz/veronie/bgg/result/ThingUserData.java | 21 + .../result/internal/OutputResultHelper.java | 14 + .../bgg/ui/handlers/ExportResultHandler.java | 15 +- 8 files changed, 565 insertions(+), 277 deletions(-) create mode 100644 xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java index b5d19e5..5670921 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/localdb/SqliteController.java @@ -90,6 +90,41 @@ public class SqliteController { stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idx1 ON ThingList(Name)"); stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingListToThing (ListId INTEGER, ThingId INTEGER, FOREIGN KEY (ListId) REFERENCES ThingList(ListId), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idx2 ON ThingListToThing(ListId, ThingId)"); + + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Artist (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxArtist ON Artist(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Designer (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idDesigner ON Designer(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Mechanic (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxMechanic ON Mechanic(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Family (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxFamily ON Family(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Category (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxCategory ON Category(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Expansion (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxExpansion ON Expansion(Id)"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS Domain (Id INTEGER PRIMARY KEY, Name);"); + stmt.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS idxDomain ON Domain(Id)"); + + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingDetails (" + + "ThingId INTEGER PRIMARY KEY, name, yearpublished INTEGER, " + + "minplayers INTEGER, maxplayers INTEGER, playingtime INTEGER, minplaytime INTEGER, maxplaytime INTEGER, " + + "age INTEGER, usersrated INTEGER, average FLOAT, bayesaverage FLOAT, " + + "owning INTEGER, trading INTEGER, wanting INTEGER, wishing INTEGER, " + + "rank INTEGER, rank_wg INTEGER, numcomments INTEGER, numweights INTEGER, " + + "averageweight FLOAT, stddev FLOAT, median FLOAT, image, comment," + + "player1,player2,player3,player4,player5,player6,player7,player8,player9,player10," + + "player11, player12, player13, player14, player15, player16, player17, player18, player19, player20," + + "description, numplays INTEGER, price FLOAT, age_poll FLOAT);"); + + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToArtist (ThingId INTEGER, ArtistId INTEGER, FOREIGN KEY (ArtistId) REFERENCES Artist(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToDesigner (ThingId INTEGER, DesignerId INTEGER, FOREIGN KEY (DesignerId) REFERENCES Designer(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToMechanic (ThingId INTEGER, MechanicId INTEGER, FOREIGN KEY (MechanicId) REFERENCES Mechanic(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToFamily (ThingId INTEGER, FamilyId INTEGER, FOREIGN KEY (FamilyId) REFERENCES Family(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToCategory (ThingId INTEGER, CategoryId INTEGER, FOREIGN KEY (CategoryId) REFERENCES Category(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToExpansion (ThingId INTEGER, ExpansionId INTEGER, FOREIGN KEY (ExpansionId) REFERENCES Expansion(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ThingToDomain (ThingId INTEGER, DomainId INTEGER, FOREIGN KEY (DomainId) REFERENCES Domain(Id), FOREIGN KEY (ThingId) REFERENCES Thing(ThingId));"); + stmt.close(); } catch (SQLException e) { System.err.println("Couldn't create Schema"); diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java index 0e46668..32db4c7 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java @@ -186,7 +186,7 @@ public class BggApi { // do something with the content System.out.println(content.toString()); - ArrayList output = parseThingMetas(content.toString()); + ArrayList output = parseThings(content.toString()); output.sort((thing1, thing2) -> thing1.getMetaData().getName().compareTo(thing2.getMetaData().getName())); @@ -213,7 +213,7 @@ public class BggApi { - private ArrayList parseThingMetas(String content) throws IllegalArgumentException { + private ArrayList parseThings(String content) throws IllegalArgumentException { ArrayList things = new ArrayList(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -239,124 +239,20 @@ public class BggApi { if( eElement.hasAttribute("objecttype") && eElement.getAttribute("objecttype").equals("thing")) { - Integer id = Integer.parseInt(eElement.getAttribute("objectid")); - if(id != 0) { - ThingMetaData tmd = new ThingMetaData( - id, - getValue(eElement, "name"), - getValue(eElement, "image"), - getValue(eElement, "thumbnail") - ); - ThingUserData tud = new ThingUserData(); - tud.setNumPlays(Integer.valueOf(getValue(eElement, "numplays"))); - tud.setComment(getValue(eElement, "comment")); - - // rating - NodeList statsNodes = eElement.getElementsByTagName("stats"); - if(statsNodes.getLength() > 0) { - Node statsNode = statsNodes.item(0); - NodeList statsChildren = statsNode.getChildNodes(); - if(statsChildren.getLength() > 0) { - Element ratingNode = (Element)statsChildren.item(0); - String ratingValue = ratingNode.getAttribute("value"); - if(!ratingValue.equals("N/A")) { - try { - Float rating = Float.valueOf(ratingValue); - tud.setRating(rating); - } - catch(NumberFormatException e) { - System.out.println("WARN: rating value is not a float."); - } - } - } - } - - NodeList statusNodes = eElement.getElementsByTagName("status"); - if(statusNodes.getLength() > 0) { - Element statusNode = (Element)statusNodes.item(0); - tud.setOwn(attributeToBoolean(statusNode, "own")); - tud.setPrevowned(attributeToBoolean(statusNode, "prevowned")); - tud.setFortrade(attributeToBoolean(statusNode, "fortrade")); - tud.setWant(attributeToBoolean(statusNode, "want")); - tud.setWanttoplay(attributeToBoolean(statusNode, "wanttoplay")); - tud.setWanttobuy(attributeToBoolean(statusNode, "wanttobuy")); - tud.setWishlist(attributeToBoolean(statusNode, "wishlist")); - if(statusNode.hasAttribute("wishlistpriority")) { - tud.setWishlistpriority(Integer.valueOf(statusNode.getAttribute("wishlistpriority"))); - } - tud.setPreordered(attributeToBoolean(statusNode, "preordered")); - tud.setLastmodified(statusNode.getAttribute("lastmodified")); - } - - Thing thing = new Thing(id, tmd); - thing.setUserData(tud); - things.add(thing); - } + extractThingByUser(things, eElement); } // when fetching things by id, type is boardgame else if(eElement.hasAttribute("type") && eElement.getAttribute("type").equals("boardgame")) { - Integer id = Integer.parseInt(eElement.getAttribute("id")); - if(id != 0) { - String name = ""; - NodeList nameList = eElement.getElementsByTagName("name"); - for(int n = 0; n < nameList.getLength(); n++) { - Node nameNode = nameList.item(n); - if (nNode.getNodeType() == Node.ELEMENT_NODE) { - Element nameElement = (Element) nameNode; - if(nameElement.hasAttribute("type") - && nameElement.getAttribute("type").equals("primary")) - { - name = nameElement.getAttribute("value"); - break; - } - } - } - - ThingMetaData tmd = new ThingMetaData( - id, - name, - getValue(eElement, "image"), - getValue(eElement, "thumbnail")); - Thing thing = new Thing(id, tmd); - things.add(thing); - } - } + extractThingsById(things, nNode, eElement); + } + // family has "type" else if(eElement.hasAttribute("type") && eElement.getAttribute("type").equals("boardgamefamily")) { - // get name and description - StringBuilder fInfo = new StringBuilder(); - - NodeList nListName = eElement.getElementsByTagName("name"); - if(nListName.getLength() > 0) { - fInfo.append(nListName.item(0).getTextContent() + "\r\n"); - } - - NodeList nListDesc = doc.getElementsByTagName("description"); - if(nListDesc.getLength() > 0) { - fInfo.append(nListDesc.item(0).getTextContent()); - } - eventBroker.send(EventConstants.TOPIC_FAMILY_INFO, fInfo.toString()); - - NodeList nLinks = eElement.getElementsByTagName("link"); - for(int l = 0; l < nLinks.getLength(); l++) { - Node link = nLinks.item(l); - if (link.getNodeType() == Node.ELEMENT_NODE) { - Element eLink = (Element) link; - Integer id = Integer.parseInt(eLink.getAttribute("id")); - if(id != 0) { - ThingMetaData tmd = new ThingMetaData( - id, - eLink.getAttribute("value"), - "", ""); - Thing thing = new Thing(id, tmd); - things.add(thing); - } - } - } + extractThingByFamily(things, doc, eElement); // because of our input, there shouldn't be more than one family in the result // but do not iterate over items just to make sure @@ -367,35 +263,7 @@ public class BggApi { } // for geeklist result else if(root.getTagName() == "geeklist") { - StringBuilder gInfo = new StringBuilder(); - - NodeList nListTitle = doc.getElementsByTagName("title"); - if(nListTitle.getLength() > 0) { - gInfo.append(nListTitle.item(0).getTextContent() + "\r\n"); - } - - NodeList nListDesc = doc.getElementsByTagName("description"); - if(nListDesc.getLength() > 0) { - gInfo.append(nListDesc.item(0).getTextContent()); - } - eventBroker.send(EventConstants.TOPIC_GEEKLIST_INFO, gInfo.toString()); - - NodeList nList = doc.getElementsByTagName("item"); - for(int i = 0; i < nList.getLength(); i++) { - Node nNode = nList.item(i); - if (nNode.getNodeType() == Node.ELEMENT_NODE) { - Element eElement = (Element) nNode; - Integer id = Integer.parseInt(eElement.getAttribute("objectid")); - if(id != 0) { - ThingMetaData tmd = new ThingMetaData( - id, - eElement.getAttribute("objectname"), - "", ""); - Thing thing = new Thing(id, tmd); - things.add(thing); - } - } - } + extractThingByGeeklist(things, doc); } return things; @@ -413,6 +281,276 @@ public class BggApi { return null; } + private void extractThingByGeeklist(ArrayList things, Document doc) { + StringBuilder gInfo = new StringBuilder(); + + NodeList nListTitle = doc.getElementsByTagName("title"); + if(nListTitle.getLength() > 0) { + gInfo.append(nListTitle.item(0).getTextContent() + "\r\n"); + } + + NodeList nListDesc = doc.getElementsByTagName("description"); + if(nListDesc.getLength() > 0) { + gInfo.append(nListDesc.item(0).getTextContent()); + } + eventBroker.send(EventConstants.TOPIC_GEEKLIST_INFO, gInfo.toString()); + + NodeList nList = doc.getElementsByTagName("item"); + for(int i = 0; i < nList.getLength(); i++) { + Node nNode = nList.item(i); + if (nNode.getNodeType() == Node.ELEMENT_NODE) { + Element eElement = (Element) nNode; + Integer id = Integer.parseInt(eElement.getAttribute("objectid")); + if(id != 0) { + ThingMetaData tmd = new ThingMetaData( + id, + eElement.getAttribute("objectname"), + "", ""); + Thing thing = new Thing(id, tmd); + things.add(thing); + } + } + } + } + + private void extractThingByFamily(ArrayList things, Document doc, Element eElement) { + // get name and description + StringBuilder fInfo = new StringBuilder(); + + NodeList nListName = eElement.getElementsByTagName("name"); + if(nListName.getLength() > 0) { + fInfo.append(nListName.item(0).getTextContent() + "\r\n"); + } + + NodeList nListDesc = doc.getElementsByTagName("description"); + if(nListDesc.getLength() > 0) { + fInfo.append(nListDesc.item(0).getTextContent()); + } + eventBroker.send(EventConstants.TOPIC_FAMILY_INFO, fInfo.toString()); + + NodeList nLinks = eElement.getElementsByTagName("link"); + for(int l = 0; l < nLinks.getLength(); l++) { + Node link = nLinks.item(l); + if (link.getNodeType() == Node.ELEMENT_NODE) { + Element eLink = (Element) link; + Integer id = Integer.parseInt(eLink.getAttribute("id")); + if(id != 0) { + ThingMetaData tmd = new ThingMetaData( + id, + eLink.getAttribute("value"), + "", ""); + Thing thing = new Thing(id, tmd); + things.add(thing); + } + } + } + } + + private void extractThingsById(ArrayList things, Node nNode, Element eElement) { + Integer id = Integer.parseInt(eElement.getAttribute("id")); + if(id != 0) { + String name = ""; + NodeList nameList = eElement.getElementsByTagName("name"); + for(int n = 0; n < nameList.getLength(); n++) { + Node nameNode = nameList.item(n); + if (nNode.getNodeType() == Node.ELEMENT_NODE) { + Element nameElement = (Element) nameNode; + if(nameElement.hasAttribute("type") + && nameElement.getAttribute("type").equals("primary")) + { + name = nameElement.getAttribute("value"); + break; + } + } + } + + ThingMetaData tmd = new ThingMetaData( + id, + name, + getValue(eElement, "image"), + getValue(eElement, "thumbnail")); + + System.out.println("DEBUG: Details for " + id); + + ThingDetails details = new ThingDetails(); + details.yearpublished = getIntegerFromString(getValueAttribute(eElement, "yearpublished")); + details.minplayers = getIntegerFromString(getValueAttribute(eElement, "minplayers")); + details.maxplayers = getIntegerFromString(getValueAttribute(eElement, "maxplayers")); + details.playingtime = getIntegerFromString(getValueAttribute(eElement, "playingtime")); + details.minplaytime = getIntegerFromString(getValueAttribute(eElement, "minplaytime")); + details.maxplaytime = getIntegerFromString(getValueAttribute(eElement, "maxplaytime")); + details.age = getIntegerFromString(getValueAttribute(eElement, "minage")); + + NodeList pollList = eElement.getElementsByTagName("poll"); + for(int n = 0; n < pollList.getLength(); n++) { + Node pollNode = pollList.item(n); + if (pollNode.getNodeType() == Node.ELEMENT_NODE) { + Element pollElement = (Element) pollNode; + if(pollElement.hasAttribute("name")) { + String pollType = pollElement.getAttribute("name"); + if(pollType.equals("suggested_numplayers")) { + for(int p = 0; p < 20; ++p) { + if(p+1 < details.minplayers || p+1 > details.maxplayers) { + details.players.add(Recommendation.N); + } else { + details.players.add(getMajorityRecommendation(pollElement, p+1)); + } + } + } + + } + } // other polls + } + + NodeList linkList = eElement.getElementsByTagName("link"); + for(int n = 0; n < linkList.getLength(); n++) { + Node linkNode = linkList.item(n); + if (linkNode.getNodeType() == Node.ELEMENT_NODE) { + Element linkElement = (Element) linkNode; + if(linkElement.hasAttribute("type")) { + String type = linkElement.getAttribute("type"); + switch(type) { + case "boardgamecategory": + details.category.add(extractLinkFromElement(linkElement)); + break; + case "boardgamemechanic": + details.mechanic.add(extractLinkFromElement(linkElement)); + break; + case "boardgamefamily": + details.family.add(extractLinkFromElement(linkElement)); + break; + case "boardgamedesigner": + details.designer.add(extractLinkFromElement(linkElement)); + break; + case "boardgameartist": + details.artist.add(extractLinkFromElement(linkElement)); + break; + case "boardgamepublisher": + details.publisher.add(extractLinkFromElement(linkElement)); + break; + case "boardgameexpansion": + details.expansion.add(extractLinkFromElement(linkElement)); + break; + case "boardgamedomain": + details.domain.add(extractLinkFromElement(linkElement)); + break; + default: + System.out.println("WARN: ignoring link element: " + type); + } + } + } + } + + Thing thing = new Thing(id, tmd); + thing.setDetails(details); + things.add(thing); + } + } + + + private IdString extractLinkFromElement(Element linkElement) { + if(linkElement.hasAttribute("id") && linkElement.hasAttribute("value")) { + return new IdString(Integer.valueOf(linkElement.getAttribute("id")), linkElement.getAttribute("value")); + } + System.out.println("WARN: Link is missing attributes."); + return null; + } + + private Recommendation getMajorityRecommendation(Element pollElement, int i) { + Recommendation recommendation = null; + + NodeList nodeList = pollElement.getElementsByTagName("results"); + for(int n = 0; n < nodeList.getLength(); ++n) { + Element elem = (Element)nodeList.item(n); + // TODO: numplayers can be X+ where X is the max number of players... + if(elem.hasAttribute("numplayers") && Integer.valueOf(elem.getAttribute("numplayers")) == i) { + int lastvotes = 0; + NodeList results = elem.getElementsByTagName("result"); + for(int r = 0; r < results.getLength(); ++r) { + Element result = (Element)results.item(r); + if(result.hasAttribute("value") && result.hasAttribute("numvotes")) { + Integer numvotes = Integer.valueOf(result.getAttribute("numvotes")); + if(numvotes > lastvotes) { + recommendation = recommendationValueToEnum(result.getAttribute("value")); + } + } + + } + } + } + + return recommendation; + } + + private Recommendation recommendationValueToEnum(String attribute) { + switch(attribute) { + case "Best": + return Recommendation.B; + case "Recommended": + return Recommendation.R; + case "Not Recommended": + return Recommendation.P; + default: + return Recommendation.N; + } + } + + private void extractThingByUser(ArrayList things, Element eElement) { + Integer id = Integer.parseInt(eElement.getAttribute("objectid")); + if(id != 0) { + ThingMetaData tmd = new ThingMetaData( + id, + getValue(eElement, "name"), + getValue(eElement, "image"), + getValue(eElement, "thumbnail") + ); + ThingUserData tud = new ThingUserData(); + tud.setNumPlays(Integer.valueOf(getValue(eElement, "numplays"))); + tud.setComment(getValue(eElement, "comment")); + + // rating + NodeList statsNodes = eElement.getElementsByTagName("stats"); + if(statsNodes.getLength() > 0) { + Node statsNode = statsNodes.item(0); + NodeList statsChildren = statsNode.getChildNodes(); + if(statsChildren.getLength() > 0) { + Element ratingNode = (Element)statsChildren.item(0); + String ratingValue = ratingNode.getAttribute("value"); + if(!ratingValue.equals("N/A")) { + try { + Float rating = Float.valueOf(ratingValue); + tud.setRating(rating); + } + catch(NumberFormatException e) { + System.out.println("WARN: rating value is not a float."); + } + } + } + } + + NodeList statusNodes = eElement.getElementsByTagName("status"); + if(statusNodes.getLength() > 0) { + Element statusNode = (Element)statusNodes.item(0); + tud.setOwn(attributeToBoolean(statusNode, "own")); + tud.setPrevowned(attributeToBoolean(statusNode, "prevowned")); + tud.setFortrade(attributeToBoolean(statusNode, "fortrade")); + tud.setWant(attributeToBoolean(statusNode, "want")); + tud.setWanttoplay(attributeToBoolean(statusNode, "wanttoplay")); + tud.setWanttobuy(attributeToBoolean(statusNode, "wanttobuy")); + tud.setWishlist(attributeToBoolean(statusNode, "wishlist")); + if(statusNode.hasAttribute("wishlistpriority")) { + tud.setWishlistpriority(Integer.valueOf(statusNode.getAttribute("wishlistpriority"))); + } + tud.setPreordered(attributeToBoolean(statusNode, "preordered")); + tud.setLastmodified(statusNode.getAttribute("lastmodified")); + } + + Thing thing = new Thing(id, tmd); + thing.setUserData(tud); + things.add(thing); + } + } + private Boolean attributeToBoolean(Element statusNode, String attribute) { if(statusNode.hasAttribute(attribute)) { @@ -420,7 +558,7 @@ public class BggApi { } return null; } - + private void checkForErrors(Document doc) throws IllegalArgumentException { NodeList nList = doc.getElementsByTagName("error"); if(nList.getLength() > 0) { @@ -433,6 +571,12 @@ public class BggApi { } } + private Integer getIntegerFromString(String s) { + if(s != null && !s.isEmpty()) { + return Integer.valueOf(s); + } + return null; + } private String getValue(Element eElement, String key) { Node node = eElement.getElementsByTagName(key).item(0); @@ -443,5 +587,15 @@ public class BggApi { } } + private String getValueAttribute(Element eElement, String key) { + Node node = (Element)eElement.getElementsByTagName(key).item(0); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element elem = (Element) node; + if(elem.hasAttribute("value")) { + return elem.getAttribute("value"); + } + } + return null; + } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java index bf0423c..009a38c 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java @@ -150,140 +150,57 @@ public class Thing { } + private static String resultHeader() { + String header = "id,name,image,"; + return header; + } + + public static String makeResultHeader() { + String header = Thing.resultHeader() + ThingUserData.resultHeader() + ThingDetails.resultHeader(); + return header; + } + /// convert the thing into a comma-separated line for a result.txt file /** * The format is (without line breaks): - * id,name,designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, + * id,name,designer, + * + * owned,trading,wanting,wishing,userrating,comment,numplays,wishpriority + * + * designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, * age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, - * owned,trading,wanting,wishing,userrating,image,category,mechanic,comment, + * category,mechanic, * 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, * 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, * description,exp,basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, - * contains,contains_name,iscontained,iscontained_name,integration,integration_name, - * numplays,price,userweight,wishpriority,expansions,domain,family,age_poll + * contains,contains_name,iscontained,iscontained_name,integration,integration_name,price, + * expansions,domain,family,age_poll * * @return */ // TODO: rewrite: order isn't important, column names are public String toResultTxtLine() { StringBuilder str = new StringBuilder(); - final String emptyItem = "\"\","; - // id,name,designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, - // age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, + + // id,name,image, str.append(Integer.toString(id)).append(","); str.append(metaData.getName()).append(","); - if(details != null) { - str.append(details.designer).append(","); - str.append(details.publisher).append(","); - str.append(details.artist).append(","); - str.append(details.yearpublished).append(","); - str.append(details.minplayers).append(","); - str.append(details.maxplayers).append(","); - str.append(details.playingtime).append(","); - str.append(details.minplaytime).append(","); - str.append(details.maxplaytime).append(","); - str.append(details.age).append(","); - str.append(details.usersrated).append(","); - str.append(details.average).append(","); - str.append(floatToResult(details.bayesaverage)).append(","); - str.append(details.rank).append(","); - str.append(details.rank_wg).append(","); - str.append(details.numcomments).append(","); - str.append(details.numweights).append(","); - str.append(floatToResult(details.averageweight)).append(","); - str.append(floatToResult(details.stddev)).append(","); - str.append(floatToResult(details.median)).append(","); - } else { - for(int i = 0; i < 20; ++i) { - str.append(emptyItem); - } - } - - // owned,trading,wanting,wishing,userrating, - if(userData != null) { - str.append(userData.getOwn()?"1":"0").append(","); - str.append(userData.getFortrade()?"1":"0").append(","); - str.append(userData.getWant()?"1":"0").append(","); - str.append(userData.getWishlist()?"1":"0").append(","); - str.append(floatToResult(userData.getRating())).append(","); - } - - // image,category,mechanic,comment, str.append(getImagePath()).append(","); - if(details != null) { - str.append(details.category).append(","); - str.append(details.mechanic).append(","); - } else str.append(emptyItem).append(emptyItem); - if(userData != null) { - str.append(userData.getComment()).append(","); - } else str.append(emptyItem); - // 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, - // 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, - if(details != null) { - for(Recommendation r : details.players) { - str.append(r.name()).append(","); - } - } else { - for(int i = 0; i < 20; ++i) { - str.append("N,"); - } - } - // description,exp,basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, - // contains,contains_name,iscontained,iscontained_name,integration,integration_name, - if(details != null) { - str.append(details.description).append(","); - str.append(details.expansion).append(","); - str.append(details.basegameId).append(","); - str.append(details.reimplementId).append(","); - str.append(details.reimplement_name).append(","); - str.append(details.reimplementedById).append(","); - str.append(details.reimplementedByName).append(","); - str.append(details.containsId).append(","); - str.append(details.containsName).append(","); - str.append(details.iscontained).append(","); - str.append(details.iscontained_name).append(","); - str.append(details.integration).append(","); - str.append(details.integration_name).append(","); + if(userData != null) { + str.append(userData.toResultTextLine()); } else { - for(int i = 0; i < 13; ++i) { - str.append(emptyItem); - } + str.append(ThingUserData.emptyReplacerLine()); } - // numplays,price,userweight,wishpriority,expansions,domain,family,age_poll - if(userData != null) { - str.append(userData.getNumPlays()).append(","); - } else str.append(emptyItem); - if(details != null) { - str.append(details.price).append(","); - } else str.append(emptyItem); - // TODO: find out how to get userweight... - str.append(emptyItem); - if(userData != null) { - str.append(userData.getWishlistpriority()).append(","); - } else str.append(emptyItem); if(details != null) { - str.append(details.expansions).append(","); - str.append(details.domain).append(","); - str.append(details.family).append(","); - str.append(details.age_poll).append(","); + str.append(details.toResultTextLine()); } else { - str.append(emptyItem); - str.append(emptyItem); - str.append(emptyItem); - str.append(emptyItem); + str.append(ThingDetails.emptyReplacerLine()); } return str.toString(); } - private String floatToResult(Float rating) { - if(rating == null) { - return ""; - } else { - return Float.toString(rating.floatValue()); - } - } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java index c4dcd82..a21804b 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java @@ -1,16 +1,42 @@ package xyz.veronie.bgg.result; +import java.util.ArrayList; +import java.util.List; + +import xyz.veronie.bgg.result.internal.OutputResultHelper; + public class ThingDetails { Integer id; - String designer; - String publisher; - String artist; + String name; + String description; + String image; + String comment; + Integer yearpublished; Integer minplayers; Integer maxplayers; + + List designer; + List publisher; + List artist; + + List category; + List mechanic; + List expansion; + List domain; + List family; + + Integer playingtime; Integer minplaytime; Integer maxplaytime; + + // poll suggested_numplayers + List players; // 1 to 20 + + // poll suggested_playerage + Float age_poll; + Integer age; Integer usersrated; Integer average; @@ -22,28 +48,120 @@ public class ThingDetails { Float averageweight; Float stddev; Float median; - String image; - IdString[] category; - IdString[] mechanic; - String comment; - Recommendation[] players; // 1 to 20 - String description; - IdString[] expansion; - Integer basegameId; - Integer reimplementId; - String reimplement_name; - Integer reimplementedById; - String reimplementedByName; - Integer containsId; - String containsName; - Integer iscontained; - String iscontained_name; - Integer integration; - String integration_name; + + Integer owning; + Integer trading; + Integer wanting; + Integer wishing; + + + + // Integer basegameId; +// Integer reimplementId; +// String reimplement_name; +// Integer reimplementedById; +// String reimplementedByName; +// Integer containsId; +// String containsName; +// Integer iscontained; +// String iscontained_name; +// Integer integration; +// String integration_name; Integer numplays; Float price; Integer expansions; - String domain; - IdString[] family; - Float age_poll; + + public ThingDetails() { + players = new ArrayList(); + + designer = new ArrayList(); + publisher = new ArrayList(); + artist = new ArrayList(); + category = new ArrayList(); + mechanic = new ArrayList(); + expansion = new ArrayList(); + domain = new ArrayList(); + family = new ArrayList(); + } + + public String toResultTextLine() { + StringBuilder str = new StringBuilder(); + + // designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, + // age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, + // category,mechanic, + str.append(designer).append(","); + str.append(publisher).append(","); + str.append(artist).append(","); + str.append(yearpublished).append(","); + str.append(minplayers).append(","); + str.append(maxplayers).append(","); + str.append(playingtime).append(","); + str.append(minplaytime).append(","); + str.append(maxplaytime).append(","); + str.append(age).append(","); + str.append(usersrated).append(","); + str.append(average).append(","); + str.append(OutputResultHelper.floatToResult(bayesaverage)).append(","); + str.append(rank).append(","); + str.append(rank_wg).append(","); + str.append(numcomments).append(","); + str.append(numweights).append(","); + str.append(OutputResultHelper.floatToResult(averageweight)).append(","); + str.append(OutputResultHelper.floatToResult(stddev)).append(","); + str.append(OutputResultHelper.floatToResult(median)).append(","); + str.append(category).append(","); + str.append(mechanic).append(","); + + // owned,trading,wanting,wishing + str.append(owning).append(","); + str.append(trading).append(","); + str.append(wanting).append(","); + str.append(wishing).append(","); + + // 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, + // 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, + for(Recommendation r : players) { + str.append(r.name()).append(","); + } + + // description,exp, + str.append(description).append(","); + str.append(expansion).append(","); + + + // basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, + // contains,contains_name,iscontained,iscontained_name,integration,integration_name, +// str.append(basegameId).append(","); +// str.append(reimplementId).append(","); +// str.append(reimplement_name).append(","); +// str.append(reimplementedById).append(","); +// str.append(reimplementedByName).append(","); +// str.append(containsId).append(","); +// str.append(containsName).append(","); +// str.append(iscontained).append(","); +// str.append(iscontained_name).append(","); +// str.append(integration).append(","); +// str.append(integration_name).append(","); + + // price,expansions,domain,family,age_poll + str.append(price).append(","); + str.append(expansions).append(","); + str.append(domain).append(","); + str.append(family).append(","); + str.append(age_poll); + + return str.toString(); + } + + public static String emptyReplacerLine() { + return ",,,,,,,,,,,0.0,0.0,,,0,0,0.0,0.0,0.0,\"\",\"\",,,,,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,\"\",\"\",0.0,,\"\",\"\",0.0"; + } + + public static String resultHeader() { + return "designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime,age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median,category,mechanic,1player,2player,3player,4player,5player,6player,7player,8player,9player,10player,11player,12player,13player,14player,15player,16player,17player,18player,19player,20player,description,exp,price,expansions,domain,family,age_poll"; + // not doing: basegame,reimplement,reimplement_name,reimplemented,reimplemented_name,contains,contains_name,iscontained,iscontained_name,integration,integration_name, + } + + } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java index c5ae7eb..30b7fdb 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java @@ -2,10 +2,13 @@ package xyz.veronie.bgg.result; import java.sql.SQLException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Predicate; import javax.annotation.PostConstruct; +import javax.inject.Inject; import javax.inject.Singleton; import org.eclipse.e4.core.di.annotations.Creatable; @@ -15,6 +18,10 @@ import xyz.veronie.bgg.localdb.LocalDbAdapterService; @Creatable @Singleton public class ThingProvider { + + @Inject + private BggApi bggApi; + private LocalDbAdapterService localDbAdapterService; /// list of things. Each ID is expected to exist exactly once. @@ -118,5 +125,20 @@ public class ThingProvider { return localDbAdapterService.loadThingListNames(); } + public void fetchDetails() { + // prepare list of ids to retrieve + Set ids = new HashSet<>(); + for (Thing thing : things) { + if(thing.getDetails() == null) { + ids.add(thing.getId()); + } + } + + ArrayList thingsWithDetails = bggApi.getThingDetails(ids); + for (Thing thing2 : thingsWithDetails) { + System.out.println(thing2.toResultTxtLine()); + } + + } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java index 5cf4e43..8b54102 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java @@ -1,5 +1,7 @@ package xyz.veronie.bgg.result; +import xyz.veronie.bgg.result.internal.OutputResultHelper; + public class ThingUserData { private Integer numplays; private String comment; @@ -123,6 +125,25 @@ public class ThingUserData { this.lastmodified = lastmodified; } + public String toResultTextLine() { + StringBuilder str = new StringBuilder(); + + str.append(OutputResultHelper.floatToResult(getRating())).append(","); + str.append(getComment()).append(","); + str.append(getNumPlays()).append(","); + // TODO: find out how to get userweight... + + return str.toString(); + } + + public static String emptyReplacerLine() { + return "N/A,\"\",0,"; + } + + public static String resultHeader() { + return "userrating,comment,numplays"; + } + // public Float getWeight() { // return weight; // } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java new file mode 100644 index 0000000..523efb5 --- /dev/null +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java @@ -0,0 +1,14 @@ +package xyz.veronie.bgg.result.internal; + +public class OutputResultHelper { + + public static String floatToResult(Float rating) { + if(rating == null) { + return "N/A"; + } else { + return Float.toString(rating.floatValue()); + } + } + + +} diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java index 935d843..2c6e0fe 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/handlers/ExportResultHandler.java @@ -3,10 +3,14 @@ package xyz.veronie.bgg.ui.handlers; import org.eclipse.e4.core.di.annotations.Execute; +import xyz.veronie.bgg.result.BggApi; import xyz.veronie.bgg.result.Thing; import xyz.veronie.bgg.result.ThingProvider; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.inject.Inject; @@ -17,14 +21,17 @@ public class ExportResultHandler { @Inject ThingProvider thingProvider; + @Inject + private BggApi bggApi; + @Execute public void execute() { - List things = thingProvider.getThings(); - for (Thing thing : things) { - System.out.println(thing.toResultTxtLine()); - } + thingProvider.fetchDetails(); } + + + @CanExecute public boolean canExecute() { From be3800e48c26d9e1675f976773bfceeacfc956de Mon Sep 17 00:00:00 2001 From: veronie Date: Sat, 16 Oct 2021 15:21:08 +0200 Subject: [PATCH 4/4] #7 output strings for result.txt are now written correctly to console (can be copied to result.txt file and used to generate card deck in nanDeck) --- .../src/xyz/veronie/bgg/result/BggApi.java | 37 ++++++--- .../src/xyz/veronie/bgg/result/IdString.java | 4 +- .../src/xyz/veronie/bgg/result/Thing.java | 14 ++-- .../xyz/veronie/bgg/result/ThingDetails.java | 83 ++++++++++--------- .../xyz/veronie/bgg/result/ThingProvider.java | 5 +- .../xyz/veronie/bgg/result/ThingUserData.java | 18 ++-- .../result/internal/OutputResultHelper.java | 52 +++++++++++- .../bgg/ui/dialogs/LoadGameListDialog.java | 2 + .../bgg/ui/filters/BggUserSourceFilter.java | 2 +- 9 files changed, 148 insertions(+), 69 deletions(-) diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java index 32db4c7..7ea23bc 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/BggApi.java @@ -381,6 +381,8 @@ public class BggApi { details.maxplaytime = getIntegerFromString(getValueAttribute(eElement, "maxplaytime")); details.age = getIntegerFromString(getValueAttribute(eElement, "minage")); + // number of players recommendations + // Only saving up to 20 players NodeList pollList = eElement.getElementsByTagName("poll"); for(int n = 0; n < pollList.getLength(); n++) { Node pollNode = pollList.item(n); @@ -462,19 +464,32 @@ public class BggApi { NodeList nodeList = pollElement.getElementsByTagName("results"); for(int n = 0; n < nodeList.getLength(); ++n) { Element elem = (Element)nodeList.item(n); - // TODO: numplayers can be X+ where X is the max number of players... - if(elem.hasAttribute("numplayers") && Integer.valueOf(elem.getAttribute("numplayers")) == i) { - int lastvotes = 0; - NodeList results = elem.getElementsByTagName("result"); - for(int r = 0; r < results.getLength(); ++r) { - Element result = (Element)results.item(r); - if(result.hasAttribute("value") && result.hasAttribute("numvotes")) { - Integer numvotes = Integer.valueOf(result.getAttribute("numvotes")); - if(numvotes > lastvotes) { - recommendation = recommendationValueToEnum(result.getAttribute("value")); + if(elem.hasAttribute("numplayers")) { + String attr = elem.getAttribute("numplayers"); + boolean numPlayersDoesMatch = false; + // numplayers can be X+ where X is the max number of players... + if(attr.endsWith("+")) { + attr = attr.substring(0, attr.length()-1); + if(Integer.valueOf(attr) <= i) { + numPlayersDoesMatch = true; + } + } else { + numPlayersDoesMatch = Integer.valueOf(attr) == i; + } + + if(numPlayersDoesMatch) { + int lastvotes = 0; + NodeList results = elem.getElementsByTagName("result"); + for(int r = 0; r < results.getLength(); ++r) { + Element result = (Element)results.item(r); + if(result.hasAttribute("value") && result.hasAttribute("numvotes")) { + Integer numvotes = Integer.valueOf(result.getAttribute("numvotes")); + if(numvotes > lastvotes) { + recommendation = recommendationValueToEnum(result.getAttribute("value")); + } } + } - } } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java index c252896..22bb581 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/IdString.java @@ -2,8 +2,8 @@ package xyz.veronie.bgg.result; /// Holds an id and its string representation, for example for Family, Extension, Mechanic, or Category. public class IdString { - int id; - String string; + public int id; + public String string; public IdString(int id, String string) { this.id = id; diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java index 009a38c..bb7fa74 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/Thing.java @@ -14,6 +14,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; +import xyz.veronie.bgg.result.internal.OutputResultHelper; import xyz.veronie.bgg.ui.helpers.Resources; public class Thing { @@ -79,7 +80,8 @@ public class Thing { if(imagePath != null) return imagePath.toString(); else - return ""; + return Paths.get(Resources.INSTANCE.getThumbsDir().toString() + + File.separator + String.valueOf(id) + ".png").toString(); } private void getThumbImage(int thingId) { @@ -183,12 +185,12 @@ public class Thing { StringBuilder str = new StringBuilder(); // id,name,image, - str.append(Integer.toString(id)).append(","); - str.append(metaData.getName()).append(","); - str.append(getImagePath()).append(","); + OutputResultHelper.appendToResult(str, id); + OutputResultHelper.appendToResult(str, metaData.getName()); + OutputResultHelper.appendToResult(str, getImagePath()); if(userData != null) { - str.append(userData.toResultTextLine()); + userData.appendToResult(str); } else { str.append(ThingUserData.emptyReplacerLine()); } @@ -203,4 +205,6 @@ public class Thing { } + + } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java index a21804b..c3ce92e 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingDetails.java @@ -32,7 +32,9 @@ public class ThingDetails { Integer maxplaytime; // poll suggested_numplayers - List players; // 1 to 20 + // The index in the list indicates the number of players + // Recommendation indicates how well it plays based on what the majority thinks + List players; // poll suggested_playerage Float age_poll; @@ -90,48 +92,53 @@ public class ThingDetails { // designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime, // age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median, // category,mechanic, - str.append(designer).append(","); - str.append(publisher).append(","); - str.append(artist).append(","); - str.append(yearpublished).append(","); - str.append(minplayers).append(","); - str.append(maxplayers).append(","); - str.append(playingtime).append(","); - str.append(minplaytime).append(","); - str.append(maxplaytime).append(","); - str.append(age).append(","); - str.append(usersrated).append(","); - str.append(average).append(","); - str.append(OutputResultHelper.floatToResult(bayesaverage)).append(","); - str.append(rank).append(","); - str.append(rank_wg).append(","); - str.append(numcomments).append(","); - str.append(numweights).append(","); - str.append(OutputResultHelper.floatToResult(averageweight)).append(","); - str.append(OutputResultHelper.floatToResult(stddev)).append(","); - str.append(OutputResultHelper.floatToResult(median)).append(","); - str.append(category).append(","); - str.append(mechanic).append(","); + OutputResultHelper.appendToResult(str, OutputResultHelper.getFirstIdListItem(designer)); + OutputResultHelper.appendToResult(str, OutputResultHelper.getFirstIdListItem(publisher)); + OutputResultHelper.appendToResult(str, OutputResultHelper.getFirstIdListItem(artist)); + OutputResultHelper.appendToResult(str, yearpublished); + OutputResultHelper.appendToResult(str, minplayers); + OutputResultHelper.appendToResult(str, maxplayers); + OutputResultHelper.appendToResult(str, playingtime); + OutputResultHelper.appendToResult(str, minplaytime); + OutputResultHelper.appendToResult(str, maxplaytime); + OutputResultHelper.appendToResult(str, age); + OutputResultHelper.appendToResult(str, usersrated); + OutputResultHelper.appendToResult(str, average); + OutputResultHelper.appendToResult(str, bayesaverage); + OutputResultHelper.appendToResult(str, rank); + OutputResultHelper.appendToResult(str, rank_wg); + OutputResultHelper.appendToResult(str, numcomments); + OutputResultHelper.appendToResult(str, numweights); + OutputResultHelper.appendToResult(str, averageweight); + OutputResultHelper.appendToResult(str, stddev); + OutputResultHelper.appendToResult(str, median); + OutputResultHelper.appendToResult(str, category); + OutputResultHelper.appendToResult(str, mechanic); // owned,trading,wanting,wishing - str.append(owning).append(","); - str.append(trading).append(","); - str.append(wanting).append(","); - str.append(wishing).append(","); + OutputResultHelper.appendToResult(str, owning); + OutputResultHelper.appendToResult(str, trading); + OutputResultHelper.appendToResult(str, wanting); + OutputResultHelper.appendToResult(str, wishing); // 1player,2player,3player,4player,5player,6player,7player,8player,9player,10player, // 11player,12player,13player,14player,15player,16player,17player,18player,19player,20player, - for(Recommendation r : players) { - str.append(r.name()).append(","); + for(int i = 0; i < players.size(); ++i) { + Recommendation r = players.get(i); + if(r != null) { + str.append(r.name()); + } + str.append(","); } // description,exp, - str.append(description).append(","); - str.append(expansion).append(","); + OutputResultHelper.appendToResult(str, description); + OutputResultHelper.appendToResult(str, expansion); // basegame,reimplement,reimplement_name,reimplemented,reimplemented_name, // contains,contains_name,iscontained,iscontained_name,integration,integration_name, + // TODO: // str.append(basegameId).append(","); // str.append(reimplementId).append(","); // str.append(reimplement_name).append(","); @@ -143,23 +150,25 @@ public class ThingDetails { // str.append(iscontained_name).append(","); // str.append(integration).append(","); // str.append(integration_name).append(","); +// str.append("0,,\"\",,\"\",,\"\",,\"\",,\"\""); // price,expansions,domain,family,age_poll - str.append(price).append(","); - str.append(expansions).append(","); - str.append(domain).append(","); - str.append(family).append(","); - str.append(age_poll); + OutputResultHelper.appendToResult(str, price); + OutputResultHelper.appendToResult(str, expansions); + OutputResultHelper.appendToResult(str, domain); + OutputResultHelper.appendToResult(str, family); + str.append(OutputResultHelper.floatToResult(age_poll)); return str.toString(); } + public static String emptyReplacerLine() { return ",,,,,,,,,,,0.0,0.0,,,0,0,0.0,0.0,0.0,\"\",\"\",,,,,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,\"\",\"\",0.0,,\"\",\"\",0.0"; } public static String resultHeader() { - return "designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime,age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median,category,mechanic,1player,2player,3player,4player,5player,6player,7player,8player,9player,10player,11player,12player,13player,14player,15player,16player,17player,18player,19player,20player,description,exp,price,expansions,domain,family,age_poll"; + return "designer,publisher,artist,yearpublished,minplayers,maxplayers,playingtime,minplaytime,maxplaytime,age,usersrated,average,bayesaverage,rank,rank_wg,numcomments,numweights,averageweight,stddev,median,category,mechanic,owned,trading,wanting,wishing,1player,2player,3player,4player,5player,6player,7player,8player,9player,10player,11player,12player,13player,14player,15player,16player,17player,18player,19player,20player,description,exp,price,expansions,domain,family,age_poll"; // not doing: basegame,reimplement,reimplement_name,reimplemented,reimplemented_name,contains,contains_name,iscontained,iscontained_name,integration,integration_name, } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java index 30b7fdb..8b2c080 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingProvider.java @@ -134,9 +134,10 @@ public class ThingProvider { } } + System.out.println(Thing.makeResultHeader()); ArrayList thingsWithDetails = bggApi.getThingDetails(ids); - for (Thing thing2 : thingsWithDetails) { - System.out.println(thing2.toResultTxtLine()); + for (Thing thing : thingsWithDetails) { + System.out.println(thing.toResultTxtLine()); } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java index 8b54102..5d6d00d 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/ThingUserData.java @@ -125,14 +125,16 @@ public class ThingUserData { this.lastmodified = lastmodified; } - public String toResultTextLine() { - StringBuilder str = new StringBuilder(); - - str.append(OutputResultHelper.floatToResult(getRating())).append(","); - str.append(getComment()).append(","); - str.append(getNumPlays()).append(","); + public void appendToResult(StringBuilder str) { + OutputResultHelper.appendToResult(str, getRating()); + OutputResultHelper.appendToResult(str, getComment()); + OutputResultHelper.appendToResult(str, getNumPlays()); // TODO: find out how to get userweight... - + } + + public String toString() { + StringBuilder str = new StringBuilder(); + appendToResult(str); return str.toString(); } @@ -141,7 +143,7 @@ public class ThingUserData { } public static String resultHeader() { - return "userrating,comment,numplays"; + return "userrating,comment,numplays,"; } // public Float getWeight() { diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java index 523efb5..f3ed241 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/result/internal/OutputResultHelper.java @@ -1,14 +1,60 @@ package xyz.veronie.bgg.result.internal; +import java.util.List; + +import xyz.veronie.bgg.result.IdString; + public class OutputResultHelper { - public static String floatToResult(Float rating) { - if(rating == null) { + public static String floatToResult(Float f) { + if(f == null) { return "N/A"; } else { - return Float.toString(rating.floatValue()); + return Float.toString(f.floatValue()); } } + + public static String getFirstIdListItem(List list) { + if(list != null && list.size() > 0) { + return list.get(0).string; + } + return ""; + } + + public static void appendToResult(StringBuilder str, String string) { + str.append("\""); + if(string != null) { + str.append(string); + } + str.append("\","); + } + + public static void appendToResult(StringBuilder str, Integer i) { + if(i != null) { + str.append(Integer.toString(i)); + } + str.append(","); + } + + public static void appendToResult(StringBuilder str, Float f) { + str.append(OutputResultHelper.floatToResult(f)).append(","); + } + + public static void appendToResult(StringBuilder str, List list) { + if(list == null) { + str.append(","); + return; + } + + str.append("\""); + String nanDeckSeparator = ""; + for (IdString idString : list) { + str.append(nanDeckSeparator); + nanDeckSeparator = "\\13\\"; + str.append(idString.string); + } + str.append("\","); + } } diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/LoadGameListDialog.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/LoadGameListDialog.java index 74a037b..85eb8d5 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/LoadGameListDialog.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/dialogs/LoadGameListDialog.java @@ -106,6 +106,8 @@ public class LoadGameListDialog extends Dialog { tableViewer.setInput(thingLists); int selection = 0; tableViewer.setSelection(new StructuredSelection(tableViewer.getElementAt(selection)), true); + selectedName = (String)tableViewer.getStructuredSelection().getFirstElement(); + tableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override diff --git a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/filters/BggUserSourceFilter.java b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/filters/BggUserSourceFilter.java index b3195ba..fc3dc97 100644 --- a/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/filters/BggUserSourceFilter.java +++ b/xyz.veronie.bgg.ui/src/xyz/veronie/bgg/ui/filters/BggUserSourceFilter.java @@ -87,7 +87,7 @@ public class BggUserSourceFilter { bottomComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); Label infoLabel = new Label(bottomComposite, SWT.LEFT); - infoLabel.setText("Select which flags are used as filter. Filters follow 'AND' rule."); + infoLabel.setText("Select which flags are used as filter. Filters follow 'AND' rule for each game."); GridData gdInfo = new GridData(SWT.FILL, SWT.FILL, true, false); gdInfo.horizontalSpan = 2; infoLabel.setLayoutData(gdInfo);