Today, I have a guest post for you from a very famous author.
By: Avik Kurup
If you’re going to visit Italy, I believe that these three tips will make you have an amazing trip.
First and foremost, visit Florence, Rome, Venice, and Cinque Terre. Florence has delicious food and the best gelato out of all of these places. Rome has the best pizza. Also, the colosseum and the forum are important and fun sites to see there. In Venice they have incredible pasta and seafood and many places to ride on boats.
Another important thing to know about visiting Italy is that you don’t want to drive. Cars are difficult to use because there are a lot of one way roads and confusing traffic lights. You will enjoy walking or going on a bike tour more. You can also use the train for longer distances.
The last tip that will help you enjoy your trip to Italy is you should eat as much pizza, pasta, and gelato as possible. Pizza in Italy has a crunchier crust and they put a ton of cheese. When you order pasta the bowl is humongous, but there is a small hole in the middle that is filled with pasta. It is way more than you think. I recommend the pasta bolognese. Italian gelato is fresh and inexpensive. You can get two flavors in a cup for $2.50!
Clearly, Italy has fantastic sights to see and food to eat. If you follow these three tips you will have a great trip!
The problem
The USB-C cord kept falling out of my Android phone. It started happening in the car. A
little bump while driving would make the cord fall out. Then it started happening even
when I just had it plugged in on a table. I’d come down in the morning and the charge
would be drained because it hadn’t been fully plugged in. Finally, I couldn’t even get
it to charge unless I plugged it in forcefully, and put a book under the cord at a
certain angle to keep it plugged in.
The googling
Googling for this problem didn’t find anything specific to my phone, but I did find
people complaining of this issue and other people telling them that this was because of
lint in the USB-C port.
[1]
[2]
[3]
I just needed to get that lint out somehow.
What didn’t work
- Compressed air
- A toothpick (it was too thick)
- A plastic zip tie (It was too flimsy)
What did work
A SIM card removal tool. You know, one of those things that they send you with a new
phone to poke into the SIM card housing to get it to eject? It was the perfect size and
depth to be able to get all the way around the SIM card and get copious amounts of lint
(ewww!) out of the port. Now my USB-C cord plugs in with a satisfying click and stays
connected.
Philosophical musings
It’s weird that this three year old phone now feels new to me. For a while now, I would
have a little dread every time I went to plug in this phone, knowing that it would be
fiddly and that I might come down to a dead phone in the morning. Now, with this little
improvement, I feel a weird warmth towards the phone again, similar to that feeling I
had when it was a new phone. I cleaned out the lint a few weeks ago, and I still have
this warm sensation towards it. Every time I plug it in, I get a little burst of
happiness when it clicks in satisfyingly. It’s almost as if seeing a problem with the
phone that I didn’t think would exist, and then seeing a solution to that problem, makes
me appreciate the phone more than if it had never had that problem. That just feels
weird and revelatory to me.
I have a new favorite laptop. I needed
a new non-work laptop and Linux is my OS of choice. It has been for at least a decade,
but recently that favoritism has been cemented since I chose to use a Macbook Pro for
work. After 6 months with the Mac, I’m finally starting to feel productive with it, but
it has been a painful re-learning (and un-learning) process. I still fondly return to my
Linux machines at the end of the work day. Mac OS X is just too “in-your-face” for me.
So I started planning to get another ThinkPad when I heard about
frame.work on Hacker News. They are a startup focused on building
functional, modular, repairable laptops which are also beautiful, light and fun to
use. Check out their “About” page for more about their mission, but it resonated with
me. I’ve always loved pokeing around my
computers and it’s unfortunately gotten much harder to do that with all of my electronic
devices. Frame.work is fighting against that trend.
Frame.work laptop contents
They have a DIY option where you install your own RAM, SSD and WiFi module. Even the
non-DIY option comes in with external pluggable modules so you can configure your
accessories. I got 2 USB-C ports, a USB-A port, an HDMI-port, and an SD-card reader. But
it’s simple to pop out a module and pop in a new one. I don’t see myself doing that too
much, but when I do need that SD card reader, I’ll be smiling. Setting up the DIY parts was
fun and easy. Each part has a QR code on the part and on the location in the laptop
where it gets installed, which points to detailed instructions.
Frame.work laptop insides
Ubuntu 20.04 doesn’t work out of the box (WiFi issues, I think?), so I installed 21.04,
and everything works except for the fingerprint reader (which never worked with Ubuntu
for me on my ThinkPad either). There are instructions to build the software needed to
get it to work, but I’ll wait for it to get rolled into a future OS release (I’m old).
Overall, the computer itself is a dream. The screen is large and bright. The keyboard is
dreamy, compared to my Thinkpad X1 Carbon. I think it’s better than my MacBook Pro too,
but honestly that thing is too huge to use as a laptop so I always use it with an
external keyboard. The computer is light and the battery life is more than adequate for
my needs (though there are gripes and workarounds on the forums about this).
Frame.work laptop
I thought that there would be more rough edges to the whole process, but I’ve been
pleasantly surprised. I hope others like it as much as I do so that the company sticks
around!
Hi loyal reader(s)! It is nearly halfway through 2021 and this is my first post of the
year. And writing on this here blog was one of my 2021 resolutions, so I guess that was
a complete failure. Although, maybe I can make up for it by actually writing more
regularly over the next few months. It has been a whirlwind of a year for just about
everyone, right? When the pandemic started, I remember making a list of the things that
I might want to blog about and the only thing I remember from that list was that I
wanted to blog about “not wanting to blog about the pandemic”. It was just too
exhausting to think about. And I think I still feel that way. I have written some
private thoughts about things, but I’ll leave them under wraps.
The biggest change in my life is that I am starting a new job tomorrow. I’ll be a
Software Engineer at Kevel, programming in Clojure (among other languages). I’ve been
infatuated with Clojure (and functional programming in general) for a long time, so I am
beyond excited to be able to use it professionally. This is also the first job that I’ll
be starting as a “remote-only” worker. Normally, I’d walk into the office and introduce
myself to someone and then start to get oriented. It will be weird to do that via an
online chat of some sort. Am I showing my age?
What else is going on? Well I’m not going to talk about the pandemic, but I do encourage
people to go help out people in India (or
wherever else people need help.) We are luckily healthy and vaccinated. The kids have
survived remote schooling and two of them have returned to school partly in-person. I’ve
been running and meditating and gardening, all with minimal success.
So, what is up with you all? Hope to write again before the end of the year.
There’s this cool thing called Advent of Code, described best
here. I found out about it while reading my
favorite weekly weblog
and so I’ve decided to try my fledgling (maybe flailing is a better word?) Elixir skills
on it. There are 2 puzzles each day (day_1_0
and day_1_1
are my solutions). Here’s
my code for Day 1:
@doc """
Find 2 numbers in file that sum to 2020 and return their product.
iex> Aoc.day_1_0()
633216
"""
def day_1_0 do
goal = 2020
list_of_nums = get_list_of_nums()
first_num = find_first_partner(list_of_nums, goal)
first_num * (goal - first_num)
end
@doc """
Find 3 numbers in file that sum to 2020 and return their product.
iex> Aoc.day_1_1()
68348924
"""
def day_1_1 do
goal = 2020
list_of_nums = get_list_of_nums()
# find first number where there exists 2 other numbers that sum to `goal - x`
first_num = Enum.find(list_of_nums, fn x -> find_first_partner(list_of_nums, goal - x) end)
second_num = find_first_partner(list_of_nums, goal - first_num)
first_num * second_num * (goal - first_num - second_num)
end
@doc """
Given a `list` of integers, return the first number in that list which has a
partner in the list that sums to `goal`.
"""
def find_first_partner(list, goal) do
Enum.find(list, fn x -> Enum.member?(list, goal - x) end)
end
defp get_file do
Path.join("data", "day1.txt") |> File.read!()
end
defp get_list_of_nums do
# Split into lines, filter out empty lines, convert each to an integer
get_file()
|> String.split("\n")
|> Enum.filter(fn x -> String.trim(x) != "" end)
|> Enum.map(&String.to_integer/1)
end
The code is also available in Github, which I’ll update if I do find time to keep going:
https://github.com/vkurup/aoc-elixir/ I feel like this could have been done more
efficiently with recursion, but I am not comfortable enough with recursion to know
how…
A sincere thanks to Eric Wastl for spending the enormous time and energy
to create cool puzzles like this.
pyenv is awesome and has changed the way that I manage
my Python environments. One tiny annoyance is that it takes a looong time to install
each Python version, which of course makes sense, since it has to download and compile
each version on your machine. But it’s not a big deal, since you generally just do this
once and then not again until you really need to upgrade to a different Python version.
Recently, though, I was working on a project that uses tox to make sure that the project
runs on multiple Python versions, in this case 3.6, 3.7, 3.8, 3.9. The specific versions
were included in the .python-version
file, which looked like this:
3.9.0
3.8.6
3.7.9
3.6.12
I didn’t have all of these installed, so rather than doing pyenv install 3.9.0
and
then waiting a few minutes, and then repeating the process for each version, I wrote up
this little script, which I named pyinstall.sh
:
#!/bin/bash
# Install all versions specified in .python-version
set -ex
while read version; do
pyenv install -s "$version"
done <.python-version
Since pyenv
is nice enough to include the -s
flag which means “skip if already
installed”, this can be run as many times as you want and it will only install the
version if it’s not already installed
Find disk space hogs:
sudo du -h -d 3 / | grep "^[0-9.]*G"
… which helped me find the command in
yesterday’s post. You can add directories
after the slash to drill down to your heart’s content.
Copy this script into ~/bin/
:
#!/bin/bash
# Removes old revisions of snaps
# CLOSE ALL SNAPS BEFORE RUNNING THIS
set -ex
LANG=en_US.UTF-8 snap list --all | awk '/disabled/{print $1, $3}' |
while read snapname revision; do
sudo snap remove "$snapname" --revision="$revision"
done
Found on
https://www.linuxuprising.com/2019/04/how-to-remove-old-snap-versions-to-free.html and
slightly edited.
I set up an OBi110 device to provide a landline at home. OK, it’s not a land line, it’s
a VOIP (Voice Over Internet Protocol) line, but it performs a similar function. We’ve
always had a landline. Our cell phone coverage is spotty at times and ever since Mala
started working from home, she needs to have a reliable phone line for her global
conference calls.
The problem is that Spectrum was charging us $44.95 per month plus taxes, which took it
near $50 every month. I considered a few options including:
- MagicJack
- Ooma
- I looked at a few others… can’t remember them all
In the end, I settled on buying an ATA device and then connecting it
to a cheaper VOIP service. I chose voip.ms which has been great so
far.
One Time Costs:
- OBi110 ATA device: $19.90
- voip.ms setup fee: $0.40
- voip.ms e911 setup fee: $1.50
Monthly Costs:
- voip.ms fee for an incoming phone number: $0.85
- voip.ms e911 monthly fee: $1.50
Per-Minute Costs:
- voip.ms incoming: $0.009 per minute
I’ll have to keep an eye on my monthly per-minute costs because there is an unlimited
plan that is $4.25 per month, which I’ll switch to if I use more than that per month. In
any case, this is MUCH cheaper than Spectrum.
Now, if you do any research about the OBi110 device, you’ll see everyone telling you not
to buy it because it is out of service and doesn’t support Google Voice. This is true,
ObiTalk doesn’t support the device anymore. When it was supported by the company, you
could just dial a special code on your phone and it would do all the set up for you.
Since it’s not supported, that procedure doesn’t work anymore. But you can manually configure the device with the
excellent docs from voip.ms, and for a geek like me that was not bad. And yes, you can’t
link a Google Voice number to the device, but I didn’t want to do that anyway. I think
if you wanted to keep using a google voice number, you could probably get pretty close
by putting a different number on your device (through voip.ms) and then pointing Google
Voice at that number. Incoming calls would work fine in that way, but I’m not sure about
outgoing calls. If you want to seamlessly use Google Voice on an ATA device, then OBi110
isn’t for you. You’ll need the OBI202 (which was out of stock when I was looking).
I worked on adding tests to some Django forms today and I was reminded that Django forms
operate in two modes. I think of them as the GET mode and the POST mode. The GET mode
displays the form and the POST mode processes the data that you supply to the form. I
don’t know why, but I often forget that. Instead, I sometimes treat them as a normal
Python class, especially when I’m creating automated tests for them. I instantiate
the class (form = MyForm()
) and then just call any of its methods and make sure they
return the right values or achieve the correct side effects. The form that I was working
with today had a couple features. The first was that it provided some helper functions
to the template so that the template could display the form properly. Those are in the
GET mode. So I did something like:
user = create_my_special_user()
form = MyForm(user=user)
self.assertTrue(form.is_user_special())
And that worked as I expected. The second feature of my custom form was that it cleaned
the data in a particular way. So I did:
form = MyForm(user=user)
cleaned_data = form.clean()
self.assertEqual(cleaned_data, expected_data)
Instead of passing, the test blew up:
AttributeError: 'MyForm' object has no attribute 'cleaned_data'
Why? Because the clean()
method is called when you provide it with POST data
(normally… there are also other ways to populate forms). If you don’t supply it data,
then cleaned_data
doesn’t get populated. The answer is to provide data to the form:
form = MyForm(user=user, data={})
cleaned_data = form.clean()
self.assertEqual(cleaned_data, expected_data)
And that works as expected. The reminder to myself is to always identify which mode of
the form you are testing, GET or POST.