Skip to content

Commit

Permalink
#122 - Add option to not combine items on export (default)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thource committed Apr 12, 2023
1 parent b11b25f commit 8b25705
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/dictionaries/adamsharp.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rootProject.name = 'dudewheresmystuff'
rootProject.name = 'dude-wheres-my-stuff'
154 changes: 154 additions & 0 deletions src/main/java/dev/thource/runelite/dudewheresmystuff/DataExporter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package dev.thource.runelite.dudewheresmystuff;

import static net.runelite.client.RuneLite.RUNELITE_DIR;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;
import jdk.internal.jline.internal.Nullable;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
class DataExporter {

private static final File EXPORT_DIR = new File(RUNELITE_DIR, "dudewheresmystuff");

private final String displayName;
private final StorageManagerManager storageManagerManager;
@Setter private boolean mergeItems = false;

DataExporter(String displayName, StorageManagerManager storageManagerManager) {
this.displayName = displayName;
this.storageManagerManager = storageManagerManager;
}

void writeItemStack(BufferedWriter writer, ItemStack itemStack) throws IOException {
writeItemStack(writer, itemStack, null, null);
}

void writeItemStack(BufferedWriter writer, ItemStack itemStack,
@Nullable StorageManager<?, ?> storageManager, @Nullable Storage<?> storage)
throws IOException {
String name = itemStack.getName();
if (!mergeItems && itemStack.getId() != itemStack.getCanonicalId() && itemStack.isStackable()) {
name += " (noted)";
}
String escapedName = name.replace(",", "").replace("\n", "");

writer.write(String.format("%d,%s,%d", itemStack.getCanonicalId(), escapedName,
itemStack.getQuantity()));
if (!mergeItems && storage != null && storageManager != null) {
writer.write(String.format(",%s,%s", storageManager.getConfigKey(), storage.getName()));
}
writer.write(String.format("%n"));
}

void writeMergedItems(BufferedWriter writer) throws IOException {
// Include a CSV header describing the columns
writer.write("ID,Name,Quantity\n");

for (ItemStack itemStack : getMergedItems()) {
writeItemStack(writer, itemStack);
}
}

void writeStorageManager(BufferedWriter writer, StorageManager<?, ?> storageManager)
throws IOException {
if (!storageManager.isEnabled()) {
return;
}

for (Storage<?> storage : storageManager.getStorages()) {
if (!storage.isEnabled() || !storage.isWithdrawable()) {
continue;
}

for (ItemStack itemStack : storage.getItems()) {
if (itemStack.getQuantity() == 0 || itemStack.getId() == -1) {
continue;
}

writeItemStack(writer, itemStack, storageManager, storage);
}
}
}

void writeAll(BufferedWriter writer) throws IOException {
// Include a CSV header describing the columns
writer.write("ID,Name,Quantity,Storage Category,Storage Type\n");

for (StorageManager<?, ?> storageManager : storageManagerManager.getStorageManagers()) {
writeStorageManager(writer, storageManager);
}
}

String export() throws IllegalArgumentException, IOException {
if (displayName.equals("")) {
throw new IllegalArgumentException("No display name");
}

File userDir = new File(EXPORT_DIR, displayName);
String fileName = new SimpleDateFormat("yyyyMMdd'T'HHmmss'.csv'").format(
new Date());
String filePath = userDir + File.separator + fileName;

//noinspection ResultOfMethodCallIgnored
userDir.mkdirs();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
if (mergeItems) {
writeMergedItems(writer);
} else {
writeAll(writer);
}
}

return filePath;
}

/**
* Gets all known withdrawable items
*
* <p>If the same item is in multiple storages, the item stacks are combined. "Same item" refers
* to items with the same canonical ID, but note that the actual ID of the stack will be set to
* the ID of one of the items arbitrarily. It is therefore recommended that callers do not use the
* IDs, only the canonical IDs.
*
* @return The item stacks
*/
private Collection<ItemStack> getMergedItems() {
// We need to deduplicate and combine item stacks if they're in multiple
// storages. This is a map from the stack's canonical (unnoted,
// un-placeholdered) ID to its stack.
TreeMap<Integer, ItemStack> items = new TreeMap<>();

storageManagerManager.getStorages()
.filter(Storage::isWithdrawable)
.map(Storage::getItems)
.flatMap(List::stream)
.forEach((ItemStack stack) -> {
if (stack.getQuantity() == 0 || stack.getId() == -1) {
return;
}

int id = stack.getCanonicalId();

ItemStack existing = items.get(id);
if (existing == null) {
// No item yet, insert a copy so that we can modify their quantities later if necessary
items.put(id, new ItemStack(stack));
} else {
// This item was already in there. Update the quantity to include the new stack.
existing.setQuantity(stack.getQuantity() + existing.getQuantity());
}
});

return items.values();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ default boolean deathpileInfoBox() {
return true;
}

@ConfigItem(
keyName = "csvCombineItems",
name = "Combine items in CSV",
description = "When enabled, items from different storages will be combined into a single "
+ "row in the exported CSV file. This means that the CSV won't contain which item is in "
+ "which storage.")
default boolean csvCombineItems() {
return false;
}

@ConfigItem(
keyName = "itemSortMode",
name = "Item Sort Mode",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public abstract class StorageManager<T extends StorageType, S extends Storage<T>
@Getter @Inject protected ItemIdentificationPlugin itemIdentificationPlugin;
@Getter @Inject protected ItemIdentificationConfig itemIdentificationConfig;
@Getter @Inject protected ClientThread clientThread;
protected boolean enabled = true;
@Getter protected boolean enabled = true;
@Getter @Setter protected boolean isPreviewManager = false;
@Getter @Setter protected StorageTabPanel<T, S, ? extends StorageManager<?, ?>> storageTabPanel;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package dev.thource.runelite.dudewheresmystuff;

import static net.runelite.client.RuneLite.RUNELITE_DIR;

import dev.thource.runelite.dudewheresmystuff.carryable.CarryableStorageManager;
import dev.thource.runelite.dudewheresmystuff.coins.CoinsStorageManager;
import dev.thource.runelite.dudewheresmystuff.coins.CoinsStorageType;
Expand All @@ -15,17 +13,10 @@
import dev.thource.runelite.dudewheresmystuff.world.WorldStorageManager;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ItemListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JComboBox;
Expand All @@ -48,8 +39,6 @@
@Getter
public class StorageManagerManager {

public static final File EXPORT_DIR = new File(RUNELITE_DIR, "dudewheresmystuff");

private final CarryableStorageManager carryableStorageManager;
private final CoinsStorageManager coinsStorageManager;
private final DeathStorageManager deathStorageManager;
Expand All @@ -58,7 +47,7 @@ public class StorageManagerManager {
private final PlayerOwnedHouseStorageManager playerOwnedHouseStorageManager;
private final WorldStorageManager worldStorageManager;
@Setter private String displayName;
@Getter() private final List<StorageManager<?, ?>> storageManagers;
@Getter private final List<StorageManager<?, ?>> storageManagers;

private final DudeWheresMyStuffPlugin plugin;
private final ConfigManager configManager;
Expand Down Expand Up @@ -199,46 +188,6 @@ public List<ItemStack> getItems() {
.collect(Collectors.toList());
}

/**
* Gets all known withdrawable items
*
* <p>If the same item is in multiple storages, the item stacks are combined. "Same item" refers
* to items with the same canonical ID, but note that the actual ID of the stack will be set to
* the ID of one of the items arbitrarily. It is therefore recommended that callers do not use the
* IDs, only the canonical IDs.
*
* @return The item stacks
*/
public Collection<ItemStack> getWithdrawableItems() {
// We need to deduplicate and combine item stacks if they're in multiple
// storages. This is a map from the stack's canonical (unnoted,
// un-placeholdered) ID to its stack.
TreeMap<Integer, ItemStack> items = new TreeMap<>();

getStorages()
.filter(Storage::isWithdrawable)
.map(Storage::getItems)
.flatMap(List::stream)
.forEach((ItemStack stack) -> {
if (stack.getQuantity() == 0 || stack.getId() == -1) {
return;
}

int id = stack.getCanonicalId();

ItemStack existing = items.get(id);
if (existing == null) {
// No item yet, insert a copy so that we can modify their quantities later if necessary
items.put(id, new ItemStack(stack));
} else {
// This item was already in there. Update the quantity to include the new stack.
existing.setQuantity(stack.getQuantity() + existing.getQuantity());
}
});

return items.values();
}

/**
* Sets the item sort mode across all storages.
*
Expand Down Expand Up @@ -270,30 +219,13 @@ public void onMenuOptionClicked(MenuOptionClicked menuOption) {

/** Creates a CSV file containing all the items in any exportable storage. */
public void exportItems() {
if (displayName.equals("")) {
log.info("Can't export: no display name");
return;
}
File userDir = new File(EXPORT_DIR, displayName);
String fileName = new SimpleDateFormat("yyyyMMdd'T'HHmmss'.csv'").format(
new Date());
String filePath = userDir + File.separator + fileName;

Collection<ItemStack> items = getWithdrawableItems();

//noinspection ResultOfMethodCallIgnored
userDir.mkdirs();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
// Include a CSV header describing the columns
writer.write("ID,Name,Quantity\n");
DataExporter exporter = new DataExporter(displayName, this);
exporter.setMergeItems(plugin.getConfig().csvCombineItems());

for (ItemStack stack : items) {
String escapedName = stack.getName().replace(",", "").replace("\n", "");
writer.write(
String.format("%d,%s,%d%n", stack.getCanonicalId(), escapedName, stack.getQuantity()));
}
try {
String filePath = exporter.export();
plugin.getNotifier().notify("Items successfully exported to: " + filePath, MessageType.INFO);
} catch (IOException e) {
} catch (IOException | IllegalArgumentException e) {
log.error("Unable to export: " + e.getMessage());
plugin.getNotifier().notify("Item export failed.", MessageType.ERROR);
}
Expand Down

0 comments on commit 8b25705

Please sign in to comment.