Skip to content

Commit

Permalink
Changed Progress.of() to take in Collections and Arrays
Browse files Browse the repository at this point in the history
Got rid Trackable, since it was unintuitive to use
Also adapted Demo
Also adapted README
  • Loading branch information
danielbinder committed Oct 20, 2023
1 parent e81aa1d commit 38308e9
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 121 deletions.
38 changes: 19 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@

This is a simple library for adding one or multiple console progress counter(s) to your project.

![demo.png](demo.png)
### I want to use and/or adapt this project

Go for it - I tried to make everything as readable and modifiable as possible.
Check out the explanation below, and the license in the 'LICENSE' file.
Regardless of the license, it would be cool if you somehow mentioned, that you got this code from here :)

### How to use
1) Implement the [Trackable.java](https://github.com/danielbinder/Progress/blob/master/src/Trackable.java) in the Objects you want to track.
2) Wherever you want to track progress, wrap the Object to track with
- `Progress.of(Object)`
- `Progress.of(Description, Object)`
- `Progress.of(Object1, Object2, ...)`
- `Progress.of(List<Object>)`
- `Progress.of(List<Description>, List<Object>)`
- `Progress.of(Map<Object, Description>)`
3) All `Progress.of(...)` methods in [Progress.java](https://github.com/danielbinder/Progress/blob/master/src/Progress.java) return the incoming objects back to you.
If a single object was passed, it is returned directly.
Multiple objects are returned as a List<Object>.
1) Add as a library (here shown in IntelliJ) or add the code directly to your project
![lib.png](lib.png)
2) Wrap the Object you want to track in `Progresss.of("optional description", someObj)` wherever you want to consume it e.g.
- You can use it anywhere, where you would use an iterator of a Collection or an Array, since it literally returns one
- If you don't add a description, an incrementing counter will be added instead
- ```
for(Thing t : Progress.of(thingList)) {
doSomethingWith(t);
}
```
3) You can also register multiple Collections in multiple Threads
4) If you want to reset the counter use `Progress.reset()`
5) You can find more details in the [Demo.java](https://github.com/danielbinder/Progress/blob/master/src/Demo.java)
### Implementation details
- Update rate of the counter is hard coded to 100ms.
- The goal of complicated progress update loop is to reduce IO access as much as possible.
- The progress will be shown as a percentage (int)
- Time is `elapsed time | estimated time left`
- Update rate of the counter is hard coded to 100ms.
- The goal of complicated progress update loop is to reduce IO access as much as possible.
- The progress will be shown as a percentage (int)
- Time is `elapsed time | estimated time left`
### Demo
The [Demo.java](https://github.com/danielbinder/Progress/blob/master/src/Demo.java) demonstrates some usages described above, where the progress is determined by counters that increase randomly.
Here you can see how it should look in your console:

![demo.png](demo.png)
Here you can see how it should look in your console above.
Binary file modified demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added lib.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 23 additions & 27 deletions src/Demo.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

public class Demo implements Trackable { // Demo needs to be Trackable!
private int i = 0;

public class Demo {
public void count() {
Random r = new Random();

// Simulate real progress by increasing i
for(i = 0; i < 100; i++) {
try {
// Sleep up to 1s
Thread.sleep(r.nextInt(1, 1000));
} catch(InterruptedException ignored) {}
}
}

@Override
public int currentProgress() {
// In a real example 'i' would be calculated in some way e.g. (int) ((filesCopied / allFiles) * 100)
return i;
// Simulate real progress
try {
// Sleep up to 1s
Thread.sleep(r.nextInt(1, 1000));
} catch(InterruptedException ignored) {}
}

public static void main(String[] args) {
System.out.println("Single counter demo:");
Progress.of(new Demo()) // Returns the Demo object passed in
.count();
List<Demo> demos = IntStream.range(0, 100)
.mapToObj(i -> new Demo())
.toList();

for(Demo d : Progress.of(demos)) d.count();

// My console won't update to 100% unless I add this
try {
// Without this, my console doesn't see the need to update to 100%
Thread.sleep(100);
} catch(InterruptedException ignored) {}

System.out.println("\n\nMulticounter demo:");
Progress.of(new Demo(),
new Demo(),
new Demo(),
new Demo(),
new Demo())
// Returns List<Demo> objects passed in
.stream()
Progress.reset();
IntStream.range(0, 5)
.mapToObj(l -> IntStream.range(0, 100)
.mapToObj(i -> new Demo())
.toList())
.parallel()
.forEach(Demo::count);
// Register multiple lists
.forEach(list -> {
for(Demo d : Progress.of(list)) d.count();
});
}
}
131 changes: 62 additions & 69 deletions src/Progress.java
Original file line number Diff line number Diff line change
@@ -1,105 +1,100 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;

public class Progress {
public static <T extends Trackable> T of(T trackable) {
return of("", trackable);
}
public class Progress<T> implements Iterable<T> {
private static final Map<String, Integer> progressMap = new HashMap<>();
private static int i = 0;
private static long startTime;
private final String description;
private final List<T> list;

public static <T extends Trackable> T of(String description, T trackable) {
new Thread() {
@Override
public void run() {
super.run();

int currentProgress = trackable.currentProgress();
String s = "";

long startTime = System.currentTimeMillis();

while(currentProgress < 100) {
int currentProgressTemp = trackable.currentProgress();
static {
reset();
}

if(currentProgress != currentProgressTemp) {
System.out.print("\b".repeat(s.length()));
private Progress(String description, List<T> list) {
progressMap.put(description, 0);

currentProgress = currentProgressTemp;
s = "\u001B[32mProgress" + (description.isBlank() ? "" : "[" + description + "]") + ": " +
currentProgress + "% " + getTimeString(startTime, currentProgress) +
"\u001B[0m";
this.description = description;
this.list = list;
}

System.out.print(s);
}
public static <T> Progress<T> of(String description, List<T> list) {
return new Progress<>(description, list);
}

try {
Thread.sleep(100);
} catch(InterruptedException ignored) {}
}
}
}.start();
public static <T> Progress<T> of(List<T> list) {
return of(String.valueOf(i++), list);
}

return trackable;
public static <T> Progress<T> of(String description, Collection<T> collection) {
return of(description, collection.stream().toList());
}

/** Multiprogress */
public static <T> Progress<T> of(Collection<T> collection) {
return of(collection.stream().toList());
}

@SafeVarargs
public static <T extends Trackable> List<T> of(T...trackables) {
return of(Arrays.stream(trackables).toList());
public static <T> Progress<T> of(String description, T...array) {
return of(description, Arrays.asList(array));
}

public static <T extends Trackable> List<T> of(List<T> trackables) {
return of(trackables.stream()
.map(t -> String.valueOf(trackables.indexOf(t)))
.toList(),
trackables);
public static <T> Progress<T> of(T...array) {
return of(Arrays.asList(array));
}

public static <T extends Trackable> List<T> of(Map<T, String> trackablesWithDescriptions) {
List<String> descriptions = new ArrayList<>();
List<T> trackables = new ArrayList<>();
@Override
public Iterator<T> iterator() {
return new Iterator<>() {
private int i = 0;

trackablesWithDescriptions.forEach((k, v) -> {
trackables.add(k);
descriptions.add(v);
});
@Override
public boolean hasNext() {
return i < list.size();
}

return of(descriptions, trackables);
@Override
public T next() {
updateProgress(description, (100 * i) / Math.max(1, list.size() - 1));

return list.get(i++);
}
};
}

public static <T extends Trackable> List<T> of(List<String> descriptions, List<T> trackables) {
List<String> transformedDescriptions = descriptions.stream()
.map(d -> "Progress[" + d + "]: ")
.toList();
private static void updateProgress(String description, int percentage) {
progressMap.put(description, percentage);
}

public static void reset() {
progressMap.clear();
i = 0;
startTime = System.currentTimeMillis();
new Thread() {
@Override
public void run() {
super.run();

while(progressMap.isEmpty()) {
try {
Thread.sleep(100);
} catch(InterruptedException ignored) {}
}

String oldString = "";
int minProgress = trackables.stream()
.map(Trackable::currentProgress)
int minProgress = progressMap.values().stream()
.min(Integer::compareTo)
// stop if no minimum found
.orElse(100);

long startTime = System.currentTimeMillis();

while(minProgress < 100) {
minProgress = trackables.stream()
.map(Trackable::currentProgress)
minProgress = progressMap.values().stream()
.min(Integer::compareTo)
// stop if no minimum found
.orElse(100);



String currString = "\u001B[32m" + transformedDescriptions.stream()
.map(d -> d + trackables.get(transformedDescriptions.indexOf(d)).currentProgress() + "% ")
String currString = "\u001B[32m" + progressMap.keySet().stream()
.map(d -> "[" + d + "] " + progressMap.get(d) + "% ")
.collect(Collectors.joining("", "", getTimeString(startTime, minProgress)));

if(!currString.equals(oldString)) {
Expand All @@ -116,8 +111,6 @@ public void run() {
}
}
}.start();

return trackables;
}

private static String getTimeString(long startTime, int minProgress) {
Expand Down
6 changes: 0 additions & 6 deletions src/Trackable.java

This file was deleted.

0 comments on commit 38308e9

Please sign in to comment.