eleven weeks of being at home + feelings update

On March 22nd, I blogged about being at home for over a week. I posted an update about two weeks later.

Communicating about how doing

As an update on the original topic, I talked about how I was doing (poorly) on:

  • A number of one on one calls with various people.
  • A weekly call we have (that has about 30 people on it.) I *really* didn’t want to. The person running the call provided a perfect scenario for someone to say that everything wasn’t ok. I didn’t want to. But when things are “normal”, I’m often someone who speaks up when “anyone” could. And I wanted to support the messaging of “it is ok not to be ok.” So I made myself. The following week we had a special senior person guest on the call who said he wanted to hear from three people before we moved on. So I made myself say something again. (just like at all hands meetings where I am often someone to speak up.) The person who runs the meeting commented later that I must be doing better because I’m more chatty on the calls. I nearly lost it. That was definitely NOT what was happening there.
  • One team retrospective. My teammate brought up an important point in this space. I replied to it, but I really had to hold in my feelings so I wouldn’t fall apart.

Tracking feelings

Anyway, this post is about something more positive. Every day for the past 11 weeks, I’ve been keeping track of two metrics

  • How “working during pandemic” is compared to normal voluntary telecommuting.
  • A “difficulty concentrating” score.

I also kept track of some data points like what time I started work, current events and external factors. Some of it was to see if it I could find patterns. And some was to see what I could improve with experimentation.

The first two weeks were absolutely terrible. After that the first metric got better faster than the second. The second metric was way more spiky. Some days were decent (I’m not going to say “good”, but I’ll say decent.) And some were terrible.

I also kept track of how many times I cried during work hours. If you asked me three months ago, I would have told you that I don’t cry often. But the last 11 weeks have been so overwhelming that hasn’t been the case. I used up all my resiliency and didn’t have enough left to deal with normal work events.

All of this data was helpful. It let me volunteer for tasks that were useful to the team but fit better with what I was able to do in the state of affairs. (For example, I like pair programming but doing it for an extended period was too much.)

My boss said he never saw Excel use for feelings before :). It helped though. It gave me data to work with. And it let me something more analytical to anchor on.

Approaching Memorial Day

The week before Memorial Day was the worst since week two. It was a combination fo a few things:

  • As I go for my walks, I see more and more people “breaking quarantine”. So the stats will go up and my governor will wait even longer before allowing people to gather.
  • I felt people were going to “break” quarantine for Memorial Day weekend regardless of direction from the government which again would stretch out when numbers will go down to trigger phase one of reopening.
  • I see more and more people on the street without masks. (If I can see your nose and mouth, your mask doesn’t count!) Yet again, stretch out when numbers will go down.
  • Many messages about disconnecting and recharging over the three day weekend. (Normally, I would spend a bunch of time away from devices. But that’s hard now)
  • Many messages about spending the long weekend with your loved ones. (I live alone)
  • It’s been hard not seeing people in person. After work, it’s only somewhat hard because it is a short time. On the weekends, it is harder. So a three day weekend was not something I was looking forward to as relaxing.
  • I’m on a distributed team. So I got to hear about what teammates were doing for the long weekend where doing things is a thing. I’m happy for them, but it still reminded me that I can’t.
  • Connecticut and New Jersey had both issued directives about the number of people who can gather. CT was at 25 and NJ was at 10. I don’t need 10. I need 3 or 5! But NY hadn’t even been talking about it being a future thing. What we did have was a steady stream of “only do things with members of your household.” So while it’s great that people can go to the beach or whatever, it doesn’t solve the problem of being alone.
  • NY has a fairly extensive plan with the four phases and the details and the discussion about risk. We also have a governor who is on TV every day sharing lots of info. So the fact that he didn’t even talk about a plan for small gatherings wasn’t promising.
  • NY said people could get together in groups of up to 10 for Memorial Day observations and religious observations. Wait a minute. If it’s safe to do that, why can’t I sit outside with one or two friends six feet apart. Now it just feels arbitrary and unfair.

When Things Got Better

  • Friday at 3:30pm was really hard because that’s when the holiday weekend started. So I had a whole stretch of time in front of me.
  • At 3:45, there was a robotics team “history lesson”. I watched it live (archived on YouTube). Just listening to three hours of people I know chat helped me not think about three days of being alone. I also got to say hi to the guest before it started and look up a link during it.
  • When I checked the news after that, I saw that Cuomo announced that groups of 10 can gather for any reason. (According to the media, this was in response to a legal challenge).
  • This helped a little because it gave me a little hope.
  • A little after that, a friend checked in on me. When I commented about the groups of 10, he invited me over to lunch with his family at some point. That gave me a little more hope. (I know what some of my friends feelings on get togethers are. But a lot of people I don’t because discussing something hypothetical isn’t everyone’s cup of tea)
  • Saturday at 4pm, I had a video chat with two friends. And we made actual concrete plans for next weekend. They are safe plans (walk in the park and eat outside.) We even discussed logistics for using the bathroom. And these friends are married so it really only two “groups” getting together. This is pretty much the safest gathering I can do. This filled me up with hope. Because it is real. It’s not that maybe I’ll see someone in person one day. It’s an actual thing I can put in my calendar.
  • Now that I have plans with actual real life human beings for next weekend, I’m ok for this weekend. Granted, I’m still spending the weekend alone with things closed. But it feels more like a holiday where things are closed for the holiday and my friends happen to be busy/away. I’m fine with *one* weekend to myself. I’m not fine with all of them combined with all the weekdays to myself too.

What I Learned

  • I have a lot to be grateful for: I can work from home. I have a job. I’m healthy. I don’t have anyone close to me who died. I’m grateful for all this. But that’s not enough to be ok.
  • Video chats are nowhere near enough to be a substitute for human interaction.
  • Be careful how you word stuff. “I” messages are better than “we all” messages. When listening to someone say “I enjoy that I can have lunch with my kids every day”, I’m fine. It’s great that you can have lunch with your kids. It’s great that you enjoy that. I’m sure they do to. When listening to someone say “It’s great that we can all have lunch with our families every day”, it triggers “when am I going to have lunch with anyone. This sucks”. Very similar message. Very different reaction.
  • It’s ok to talk about feelings at work. I didn’t want to talk about how I was doing. I didn’t want to appear broken or weak. I didn’t want to be the only one struggling. It took a long time to get past that.
  • For the first 2-4 weeks, things just kept getting worse. So there wasn’t even a steady state to try to adjust to. I was very worried that some things that were happening in other countries were coming. One of the big ones I was afraid of was losing the ability to go for a walk.
  • I hate the phrase “new normal” in the context of COVID-19. Prior to this, I had heard the term in relation to financial stuff. Hearing it on things that affect day to day life and emotions is harder. First of all, this situation we are in is NOT normal and is NOT permanent. Pieces of it may be. For example, we may never go back to shaking hands. But the current state of affairs is not permanent. Hearing repeatedly about how we are “adjusting to the new normal” is not helpful when one is falling apart.
  • The weather getting better helped a little because I could take longer walks and sit on my balcony (and make my small apartment a little better.) Which means if we are doing lockdown again next winter, it’s going to be even worse for me.
  • The open-endedness was the hardest part. Granted it is still open ended. For example, I don’t know when I’ll be on a subway again. But knowing that I’ll be seeing friends in a week means I get part of my support system back. And that’ll make it easier to deal with the other parts.

I want to thank everyone who has been virtually checking in on me. Even though it wasn’t “enough” to make things better, I’m sure things would have been far worse without it. And special thanks to the three people who gave me hope this weekend!

My Java Journey

A lot of people are posting about their Java Journey in honor of Java’s 25th birthday today. My turn! (My CV is at jeanneboyarsky.com). This blog post is about the journey and memories

1999: Getting started

As a high school junior (1997-1998), I took AP Computer Science A and learned Pascal. That was the last year Pascal was offered so the school let three of us take the class again as seniors (1998-1999) learning C++ if we self studied for AP Computer Science AB. (There were two exams back then),

I got a 5 (highest score) both exam so was able to get credit for a full year of computer science classes. However, the school used Java. I had two options:

  • Take intro to programming for a third time
  • Take a “challenge test” to show I knew Java.

Other than the name of the language, I didn’t know Java. So I read a book as a high school senior and gave the challenge test a shot. I was woefully unprepared. I didn’t have internet at home then. And I didn’t actually write any Java code. (Because I was focused on the AP exam which was in C++)

The challenge test looked like the CS 101 final. I think the professor lost the challenge test or didn’t bother grading it. I got the array question right because it was the same as the answer would have been in C++. A good amount of the test was about Swing which I didn’t know at all. So there’s no way I could have passed. Who knows. Maybe he figured it was my problem if I didn’t know Java in the advanced classes.

2001: First time using Java professionally

I did two internships in college. (I graduated in three years so there were two summers.) The first one used C++ and SQL. The second one (summer 2001) used Java. I learned more about JSP and XSLT at that internship

2002: Started Full time Java Developer

I’ve been a primarily full time Java developer since 2002. While I’ve also been a software quality engineer, architect and test lead, I’ve been coding in Java straight thru).

2003: Joined CodeRanch

I started answering questions on CodeRanch in 2003. Before that I was active on JGuru. CodeRanch has a great community and I’m still active there. It’s great to be part of a Java Community.

2003 (or 2004): Teaching new features – Java 1.4

Java 1.4 came out in 2002. My career has been in the financial industry so we weren’t the first to upgrade. When we started using Java 1.4, I fell in love with regular expressions. I liked that you could write concise powerful code. You can ever write them clearly. Really. See the blog post. (I like lambdas/streams for the same reason).

I learned that many people at work were afraid of regular expressions. So I taught them. That’s when I learned that teaching people about Java and sharing information about programming was a passion.

2008: First Java contribution to Open Source

My first Java contribution to open source was the ClassPath suite project for JUnit. I fixed a bug and added a feature I wanted.

2014: First Java book published

Scott and I have been working on Java certification books since 2014 covering Java 8 and Java 11.

2016: First time speaking about Java at a user group

I spoke in 2011 and 2015 about testing and agile. And I spoke about Java t my company. But 2016 was my first time doing so publicly.

2017: First time speaking at a Java Conference

In 2014-2015, I made a concerted effort to speak more about Java. (It took me a while to get up the nerve to do it and then to get accepted). I spoke at Spring One and JavaOne in 2017!

2019: Became a Java Champion

Last year, I became a Java champion. So proud!

2020: Celebrated Java’s 25th anniversary

finding most likes on a tag on instagram

Finally, a technical post. I’m on the NYC FRC (FIRST Robotics Challenge) planning committee. Even though the competition was cancelled, the Instagram photo contest was not. The idea was that students post pictures to a specific hashtag and the ones with a lot of likes win.

On a planning call this week, someone noted that it was no longer easy to see the number of likes making it a pain to find the ones with the most likes. That sounded like something a computer would be good at so I volunteered to take care of it.

There were only 86 submissions so mousing over each by hand and keeping a list wouldn’t have been terrible. And I probably could have gotten it done faster that way than by automating it. But where’s the fun in that.

Attempt #1 (failed) – API

There is a documented API to get posts by hashtag. It requires you to have a business or creator account to use. I have neither. This page says anyone can get a creator page. I don’t see that option. Possibly because my account is new or private. And I don’t want to make it public so not going down this road.

Attempt #2 (failed) – screenscraping

I know the URL of the hashtag. And it is available without a login. Great. I can just use Selenium to scrape the data. Well, I couldn’t get this working. The page uses progressive rendering. I did try using code from StackOverflow to page down. I used the ChromeDriver so I could confirm it really was scrolling. It did. But I still didn’t get all the images available to the Selenium driver. So I had to abandon that approach.

private void scrollToBottomOfPage() {
		
  JavascriptExecutor js = (JavascriptExecutor) driver;
  try {
     long lastHeight = ((Number) js.executeScript("return document.body.scrollHeight")).longValue();
     while (true) {
        ((JavascriptExecutor) driver).executeScript("window.scrollTo(0, document.body.scrollHeight);");
        Thread.sleep(2000);
        long newHeight = ((Number) js.executeScript("return document.body.scrollHeight")).longValue();
        if (newHeight == lastHeight) {
           break;
        }
	lastHeight = newHeight;
     }
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}

Attempt #3 (failed) – logging in

When watching it in ChromeDriver, I noticed that there was a prompt about logging in. So I thought maybe that was the problem. I wrote some sloppy Selenium code to login and saw the same behavior. It did login, but I still only got a subset of images. (I would have refactored the timeout, hard coded credentials and loop if it had helped)

driver.get("https://www.instagram.com/");
System.out.println(driver.getPageSource());

List<WebElement> x = driver.findElements(By.tagName("input"));
System.out.println(x);

// TODO change timeout to a wait until
try {
   Thread.sleep(5000);
} catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
}

WebElement username = driver.findElement(By.name("username"));
WebElement password = driver.findElement(By.name("password"));

// TODO don't hard code
username.sendKeys("xxx");
password.sendKeys("xxx");

// TODO rewrite
List<WebElement> l = driver.findElements(By.tagName("button"));
System.out.println(l);
for (WebElement webElement : l) {
   System.out.println(webElement.getAttribute("type"));
   if (webElement.getAttribute("type").equals("submit")) {
     webElement.click();
   }
}

Attempt #4 (failed) – save file

At this point, I decided to stop messing with Selenium and just download the data myself. I opened the web page and scrolled to the bottom to get all the images. I then saved the page in chrome to get all the files. And… still didn’t have everything. This suggests the page is set up to not store everything and no amount of fiddling with Selenium was going to work.

Attempt #5 (failed) – network traffic

The files are all downloaded in my browser at some point. So I used Chrome’s network traffic monitor (in developer tools). Unfortunately, you can’t get the actual Instagram URL from the image link used for the CDN (content delivery network)

Attempt #6 (success kinda) – JSON

The “kinda” is because I don’t have paging working and the__a API is deprecated

Then I found this post which tells me I can use https://www.instagram.com/explore/tags/frcnyc2020/?__a=1 to get the results as JSON. Whoo hoo! This returns the data. Then it was just a matter of parsing it and creating the report.

That worked. The completed code is below and on GitHub

package com.jeanneboyarsky.instagram;

import java.util.*;
import java.util.Map.*;
import java.util.stream.*;

import org.junit.jupiter.api.*;
import org.openqa.selenium.*;
import org.openqa.selenium.htmlunit.*;

import com.fasterxml.jackson.databind.*;

public class CountLikesIT {

  private static final String TAG = "frcnyc2020";

  private WebDriver driver;

  @BeforeEach
  void setup() {
    driver = new HtmlUnitDriver();
  }

  @AfterEach
  void tearDown() {
    if (driver != null) {
      driver.close();
    }
  }

  @Test
  void graphQlJson() throws Exception {
    // https://stackoverflow.com/questions/43655098/how-to-get-all-instagram-posts-by-hashtag-with-the-api-not-only-the-posts-of-my
    // "count" shows up 258 times (this is three times per image)
    // 1) edge_media_to_comment
    // 2) edge_liked_by
    // 3) edge_media_preview_like - looks same as #2
    String json = getJson();

    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode rootNode = objectMapper.readTree(json);
    List<JsonNode> nodes = rootNode.findValues("node");
		
    Map<String, Integer> result = nodes.stream()
   // node occurs at multiple levels; we only want the ones that go with posts
   .filter(this::isPost)
   .collect(Collectors.toMap(this::getUrl, this::getNumLikes, 
  // ignore duplicates by choosing either
    (k, v) -> v));
	
   printDescendingByLikes(result);
  }
	
  private String getUrl(JsonNode node) {
    JsonNode shortCodeNode = node.findValue("shortcode");
    return "https://instagram.com/p/" + shortCodeNode.asText();
  }
	
  private int getNumLikes(JsonNode node) {
    JsonNode likeNode = node.get("edge_liked_by");
    return likeNode.get("count").asInt();
  }
	
  private boolean isPost(JsonNode node) {
    return node.findValue("display_url") != null;
  }

  private String getJson() {
    driver.get("https://www.instagram.com/explore/tags/" + TAG + "/?__a=1");
    String pageSource = driver.getPageSource();
    return removeHtmlTagsSinceReturnedAsWebPage(pageSource);
}

  private String removeHtmlTagsSinceReturnedAsWebPage(String pageSource) {
    String openTag = "<";
    String closeTag = ">";
    String anyCharactersInTag = "[^>]*";
		
    String regex = openTag + anyCharactersInTag + closeTag;
    return pageSource.replaceAll(regex, "");
  }

  private void printDescendingByLikes(Map<String, Integer> result) {
    Comparator<Entry<String, Integer>> comparator = 
       Comparator.comparing((Entry<String, Integer> e) -> e.getValue())
      .reversed();
	
    result.entrySet().stream()
       .sorted(comparator)
       .map(e -> e.getValue() + "\t" + e.getKey())
       .forEach(System.out::println);
    }
}