Weekly digest 2022-48

“Seltsam, im Nebel zu wandern! Leben ist Einsam sein. Kein Mensch kennt den andern, jeder ist allein.”
December 2, 2022 by Michael

It’s getting bleak outside but as you might have guessed, it does not stop me from getting outside. Last week I managed to increase my Veloviewer maximum square to 40×40 tiles. That is kinda of an online game connected to Strava. I am a big fan of both services. The goal with the squad is to reach as many small tiles as possible from a location and create a square as big as possible. All running, cycling, hiking and swimming activities count and one small tile is one square mile. This is how my square looks like:



Most of it is cycling but also some hiking. Next year it’s going to get though. In the east the open coal mines start and in the south there’s a protected moor area and some tiles are… difficult to get. So I am gonna focus on the north and west.

Ongoing fires in IT world

Twitter, Meta and other big, add driven companies are dumpster fires these days, laying of workers in the thousands. While I feel sad for the people, it’s maybe for the better: Are these companies producing value or just issues? I don’t know. But the recession is reaching others as well. Others, that actually produce something tangible. Yesterday, Ash Kulkarni from Elastic published the linked letter about reducing teams by 13% of people. It feels like hitting close: Elastic is an open source company as well, with a lot of good people and many that I actually do know personally. I always assumed they are an economical wealthy company but alas, less growth than usual and not even losses are quite bad these days.

Earlier this year in June I read a book named “RCE”, by Sibylle Berg. RCE is the successor to GRM and I do recommend both of them:

The predictions in GRM about UK, social descent on the one hand and incredible wealth on the other felt like a prediction come true already and apparently, the things in RCE start happening, too. Why have we IT works thought at all that not having unions, not working together in many cases would do us any good? Why could we have thought just for a second that we aren’t expendables? Curious and frightened how things will continue.

More good reads

But more on the reading side of things. I finished “Sternkinder” by Clara Asscher-Pinkhof. A book from a Holocaust survivor writing about the Holocaust from children’s perspective. Going from Sternkinder to Sternhaus, Sternwüste and finally Sternhölle. It’s all short pieces and beautiful, but simple language, technical easy to read, but heartbreaking. I had to stop several times. But sure, let’s let idiot “musicians” like Mr. West talk about the “good things” Hitler and the Nazis did. Like my father used to say, Ich kann nicht soviel fressen, wie ich kotzen möchte.

Your weekly cup of Java

I am so happy: All PRs in Neo4j-Migrations that in one way or the other deal with JDK or dependency versions closed and dealt with: Upgrades to JDK 17, Spring Boot 3 and the latest and greatest Neo4j Java Driver done. The Quarkus Neo4j extension has just been released as 2.0 as well, including the latest upgrade and based on all of that, Gerrit released Neo4j-OGM 4.0 with support for Neo4j 5.

The last nights I did something else for a change, basically combined sports and Java and created this: Garmin Babel. It is a tool to work with your archive data from Garmin.com respectively Garmin Connect.

What? Let me show you:

I love Java and the ecosystem.

Let me point out some tech I use and the people that I know behind it:

And wow, I do love the modern HTTP Client in the JDK. Here’s the full async code to download a bunch of data:

private CompletableFuture<Optional<Path>> scheduleDownload(Activity activity, DownloadFormat format, Tokens tokens, Optional<Path> base) {
  return CompletableFuture
    .supplyAsync(() -> {
      // Randomly sleep a bit
      try {
        Thread.sleep(ThreadLocalRandom.current().nextInt(10) * 100);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
 
      var uri = switch (format) {
        case FIT -> "https://connect.garmin.com/download-service/files/activity/%d".formatted(activity.garminId());
        default -> "https://connect.garmin.com/download-service/export/%s/activity/%d".formatted(format.name().toLowerCase(Locale.ROOT), activity.garminId());
      };
 
      return HttpRequest
        .newBuilder(URI.create(uri))
        .header("Authorization", "Bearer %s".formatted(tokens.backend()))
        .header("DI-Backend", "connectapi.garmin.com")
        .header("Cookie", "JWT_FGP=%s".formatted(tokens.jwt()))
        .header("User-Agent", "garmin-babel")
        .GET()
        .build();
    })
    .thenCompose(request -> httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()))
    .thenApply(res -> {
      if (res.statusCode() != 200) {
        throw new ConnectException("HTTP/2 %d for %s".formatted(res.statusCode(), res.uri()));
      }
      try {
        var suffix = switch (format) {
          case FIT -> "zip";
          default -> format.name().toLowerCase(Locale.ROOT);
        };
        var filename = "%d.%s".formatted(activity.garminId(), suffix);
        var targetFile = base.map(v -> v.resolveSibling(filename)).orElseGet(() -> Path.of(filename));
        Files.copy(res.body(), targetFile, StandardCopyOption.REPLACE_EXISTING);
        return Optional.of(targetFile);
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    })
    .thenApply(path -> {
      path.ifPresent(v -> System.err.printf("Stored data for %d %s as %s%n", activity.garminId(), activity.name() == null ? "" : "(" + activity.name() + ")", v.toAbsolutePath()));
      return path;
    })
    .exceptionally(e -> {
      var prefix = "Error downloading activity %d ".formatted(activity.garminId());
      if (e.getCause() instanceof ConnectException connectException) {
        System.err.println(prefix + connectException.getHttpStatusAndUri());
      } else {
        System.err.println(prefix + e.getMessage());
      }
 
      return Optional.empty();
    });
}

From scheduleDownload.

Anyway, if you find michael-simons/garmin-babel useful, please let me know. I had a lot of fun writing it, but I realize I am not 30 anymore and hacking away 3 nights straight in a row took its tool. Super happy that my employer Neo4j continued with the 4th Neo4j Global Wellness day in 2022 today and I just can sit down with a cup of coffee and write this piece here.

Before I leave, todays featured biking picture:



Taken during the bleak and dark morning hours, accompanying my kid to school. In that sense, do something nice, not only over the weekend, but also during the week. Time is too short to waste it.

No comments yet

Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *