<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>/devices/pseudo/bitbucket@0,0:pseudo (Posts about programming)</title><link>https://www.jmcpdotcom.com/blog/</link><description></description><atom:link href="https://www.jmcpdotcom.com/blog/categories/programming.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2022 &lt;a href="mailto:blogadmin@jmcpdotcom.com"&gt;jmcp&lt;/a&gt; </copyright><lastBuildDate>Thu, 21 Jul 2022 20:44:10 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>A ramble on tech industry hiring</title><link>https://www.jmcpdotcom.com/blog/posts/2022-04-21-a-ramble-on-tech-industry-hiring/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;There's always discourse (on twitter, reddit, HN etc) about hiring, JDs, interview processes etc. Mostly about how it all sucks (which is true to various degrees).&lt;/p&gt;
&lt;p&gt;My colleague left (_1_ day after his probation ended; I was _unhappy_) and I got
to write his replacement's JD. I've become increasingly aware over the last few
years of the implicit and explicit bias in tech hiring, and vowed that if I was
ever in a position to do something about it, I would.&lt;/p&gt;
&lt;p&gt;Since I've got a BA, not a CS or EE degree, I've always felt like I didn't quite
fit in in the industry. This, despite having 2y of CS and Physics in my BA
where I wound up majoring in Maths and Modern History. For a long time I put myself in with the "non-traditional entry to tech" pool.&lt;/p&gt;
&lt;p&gt;After leaving Oracle in 2019 I talked with AWS and GCP recruiters, who &lt;em&gt;all&lt;/em&gt;
emphasized questions on CS theory, recommended specific study guides for their
interview processes... which I read .. and I still couldn't answer their
questions. They made me feel incompetent so I eventually stopped responding to
them.&lt;/p&gt;
&lt;p&gt;At that point I'd been a successful Solaris kernel engineer for many years. I
learnt good software engineering theory and practice from some of the giants in
the industry. Just as important, perhaps even more so, though, was the _culture_
I imbided. I'll ramble on about that some other time.&lt;/p&gt;
&lt;p&gt;In q3 2019 I had an interview with a different FAANG-like entity. It took seven
hours, and I thought I got out lightly. There were three questions in that time
which got me mad. One was from a ~3y PhD who asked why I hadn't written a Python
generator (had not had a need too). The second was from a former CS prof, who
was &lt;em&gt;disgusted&lt;/em&gt; that I had never implemented a thread stack.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Him: How can you call yourself a software engineer if you've never implemented a thread stack?&lt;/p&gt;
&lt;p&gt;Me: In the Unix kernel space, that's a solved problem. And solved by people who are &lt;em&gt;much&lt;/em&gt; smarter than me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(We then spent the rest of the hour-long session whiteboarding (ugh) stacks with
queues &lt;em&gt;and vice versa&lt;/em&gt;).&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Seriously - I've &lt;em&gt;met and worked with&lt;/em&gt; some of the people who worked on
threading in the Solaris kernel. They are incredibly smart and excellent
engineers. More importantly to me, though, they showed me both how and why the
implementation worked. Never in a "I'm so smart" fashion, but always
matter-of-fact, "I worked it out, I need to know that &lt;em&gt;you can do it too&lt;/em&gt;".&lt;/p&gt;
&lt;p&gt;The third question was from one of the overseas members of the panel, who
after expressing horror that I didn't know how the internals of Apache Spark
worked, demanded that I size - off the cuff - an ElasticSearch-based solution
to a text-processing problem that they had just described to me. Including how
much ram, how many cores, how much disk and network capacity..... I told the
interviewer (and, later, the hiring manager who asked me for feedback on the
whole process) that I didn't think that was a reasonable question under any
circumstance.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Anyway.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;After than FAANG-like entity I wound up at my current employer. I had two
interviews, one with hiring manager and team lead, the other with the rest of
the team. Rather than the torture session I anticipated, these were two very
pleasant discussions. We talked about what they did, what I'd done, got to know
each other. When I met the wider team via zoom on my first day (we were in
lockdown so that was my only day in an office for another 4-5 months) my
Director started the meeting by welcoming me and asking me for a short intro.
I was delighted and relieved to discover that the people I'd interviewed with
were representative of the rest of the org.&lt;/p&gt;
&lt;p&gt;A few months after I started we got some interns via a bootcamp called
&lt;a class="reference external" href="https://www.coderacademy.edu.au"&gt;Coder Academy&lt;/a&gt;, and I was asked to be their onboarding buddy. We had lots of
sessions in their six weeks, talked about anything and everything, and I was
truly delighted when my boss announced on their last day of interning that we'd
hired them as employees.&lt;/p&gt;
&lt;p&gt;These three (two in my team, one in an adjacent team) people brought home to me
just how much I knew, and how much of what they wanted to know I had learnt in
my two years of CS - three decades previously. What's that I hear? The claim
that you _must_ have a degree in order to succeed in this industry? Utter tosh.
Bollocks. A complete lie. These three (and this is my &lt;em&gt;personal&lt;/em&gt; experiences,
I'm not alone here - check twitter!) demonstrate that there is more than one
pathway in to tech, and no singular definition of success.&lt;/p&gt;
&lt;p&gt;Back to job descriptions.&lt;/p&gt;
&lt;p&gt;My and my colleague's JD's were not wordsmithed. They were also chock full of
"must have" and specific tech stack references. I've been in this role for seven
and a half months now, but even in my first week I knew that general principles,
an inquisitive nature and a desire to make this &lt;em&gt;SCALE&lt;/em&gt; were more important than
any particular piece of "do you have this certified piece of knowledge right now".&lt;/p&gt;
&lt;p&gt;I spent maybe two hours re-writing the piece. I asked a very good friend (ht
&lt;a class="reference external" href="https://twitter.com/girlgerms"&gt;@girlgerms&lt;/a&gt;) for help and she gave me an excellent phrase to use, which was
along these lines:&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Your work history should show that you are already skilled in,
&lt;em&gt;or have the ability to quickly become skilled in&lt;/em&gt; these areas&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With this way of expressing our needs, it was a fairly small jump to using the
indirect language of "generic technology space, we're using (X)" to round out
the requirements:&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;Data Pipeline Technologies such as (but not limited to):&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Apache Kafka, including Confluent or Aiven’s managed offerings&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relational Databases and SQL. We primarily use PostgreSQL and MS SQL&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Containerisation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/blockquote&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In my part of the company we're gearing up for a hiring spree, and my group
(I've been reorg'd into another team) needs two "data devs", one Java dev, a
business analyst and a product manager. Several other groups within our org are
also down at least one, sometimes two developers - so there's a lot of activity
right now around writing job descriptions. In the first two weeks of April I
spent several hours with my new manager working on wordsmithing our
requirements, putting a lot of effort into removing language that we believe
actively discourages people who do not look like us (we fit the stereotype) from
applying for these roles.&lt;/p&gt;
&lt;p&gt;Just before Easter the manager of another team within our org sent an
operations-focused JD around for comment, and it was ... ok. Ok in that it asked
for specific tech and a degree, but that was about it. This JD fit the
stereotype, and it was meh in the 90s, it's definitely not good
enough now, 20+ years later.&lt;/p&gt;
&lt;p&gt;I suggested he change some of the language, focus on principles rather than
specific tech, and entirely remove mention of requiring a degree. Yesterday
afternoon he pinged wanting a bit of clarification so I went into more detail,
and I am delighted that his second draft (again, sent to our Director and our
peers) did just that. There's still some wordsmithing required, but overall it's
a much better document.&lt;/p&gt;
&lt;p&gt;Yesterday morning I had a chat with our org's Recruiting Partner about my former
colleague's replacement. He clued me in on a few of the vagaries of recruiting,
which was really helpful, and then we talked about interviewing. He was relieved
to hear that I flat out refuse to do whiteboarding, live coding or leetcode -
for myself, and for candidates. He also asked me why any candidate would want to
work with me on my team. I admitted that I hadn't thought of an answer for that
question - but I came up with something that I think is an ok platform to build
a better answer on. By the time I get to interview any candidate I'll have
worked on it a lot more :).&lt;/p&gt;
&lt;p&gt;That's enough rambling and self-aggrandizement. Time to wrap this up.&lt;/p&gt;
&lt;p&gt;I'm now in a position where I get to directly and formally influence how my
company presents itself to potential employees. I do not believe that there is a
candidate "pipeline problem" - there are &lt;em&gt;plenty&lt;/em&gt; of candidates out there who
could fill any of the roles that we have, _we_ have to make ourselves attractive
so that they'll consider us in the first place. _How_ we do that starts with
what we write, and for too long (waay too long) our industry has been focused on
some specific hiring language and practices which are both implicitly and
explicity exclusive. I want that to change, so I'm making changes.&lt;/p&gt;</description><category>Databases</category><category>diversity</category><category>inclusion</category><category>interviews</category><category>language</category><category>programming</category><category>qualifications</category><category>software engineering</category><category>words</category><category>wordsmithing</category><guid>https://www.jmcpdotcom.com/blog/posts/2022-04-21-a-ramble-on-tech-industry-hiring/</guid><pubDate>Wed, 20 Apr 2022 12:00:00 GMT</pubDate></item><item><title>Interview questions - A Complaint</title><link>https://www.jmcpdotcom.com/blog/posts/2021-07-17-interview-questions-a-complaint/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;A few days ago &lt;a class="reference external" href="https://twitter.com/jamescmcpherson/status/1415179153641410562"&gt;I tweeted&lt;/a&gt;&lt;/p&gt;
&lt;img alt="/images/2021/my-original-tweet.png" src="https://www.jmcpdotcom.com/blog/images/2021/my-original-tweet.png"&gt;
&lt;p&gt;Alt text:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Somebody asked (last ~week) something similar to "what questions do you
always ask in an #software #engineering #interview?"&lt;/p&gt;
&lt;p&gt;The response I saw (but failed to hit like on for bookmarking purposes)
was "You hit X on the keyboard. What happens next?"&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I was annoyed by this response for several reasons. So annoyed, in fact, that
while we were away on a long-planned family holiday I lay awake one night
thinking of the many ways which one could respond.&lt;/p&gt;
&lt;p&gt;The "&lt;em&gt;always&lt;/em&gt; ask" really got to me. Why is that particular question something
that the interviewer always asks? Is it appropriate for every role? At what
level and in how much detail do they expect the candidate to respond?&lt;/p&gt;
&lt;p&gt;Thinking back to my very first encounter with a computer &lt;em&gt;at all&lt;/em&gt;, it was an
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Apple_IIe"&gt;Apple //e&lt;/a&gt; at my primary school, early 1980s. You know the one, its &lt;a class="reference external" href="https://web.archive.org/web/20091030162344/http://apple2history.org/museum/books_manuals/a2refman.html"&gt;user
reference manual&lt;/a&gt; came with the complete circuit schematics &lt;em&gt;and AppleBasic&lt;/em&gt;
listing. I don't recall exactly how Apple handled the keyboard interface, but
I'll punt and suggest that everything was hardwired....&lt;/p&gt;
&lt;p&gt;And it was. I found the &lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/"&gt;Apple //e Hardware Schematics&lt;/a&gt;! If you piece together
&lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Page%204.JPG"&gt;page 4&lt;/a&gt;, &lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Page%202.JPG"&gt;page 2&lt;/a&gt; and the &lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Keyboard.jpg"&gt;keyboard circuit schematic&lt;/a&gt; -- J17 is where the
keyboard ribbon cable connects to the motherboard, and the circuit schematic
shows very clearly that the keyboard is a row+column lookup. Very hard-wired.&lt;/p&gt;
&lt;img alt="/images/2021/apple-iie-actual-circuit-board-detail.png" src="https://www.jmcpdotcom.com/blog/images/2021/apple-iie-actual-circuit-board-detail.png"&gt;
&lt;img alt="/images/2021/apple-iie-keyboard-schematic-detail.png" src="https://www.jmcpdotcom.com/blog/images/2021/apple-iie-keyboard-schematic-detail.png"&gt;
&lt;p&gt;From the &lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Keyboard.jpg"&gt;keyboard circuit schematic&lt;/a&gt; we see that key 'X' is on (20, 4) which
maps to Y2 (pin 19 in UE14) and X3 (pin 31). The markings on UE14 are for the
GI (General Instruments) &lt;em&gt;AY-5-3600-PRO&lt;/em&gt;, which is a Keyboard Encoder.&lt;/p&gt;
&lt;p&gt;From UE14 we go through UE12, the keyboard rom looks up the specific rendering
for 'X', pushes that onto the main data bus and sends an interrupt to the 6502b
cpu (see &lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Page%201.JPG"&gt;page 1 schematic&lt;/a&gt;). Then the relevant display function in ROM is
invoked to push the actual "how to render this character" instructions into
the video (NTSC or PAL) controller chip and thence to the physical display
hardware.&lt;/p&gt;
&lt;p&gt;By the way, you might be wondering what UE14 actually means. The 'U' means
that the component is an integrated circuit, the E14 is an actual row+column
lookup so you know where to look for it on the physical board.&lt;/p&gt;
&lt;img alt="/images/2021/apple-iie-row-column-layout.png" src="https://www.jmcpdotcom.com/blog/images/2021/apple-iie-row-column-layout.png"&gt;
&lt;p&gt;It's somewhat obscured, but on the left of the pcb you can see a D and an E,
while along the bottom you can see the numbers 1-15. This is a different pcb
to what's in the schematics, because you can see that the 6502 is at E1 (so it
would be UE1 on the schematic) while in the schematics I've found (see
&lt;a class="reference external" href="https://downloads.reactivemicro.com/Apple%20II%20Items/Hardware/IIe/Schematic/Apple%20IIe%20Schematic%20-%20Page%201.JPG"&gt;page 1&lt;/a&gt;) it's UC4.&lt;/p&gt;
&lt;p&gt;Later, there were PCs with keyboards connected by a cable, and as it happens,
there was a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Intel_MCS-48"&gt;Intel 8042&lt;/a&gt; inside as the microcontroller - which did pretty
much the same thing as the entire &lt;cite&gt;Apple //e&lt;/cite&gt;, except that the interrupt is
generated by the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Intel_MCS-48"&gt;Intel 8042&lt;/a&gt; and sent out along the cable to the keyboard
port on the main motherboard.... where again, an interrupt was generated and
all the other lookups occurred prior to sending out to the display.&lt;/p&gt;
&lt;p&gt;That's all well and good, but how about a more complicated system, like one
of the many UNIXes such as Solaris. Or how about a minicomputer with hardwired
terminals like a PDP-11? One of those hardwired terminals essentially used
the same principles described above, but had a much more complicated kernel to
push the data into. They also had local display buffer storage, so that wasn't
something the kernel needed to worry about. What the kernel was interested in
(please don't anthropomorphise computers, they hate it) was sending the
keystrokes to the correct process.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>challenges</category><category>programming</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2021-07-17-interview-questions-a-complaint/</guid><pubDate>Sat, 17 Jul 2021 03:00:00 GMT</pubDate></item><item><title>Some Pythonic Kafka stuff</title><link>https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;I've been actively interviewing over the last few months, and was recently talking with a cloud streaming
provider about a Staff Engineer SRE role specializing in &lt;a class="reference external" href="https://kafka.apache.org"&gt;Apache Kafka&lt;/a&gt;. I've been doing a lot of work
in that space for $employer and quite like it as a data streaming solution.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The interviews went well, and they sent me a do-at-home challenge with a one week timeout.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The gist of it was I had to create a &lt;a class="reference external" href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt; app which allowed the user to enter a URL to monitor for status
on a given frequency, use &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; to write a &lt;a class="reference external" href="https://kafka.apache.org/documentation/#producerapi"&gt;Kafka Producer&lt;/a&gt; to publish this data to a topic, and
write a &lt;a class="reference external" href="https://kafka.apache.org/documentation/#consumerapi"&gt;Kafka Consumer&lt;/a&gt; to read from the topic and insert into a &lt;a class="reference external" href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt; database.&lt;/p&gt;
&lt;p&gt;Fun!&lt;/p&gt;
&lt;p&gt;I did a brief investigation into using threads within a &lt;a class="reference external" href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt; app for the monitoring code, but quickly
decided that a better architecture would be to do the monitoring via a separate daemon. Separation of
concerns to allow for easier maintenance. Suddenly I'm all about ongoing maintenance rather then how
quickly I can deliver a new feature... Hmmm.&lt;/p&gt;
&lt;p&gt;The next step was to sketch out the table schema I wanted in the database:&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;pre class="code sql"&gt;&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-1" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urltargets_seq&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-2" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-2"&gt;&lt;/a&gt;            &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-3" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-3"&gt;&lt;/a&gt;            &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-4" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-4"&gt;&lt;/a&gt;            &lt;span class="k"&gt;MAXVALUE&lt;/span&gt; &lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-5" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-5"&gt;&lt;/a&gt;            &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-6" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-6"&gt;&lt;/a&gt;            &lt;span class="k"&gt;CACHE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-7" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-7"&gt;&lt;/a&gt;            &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;CYCLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-8" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-9" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor_results_seq&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-10" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-10"&gt;&lt;/a&gt;            &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-11" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-11"&gt;&lt;/a&gt;            &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-12" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-12"&gt;&lt;/a&gt;            &lt;span class="k"&gt;MAXVALUE&lt;/span&gt; &lt;span class="mi"&gt;9223372036854775807&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-13" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-13"&gt;&lt;/a&gt;            &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-14" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-14"&gt;&lt;/a&gt;            &lt;span class="k"&gt;CACHE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-15" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-15"&gt;&lt;/a&gt;            &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;CYCLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-16" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-16"&gt;&lt;/a&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-17" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-17"&gt;&lt;/a&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;urltargets&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-18" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-18"&gt;&lt;/a&gt;            &lt;span class="n"&gt;urltargets_pk&lt;/span&gt;           &lt;span class="n"&gt;int4&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'urltargets_seq'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-19" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-19"&gt;&lt;/a&gt;            &lt;span class="n"&gt;urltarget&lt;/span&gt;                       &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-20" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-20"&gt;&lt;/a&gt;            &lt;span class="n"&gt;monitor_frequency&lt;/span&gt;       &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;monitor_frequency&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-21" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-21"&gt;&lt;/a&gt;            &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;urltargets_pkey&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urltargets_pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-22" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-22"&gt;&lt;/a&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-23" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-23"&gt;&lt;/a&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-24" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-24"&gt;&lt;/a&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;monitor_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-25" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-25"&gt;&lt;/a&gt;            &lt;span class="n"&gt;monitor_results_pk&lt;/span&gt;      &lt;span class="n"&gt;int4&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'monitor_results_seq'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-26" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-26"&gt;&lt;/a&gt;            &lt;span class="n"&gt;http_status&lt;/span&gt;                     &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-27" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-27"&gt;&lt;/a&gt;            &lt;span class="n"&gt;start_time&lt;/span&gt;                      &lt;span class="k"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-28" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-28"&gt;&lt;/a&gt;            &lt;span class="n"&gt;duration&lt;/span&gt;            &lt;span class="n"&gt;int4&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-29" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-29"&gt;&lt;/a&gt;            &lt;span class="n"&gt;urltarget_fk&lt;/span&gt;            &lt;span class="n"&gt;int4&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-30" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-30"&gt;&lt;/a&gt;            &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;monitor_results_fk_fkey&lt;/span&gt; &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urltarget_fk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;urltargets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;urltargets_pk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_1f6d4d21b7e649b8b7002355d12febd9-31" name="rest_code_1f6d4d21b7e649b8b7002355d12febd9-31"&gt;&lt;/a&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Having decided that I would offer monitoring frequencies of 1, 2, 5, 10, 20 and 30 minutes, I created views
for the &lt;a class="reference external" href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt; app to use as well, rather than direct queries. They all look like this, with other values
substituted in as you would expect.&lt;/p&gt;
&lt;pre class="code sql"&gt;&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-1" name="rest_code_41e75a1f12df42c481b715277890a5d1-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;view_1m&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-2" name="rest_code_41e75a1f12df42c481b715277890a5d1-2"&gt;&lt;/a&gt;            &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;mr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor_results_pk&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-3" name="rest_code_41e75a1f12df42c481b715277890a5d1-3"&gt;&lt;/a&gt;                    &lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;ut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urltarget&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-4" name="rest_code_41e75a1f12df42c481b715277890a5d1-4"&gt;&lt;/a&gt;                    &lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;mr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_status&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-5" name="rest_code_41e75a1f12df42c481b715277890a5d1-5"&gt;&lt;/a&gt;                    &lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;mr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-6" name="rest_code_41e75a1f12df42c481b715277890a5d1-6"&gt;&lt;/a&gt;                    &lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="n"&gt;mr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-7" name="rest_code_41e75a1f12df42c481b715277890a5d1-7"&gt;&lt;/a&gt;            &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;monitor_results&lt;/span&gt; &lt;span class="n"&gt;mr&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-8" name="rest_code_41e75a1f12df42c481b715277890a5d1-8"&gt;&lt;/a&gt;            &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;urltargets&lt;/span&gt; &lt;span class="n"&gt;ut&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;ut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urltargets_pk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urltarget_fk&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-9" name="rest_code_41e75a1f12df42c481b715277890a5d1-9"&gt;&lt;/a&gt;            &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;monitor_frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_41e75a1f12df42c481b715277890a5d1-10" name="rest_code_41e75a1f12df42c481b715277890a5d1-10"&gt;&lt;/a&gt;    &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Since I really like seeing schemata visualised, I created a nice(ish) ERD as well:&lt;/p&gt;
&lt;img alt="/images/2021/DB-ERD.png" src="https://www.jmcpdotcom.com/blog/images/2021/DB-ERD.png"&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Well that was straight forward, how about the application and daemon?&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I split out the setup functionality into a separate file importable by the &lt;a class="reference external" href="https://flask.palletsprojects.com"&gt;Flask&lt;/a&gt; app,
the monitor daemon and the consumer. This contained the database connection, &lt;a class="reference external" href="https://kafka.apache.org/documentation/#producerapi"&gt;Kafka Producer&lt;/a&gt;
and &lt;a class="reference external" href="https://kafka.apache.org/documentation/#consumerapi"&gt;Kafka Consumer&lt;/a&gt; code. There's an interesting little niggle in the &lt;a class="reference external" href="https://kafka.apache.org/documentation/#producerapi"&gt;Kafka Producer&lt;/a&gt;
setup which is not immediately obvious and required a bit of digging in &lt;a class="reference external" href="https://stackoverflow.com"&gt;StackOverflow&lt;/a&gt;
as well as enabling debug output with &lt;a class="reference external" href="https://github.com/edenhill/librdkafka/"&gt;librdkafka&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;table class="codetable"&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-1"&gt;&lt;code data-line-number=" 1"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-1" name="rest_code_2850825581f447efa60afe7274348cc8-1"&gt;&lt;/a&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_kafka_configuration&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-2"&gt;&lt;code data-line-number=" 2"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-2" name="rest_code_2850825581f447efa60afe7274348cc8-2"&gt;&lt;/a&gt;      &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-3"&gt;&lt;code data-line-number=" 3"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-3" name="rest_code_2850825581f447efa60afe7274348cc8-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Common function to retrieve the Kafka configuration.&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-4"&gt;&lt;code data-line-number=" 4"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-4" name="rest_code_2850825581f447efa60afe7274348cc8-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      """&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-5"&gt;&lt;code data-line-number=" 5"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-5" name="rest_code_2850825581f447efa60afe7274348cc8-5"&gt;&lt;/a&gt;      &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-6"&gt;&lt;code data-line-number=" 6"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-6" name="rest_code_2850825581f447efa60afe7274348cc8-6"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-7"&gt;&lt;code data-line-number=" 7"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-7" name="rest_code_2850825581f447efa60afe7274348cc8-7"&gt;&lt;/a&gt;      &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-8"&gt;&lt;code data-line-number=" 8"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-8" name="rest_code_2850825581f447efa60afe7274348cc8-8"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"bootstrap.servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"kafka"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"broker"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-9"&gt;&lt;code data-line-number=" 9"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-9" name="rest_code_2850825581f447efa60afe7274348cc8-9"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"group.id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"website-monitor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-10"&gt;&lt;code data-line-number="10"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-10" name="rest_code_2850825581f447efa60afe7274348cc8-10"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"ssl.key.location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"kafka"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"keyfile"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-11"&gt;&lt;code data-line-number="11"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-11" name="rest_code_2850825581f447efa60afe7274348cc8-11"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"ssl.certificate.location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"kafka"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"certfile"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-12"&gt;&lt;code data-line-number="12"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-12" name="rest_code_2850825581f447efa60afe7274348cc8-12"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"ssl.ca.location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;appconfig&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"kafka"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"cafile"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-13"&gt;&lt;code data-line-number="13"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-13" name="rest_code_2850825581f447efa60afe7274348cc8-13"&gt;&lt;/a&gt;          &lt;span class="s2"&gt;"security.protocol"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"SSL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-14"&gt;&lt;code data-line-number="14"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-14" name="rest_code_2850825581f447efa60afe7274348cc8-14"&gt;&lt;/a&gt;          &lt;span class="c1"&gt;# "debug": "eos, broker, admin",  # re-enable if needed&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-15"&gt;&lt;code data-line-number="15"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-15" name="rest_code_2850825581f447efa60afe7274348cc8-15"&gt;&lt;/a&gt;          &lt;span class="s1"&gt;'transaction.timeout.ms'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-16"&gt;&lt;code data-line-number="16"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-16" name="rest_code_2850825581f447efa60afe7274348cc8-16"&gt;&lt;/a&gt;          &lt;span class="s1"&gt;'enable.idempotence'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-17"&gt;&lt;code data-line-number="17"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-17" name="rest_code_2850825581f447efa60afe7274348cc8-17"&gt;&lt;/a&gt;      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-18"&gt;&lt;code data-line-number="18"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-18" name="rest_code_2850825581f447efa60afe7274348cc8-18"&gt;&lt;/a&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-19"&gt;&lt;code data-line-number="19"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-19" name="rest_code_2850825581f447efa60afe7274348cc8-19"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-20"&gt;&lt;code data-line-number="20"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-20" name="rest_code_2850825581f447efa60afe7274348cc8-20"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-21"&gt;&lt;code data-line-number="21"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-21" name="rest_code_2850825581f447efa60afe7274348cc8-21"&gt;&lt;/a&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setup_kafka_producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-22"&gt;&lt;code data-line-number="22"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-22" name="rest_code_2850825581f447efa60afe7274348cc8-22"&gt;&lt;/a&gt;      &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-23"&gt;&lt;code data-line-number="23"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-23" name="rest_code_2850825581f447efa60afe7274348cc8-23"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Creates the connection to our Kafka brokers so we can publish&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-24"&gt;&lt;code data-line-number="24"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-24" name="rest_code_2850825581f447efa60afe7274348cc8-24"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      messages to the topics we want. We take the {view} argument so&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-25"&gt;&lt;code data-line-number="25"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-25" name="rest_code_2850825581f447efa60afe7274348cc8-25"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      that we can avoid blatting multiple producers together and getting&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-26"&gt;&lt;code data-line-number="26"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-26" name="rest_code_2850825581f447efa60afe7274348cc8-26"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      errors from the broker about zombies and fencing. See&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-27"&gt;&lt;code data-line-number="27"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-27" name="rest_code_2850825581f447efa60afe7274348cc8-27"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      https://stackoverflow.com/questions/50335227/how-to-pick-a-kafka-transaction-id&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-28"&gt;&lt;code data-line-number="28"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-28" name="rest_code_2850825581f447efa60afe7274348cc8-28"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      for more details&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-29"&gt;&lt;code data-line-number="29"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-29" name="rest_code_2850825581f447efa60afe7274348cc8-29"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-30"&gt;&lt;code data-line-number="30"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-30" name="rest_code_2850825581f447efa60afe7274348cc8-30"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Return: a Producer&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-31"&gt;&lt;code data-line-number="31"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-31" name="rest_code_2850825581f447efa60afe7274348cc8-31"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      """&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-32"&gt;&lt;code data-line-number="32"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-32" name="rest_code_2850825581f447efa60afe7274348cc8-32"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-33"&gt;&lt;code data-line-number="33"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-33" name="rest_code_2850825581f447efa60afe7274348cc8-33"&gt;&lt;/a&gt;      &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_get_kafka_configuration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-34"&gt;&lt;code data-line-number="34"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-34" name="rest_code_2850825581f447efa60afe7274348cc8-34"&gt;&lt;/a&gt;      &lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"transactional.id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"website-monitor"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-35"&gt;&lt;code data-line-number="35"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-35" name="rest_code_2850825581f447efa60afe7274348cc8-35"&gt;&lt;/a&gt;      &lt;span class="n"&gt;kafkaProducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-36"&gt;&lt;code data-line-number="36"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-36" name="rest_code_2850825581f447efa60afe7274348cc8-36"&gt;&lt;/a&gt;      &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-37"&gt;&lt;code data-line-number="37"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-37" name="rest_code_2850825581f447efa60afe7274348cc8-37"&gt;&lt;/a&gt;          &lt;span class="n"&gt;kafkaProducer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init_transactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-38"&gt;&lt;code data-line-number="38"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-38" name="rest_code_2850825581f447efa60afe7274348cc8-38"&gt;&lt;/a&gt;      &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;KafkaError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-39"&gt;&lt;code data-line-number="39"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-39" name="rest_code_2850825581f447efa60afe7274348cc8-39"&gt;&lt;/a&gt;          &lt;span class="c1"&gt;# If we can't do this, then we have to quit&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-40"&gt;&lt;code data-line-number="40"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-40" name="rest_code_2850825581f447efa60afe7274348cc8-40"&gt;&lt;/a&gt;          &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"""Producer failed to init_transactions(), throwing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ke&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-41"&gt;&lt;code data-line-number="41"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-41" name="rest_code_2850825581f447efa60afe7274348cc8-41"&gt;&lt;/a&gt;          &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-42"&gt;&lt;code data-line-number="42"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-42" name="rest_code_2850825581f447efa60afe7274348cc8-42"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2850825581f447efa60afe7274348cc8-43"&gt;&lt;code data-line-number="43"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2850825581f447efa60afe7274348cc8-43" name="rest_code_2850825581f447efa60afe7274348cc8-43"&gt;&lt;/a&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;kafkaProducer&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;When I was working with the daemon, my first iteration tried opening the DB connection
and Producer in each thread's (one for each frequency) &lt;code class="docutils literal"&gt;__init__()&lt;/code&gt; function, and .... that didn't work.&lt;/p&gt;
&lt;p&gt;The DB connection is not &lt;a class="reference external" href="https://docs.python.org/3/library/pickle.html"&gt;picklable&lt;/a&gt;, so does _not_ survive the call to &lt;code class="docutils literal"&gt;os.fork()&lt;/code&gt;. Once I had rewritten the
setup and run methods to get the DB connection, that part was groovy.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://kafka.apache.org/documentation/#producerapi"&gt;Kafka Producer&lt;/a&gt; still required a bit of work. After reading through stackoverflow and the upstream for
&lt;a class="reference external" href="https://github.com/edenhill/librdkafka/"&gt;librdkafka&lt;/a&gt;, I saw that I needed to similarly delay initialising the producer until the thread's
&lt;code class="docutils literal"&gt;run()&lt;/code&gt; method was called. I also observed that each Producer should also initialise the transaction feature,
but leave the &lt;code class="docutils literal"&gt;begin&lt;/code&gt;... &lt;code class="docutils literal"&gt;end&lt;/code&gt; of the transaction to when it was called to publish a message.&lt;/p&gt;
&lt;p&gt;I still had a problem, though - &lt;em&gt;some&lt;/em&gt; transactions would get through, but then the Producer would be
&lt;strong&gt;fenced&lt;/strong&gt;. This was the &lt;em&gt;niggle&lt;/em&gt;, and where the &lt;a class="reference external" href="https://stackoverflow.com"&gt;StackOverflow&lt;/a&gt; comments helped me out:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Finally, in distributed environments, applications will crash or —worse!— temporarily
lose connectivity to the rest of the system. Typically, new instances are automatically
started to replace the ones which were deemed lost. Through this process, we may have
multiple instances processing the same input topics and writing to the same output
topics, causing duplicate outputs and violating the exactly once processing semantics.&lt;/p&gt;
&lt;p&gt;We call this the problem of “zombie instances.” [emphasis added]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I realised that I was giving the same transactional id to each of the six producer instances (setting
the 'transactional.id' in the configuration dict generated by &lt;code class="docutils literal"&gt;_get_kafka_configuration()&lt;/code&gt;, so I needed
to uniqify them somehow. I decided to pass the monitoring frequency of the thread to the setup function,
and ... booyah, I had messages being published.&lt;/p&gt;
&lt;p&gt;That was a really nice feeling.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;There is one other aspect of the monitoring daemon that I need to mention. Since each thread reads its
list of URLs to monitor each time it wakes, I wanted to parallelize this effort. Monitoring each of
the URLs in series could easily take too long from a &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;sleep(...)&lt;/span&gt;&lt;/code&gt; point of view, and I &lt;em&gt;really&lt;/em&gt; did not
want to just fork a whole heap of processes and thread either - avoiding the potential for a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Fork_bomb"&gt;fork-bomb&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To work around this I used the &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; standard library &lt;a class="reference external" href="https://docs.python.org/3/library/concurrent.futures.html"&gt;concurrent.futures&lt;/a&gt; with a ThreadPoolExecutor
for each target URL. Adding attributes to the &lt;code class="docutils literal"&gt;future&lt;/code&gt; object enabled me to use an &lt;code class="docutils literal"&gt;add_done_callback&lt;/code&gt;
so that when the future crystallized it would then publish the message.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;&lt;table class="codetable"&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-1"&gt;&lt;code data-line-number=" 1"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-1" name="rest_code_2d5e58a399814a09a408127e5643cfde-1"&gt;&lt;/a&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-2"&gt;&lt;code data-line-number=" 2"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-2" name="rest_code_2d5e58a399814a09a408127e5643cfde-2"&gt;&lt;/a&gt;      &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-3"&gt;&lt;code data-line-number=" 3"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-3" name="rest_code_2d5e58a399814a09a408127e5643cfde-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Runs the monitor, updates account-keeping and kicks off&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-4"&gt;&lt;code data-line-number=" 4"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-4" name="rest_code_2d5e58a399814a09a408127e5643cfde-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      notifications if required. Then back to sleep.&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-5"&gt;&lt;code data-line-number=" 5"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-5" name="rest_code_2d5e58a399814a09a408127e5643cfde-5"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      """&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-6"&gt;&lt;code data-line-number=" 6"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-6" name="rest_code_2d5e58a399814a09a408127e5643cfde-6"&gt;&lt;/a&gt;      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_producer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setup_kafka_producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-7"&gt;&lt;code data-line-number=" 7"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-7" name="rest_code_2d5e58a399814a09a408127e5643cfde-7"&gt;&lt;/a&gt;      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setup_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-8"&gt;&lt;code data-line-number=" 8"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-8" name="rest_code_2d5e58a399814a09a408127e5643cfde-8"&gt;&lt;/a&gt;      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-9"&gt;&lt;code data-line-number=" 9"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-9" name="rest_code_2d5e58a399814a09a408127e5643cfde-9"&gt;&lt;/a&gt;      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-10"&gt;&lt;code data-line-number="10"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-10" name="rest_code_2d5e58a399814a09a408127e5643cfde-10"&gt;&lt;/a&gt;          &lt;span class="n"&gt;alltargets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_targets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-11"&gt;&lt;code data-line-number="11"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-11" name="rest_code_2d5e58a399814a09a408127e5643cfde-11"&gt;&lt;/a&gt;          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;alltargets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-12"&gt;&lt;code data-line-number="12"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-12" name="rest_code_2d5e58a399814a09a408127e5643cfde-12"&gt;&lt;/a&gt;              &lt;span class="c1"&gt;# We use a 'with' statement to ensure threads in the pool&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-13"&gt;&lt;code data-line-number="13"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-13" name="rest_code_2d5e58a399814a09a408127e5643cfde-13"&gt;&lt;/a&gt;              &lt;span class="c1"&gt;# are cleaned up promptly&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-14"&gt;&lt;code data-line-number="14"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-14" name="rest_code_2d5e58a399814a09a408127e5643cfde-14"&gt;&lt;/a&gt;              &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;cf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-15"&gt;&lt;code data-line-number="15"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-15" name="rest_code_2d5e58a399814a09a408127e5643cfde-15"&gt;&lt;/a&gt;                  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tgt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;alltargets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-16"&gt;&lt;code data-line-number="16"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-16" name="rest_code_2d5e58a399814a09a408127e5643cfde-16"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;check_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-17"&gt;&lt;code data-line-number="17"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-17" name="rest_code_2d5e58a399814a09a408127e5643cfde-17"&gt;&lt;/a&gt;                                               &lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-18"&gt;&lt;code data-line-number="18"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-18" name="rest_code_2d5e58a399814a09a408127e5643cfde-18"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;tgt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-19"&gt;&lt;code data-line-number="19"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-19" name="rest_code_2d5e58a399814a09a408127e5643cfde-19"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_producer&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-20"&gt;&lt;code data-line-number="20"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-20" name="rest_code_2d5e58a399814a09a408127e5643cfde-20"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_done_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;construct_and_publish&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-21"&gt;&lt;code data-line-number="21"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-21" name="rest_code_2d5e58a399814a09a408127e5643cfde-21"&gt;&lt;/a&gt;                      &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_futures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-22"&gt;&lt;code data-line-number="22"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-22" name="rest_code_2d5e58a399814a09a408127e5643cfde-22"&gt;&lt;/a&gt;                  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;as_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_futures&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-23"&gt;&lt;code data-line-number="23"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-23" name="rest_code_2d5e58a399814a09a408127e5643cfde-23"&gt;&lt;/a&gt;                      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;done&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-24"&gt;&lt;code data-line-number="24"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-24" name="rest_code_2d5e58a399814a09a408127e5643cfde-24"&gt;&lt;/a&gt;                          &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_futures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-25"&gt;&lt;code data-line-number="25"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-25" name="rest_code_2d5e58a399814a09a408127e5643cfde-25"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_2d5e58a399814a09a408127e5643cfde-26"&gt;&lt;code data-line-number="26"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_2d5e58a399814a09a408127e5643cfde-26" name="rest_code_2d5e58a399814a09a408127e5643cfde-26"&gt;&lt;/a&gt;          &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sleeptime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The check and publish methods are outside of the thread definition:&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;&lt;table class="codetable"&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-1"&gt;&lt;code data-line-number=" 1"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-1" name="rest_code_03c5c44fafec4c178433057abbcd8db4-1"&gt;&lt;/a&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;construct_and_publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-2"&gt;&lt;code data-line-number=" 2"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-2" name="rest_code_03c5c44fafec4c178433057abbcd8db4-2"&gt;&lt;/a&gt;      &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-3"&gt;&lt;code data-line-number=" 3"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-3" name="rest_code_03c5c44fafec4c178433057abbcd8db4-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Callback function for the concurrent.future that each thread&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-4"&gt;&lt;code data-line-number=" 4"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-4" name="rest_code_03c5c44fafec4c178433057abbcd8db4-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      makes use of to query a website. Turns the future's attributes&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-5"&gt;&lt;code data-line-number=" 5"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-5" name="rest_code_03c5c44fafec4c178433057abbcd8db4-5"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      into a message for 'url-to-monitor' topic, then publishes that&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-6"&gt;&lt;code data-line-number=" 6"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-6" name="rest_code_03c5c44fafec4c178433057abbcd8db4-6"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      message to the topic.&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-7"&gt;&lt;code data-line-number=" 7"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-7" name="rest_code_03c5c44fafec4c178433057abbcd8db4-7"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      """&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-8"&gt;&lt;code data-line-number=" 8"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-8" name="rest_code_03c5c44fafec4c178433057abbcd8db4-8"&gt;&lt;/a&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cancelled&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-9"&gt;&lt;code data-line-number=" 9"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-9" name="rest_code_03c5c44fafec4c178433057abbcd8db4-9"&gt;&lt;/a&gt;          &lt;span class="n"&gt;errmsg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""Monitor attempt for &lt;/span&gt;&lt;span class="si"&gt;{args}&lt;/span&gt;&lt;span class="s2"&gt; failed"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-10"&gt;&lt;code data-line-number="10"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-10" name="rest_code_03c5c44fafec4c178433057abbcd8db4-10"&gt;&lt;/a&gt;          &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errmsg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-11"&gt;&lt;code data-line-number="11"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-11" name="rest_code_03c5c44fafec4c178433057abbcd8db4-11"&gt;&lt;/a&gt;                              &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-12"&gt;&lt;code data-line-number="12"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-12" name="rest_code_03c5c44fafec4c178433057abbcd8db4-12"&gt;&lt;/a&gt;      &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-13"&gt;&lt;code data-line-number="13"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-13" name="rest_code_03c5c44fafec4c178433057abbcd8db4-13"&gt;&lt;/a&gt;          &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msgFields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;())))&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-14"&gt;&lt;code data-line-number="14"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-14" name="rest_code_03c5c44fafec4c178433057abbcd8db4-14"&gt;&lt;/a&gt;          &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin_transaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-15"&gt;&lt;code data-line-number="15"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-15" name="rest_code_03c5c44fafec4c178433057abbcd8db4-15"&gt;&lt;/a&gt;          &lt;span class="n"&gt;publish_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-16"&gt;&lt;code data-line-number="16"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-16" name="rest_code_03c5c44fafec4c178433057abbcd8db4-16"&gt;&lt;/a&gt;                          &lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"url-monitor-results"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-17"&gt;&lt;code data-line-number="17"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-17" name="rest_code_03c5c44fafec4c178433057abbcd8db4-17"&gt;&lt;/a&gt;                          &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'utf-8'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-18"&gt;&lt;code data-line-number="18"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-18" name="rest_code_03c5c44fafec4c178433057abbcd8db4-18"&gt;&lt;/a&gt;          &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit_transaction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-19"&gt;&lt;code data-line-number="19"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-19" name="rest_code_03c5c44fafec4c178433057abbcd8db4-19"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-20"&gt;&lt;code data-line-number="20"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-20" name="rest_code_03c5c44fafec4c178433057abbcd8db4-20"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-21"&gt;&lt;code data-line-number="21"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-21" name="rest_code_03c5c44fafec4c178433057abbcd8db4-21"&gt;&lt;/a&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fk&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-22"&gt;&lt;code data-line-number="22"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-22" name="rest_code_03c5c44fafec4c178433057abbcd8db4-22"&gt;&lt;/a&gt;      &lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-23"&gt;&lt;code data-line-number="23"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-23" name="rest_code_03c5c44fafec4c178433057abbcd8db4-23"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      Performs an 'HTTP GET' of the supplied url and returns a tuple containing&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-24"&gt;&lt;code data-line-number="24"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-24" name="rest_code_03c5c44fafec4c178433057abbcd8db4-24"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      (fk, start_time, duration, http_status).&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-25"&gt;&lt;code data-line-number="25"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-25" name="rest_code_03c5c44fafec4c178433057abbcd8db4-25"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      The start_time is expressed in milliseconds since the UNIX Epoch.&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-26"&gt;&lt;code data-line-number="26"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-26" name="rest_code_03c5c44fafec4c178433057abbcd8db4-26"&gt;&lt;/a&gt;&lt;span class="sd"&gt;      """&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-27"&gt;&lt;code data-line-number="27"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-27" name="rest_code_03c5c44fafec4c178433057abbcd8db4-27"&gt;&lt;/a&gt;      &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-28"&gt;&lt;code data-line-number="28"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-28" name="rest_code_03c5c44fafec4c178433057abbcd8db4-28"&gt;&lt;/a&gt;      &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-29"&gt;&lt;code data-line-number="29"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-29" name="rest_code_03c5c44fafec4c178433057abbcd8db4-29"&gt;&lt;/a&gt;      &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_03c5c44fafec4c178433057abbcd8db4-30"&gt;&lt;code data-line-number="30"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_03c5c44fafec4c178433057abbcd8db4-30" name="rest_code_03c5c44fafec4c178433057abbcd8db4-30"&gt;&lt;/a&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With the monitoring daemon written, I now needed the other end of the pipe - writing the
&lt;a class="reference external" href="https://kafka.apache.org/documentation/#consumerapi"&gt;Kafka Consumer&lt;/a&gt; to read &lt;em&gt;from&lt;/em&gt; the topic and insert into the database. This was straightforward:
we're polling for messages on both configured topics, when we read one we write it to the
appropriate DB table using a prepared statement, &lt;code class="docutils literal"&gt;commit&lt;/code&gt; and then do it all again
with a while loop.&lt;/p&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="code"&gt;&lt;table class="codetable"&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-1"&gt;&lt;code data-line-number=" 1"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-1" name="rest_code_a1d5446b6f9942118f0177235a8e498e-1"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlToMonitorStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"INSERT INTO urltargets (urltarget, monitor_frequency "&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-2"&gt;&lt;code data-line-number=" 2"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-2" name="rest_code_a1d5446b6f9942118f0177235a8e498e-2"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlToMonitorStmt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"VALUES (&lt;/span&gt;&lt;span class="si"&gt;%(urltarget)s&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%(monitor_frequency)s&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-3"&gt;&lt;code data-line-number=" 3"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-3" name="rest_code_a1d5446b6f9942118f0177235a8e498e-3"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-4"&gt;&lt;code data-line-number=" 4"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-4" name="rest_code_a1d5446b6f9942118f0177235a8e498e-4"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlMonitorResultsStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"INSERT INTO monitor_results (http_status, "&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-5"&gt;&lt;code data-line-number=" 5"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-5" name="rest_code_a1d5446b6f9942118f0177235a8e498e-5"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlMonitorResultsStmt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"urltarget_fk, start_time, duration) "&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-6"&gt;&lt;code data-line-number=" 6"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-6" name="rest_code_a1d5446b6f9942118f0177235a8e498e-6"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlMonitorResultsStmt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"VALUES (&lt;/span&gt;&lt;span class="si"&gt;%(http_status)s&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%(targetId)s&lt;/span&gt;&lt;span class="s2"&gt;, "&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-7"&gt;&lt;code data-line-number=" 7"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-7" name="rest_code_a1d5446b6f9942118f0177235a8e498e-7"&gt;&lt;/a&gt;  &lt;span class="n"&gt;urlMonitorResultsStmt&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;"to_timestamp(&lt;/span&gt;&lt;span class="si"&gt;%(start_time)s&lt;/span&gt;&lt;span class="s2"&gt;), &lt;/span&gt;&lt;span class="si"&gt;%(duration)s&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-8"&gt;&lt;code data-line-number=" 8"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-8" name="rest_code_a1d5446b6f9942118f0177235a8e498e-8"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-9"&gt;&lt;code data-line-number=" 9"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-9" name="rest_code_a1d5446b6f9942118f0177235a8e498e-9"&gt;&lt;/a&gt;  &lt;span class="n"&gt;lookups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-10"&gt;&lt;code data-line-number="10"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-10" name="rest_code_a1d5446b6f9942118f0177235a8e498e-10"&gt;&lt;/a&gt;      &lt;span class="s2"&gt;"url-to-monitor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;urlToMonitorStmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-11"&gt;&lt;code data-line-number="11"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-11" name="rest_code_a1d5446b6f9942118f0177235a8e498e-11"&gt;&lt;/a&gt;      &lt;span class="s2"&gt;"url-monitor-results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;urlMonitorResultsStmt&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-12"&gt;&lt;code data-line-number="12"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-12" name="rest_code_a1d5446b6f9942118f0177235a8e498e-12"&gt;&lt;/a&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-13"&gt;&lt;code data-line-number="13"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-13" name="rest_code_a1d5446b6f9942118f0177235a8e498e-13"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-14"&gt;&lt;code data-line-number="14"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-14" name="rest_code_a1d5446b6f9942118f0177235a8e498e-14"&gt;&lt;/a&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-15"&gt;&lt;code data-line-number="15"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-15" name="rest_code_a1d5446b6f9942118f0177235a8e498e-15"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-16"&gt;&lt;code data-line-number="16"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-16" name="rest_code_a1d5446b6f9942118f0177235a8e498e-16"&gt;&lt;/a&gt;      &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setup_kafka_consumer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-17"&gt;&lt;code data-line-number="17"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-17" name="rest_code_a1d5446b6f9942118f0177235a8e498e-17"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-18"&gt;&lt;code data-line-number="18"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-18" name="rest_code_a1d5446b6f9942118f0177235a8e498e-18"&gt;&lt;/a&gt;      &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setup_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-19"&gt;&lt;code data-line-number="19"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-19" name="rest_code_a1d5446b6f9942118f0177235a8e498e-19"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-20"&gt;&lt;code data-line-number="20"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-20" name="rest_code_a1d5446b6f9942118f0177235a8e498e-20"&gt;&lt;/a&gt;      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-21"&gt;&lt;code data-line-number="21"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-21" name="rest_code_a1d5446b6f9942118f0177235a8e498e-21"&gt;&lt;/a&gt;          &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-22"&gt;&lt;code data-line-number="22"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-22" name="rest_code_a1d5446b6f9942118f0177235a8e498e-22"&gt;&lt;/a&gt;              &lt;span class="n"&gt;msglist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-23"&gt;&lt;code data-line-number="23"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-23" name="rest_code_a1d5446b6f9942118f0177235a8e498e-23"&gt;&lt;/a&gt;              &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msglist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-24"&gt;&lt;code data-line-number="24"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-24" name="rest_code_a1d5446b6f9942118f0177235a8e498e-24"&gt;&lt;/a&gt;                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-25"&gt;&lt;code data-line-number="25"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-25" name="rest_code_a1d5446b6f9942118f0177235a8e498e-25"&gt;&lt;/a&gt;                      &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-26"&gt;&lt;code data-line-number="26"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-26" name="rest_code_a1d5446b6f9942118f0177235a8e498e-26"&gt;&lt;/a&gt;                  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-27"&gt;&lt;code data-line-number="27"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-27" name="rest_code_a1d5446b6f9942118f0177235a8e498e-27"&gt;&lt;/a&gt;                      &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Received error during poll: &lt;/span&gt;&lt;span class="si"&gt;{error}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-28"&gt;&lt;code data-line-number="28"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-28" name="rest_code_a1d5446b6f9942118f0177235a8e498e-28"&gt;&lt;/a&gt;                          &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-29"&gt;&lt;code data-line-number="29"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-29" name="rest_code_a1d5446b6f9942118f0177235a8e498e-29"&gt;&lt;/a&gt;                  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-30"&gt;&lt;code data-line-number="30"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-30" name="rest_code_a1d5446b6f9942118f0177235a8e498e-30"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lookups&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-31"&gt;&lt;code data-line-number="31"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-31" name="rest_code_a1d5446b6f9942118f0177235a8e498e-31"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'utf-8'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-32"&gt;&lt;code data-line-number="32"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-32" name="rest_code_a1d5446b6f9942118f0177235a8e498e-32"&gt;&lt;/a&gt;                      &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-33"&gt;&lt;code data-line-number="33"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-33" name="rest_code_a1d5446b6f9942118f0177235a8e498e-33"&gt;&lt;/a&gt;              &lt;span class="n"&gt;curs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"COMMIT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-34"&gt;&lt;code data-line-number="34"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-34" name="rest_code_a1d5446b6f9942118f0177235a8e498e-34"&gt;&lt;/a&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/#rest_code_a1d5446b6f9942118f0177235a8e498e-35"&gt;&lt;code data-line-number="35"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a1d5446b6f9942118f0177235a8e498e-35" name="rest_code_a1d5446b6f9942118f0177235a8e498e-35"&gt;&lt;/a&gt;      &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Of course there should be error handling for the &lt;code class="docutils literal"&gt;execute()&lt;/code&gt;. There should also be packaging
and tests and templates for the report. Do not @ me, etc etc.&lt;/p&gt;
&lt;p&gt;The reason why all these pieces are missing is because the day before I was due to hand this
assignment in to my interviewer, I received a very, very nice offer from another company that
I'd also been interviewing with - and I accepted it.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>challenges</category><category>Kafka</category><category>programming</category><category>Python</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2021-05-10-some-python-kafka-stuff/</guid><pubDate>Mon, 10 May 2021 03:00:00 GMT</pubDate></item><item><title>An unexpected live coding challenge</title><link>https://www.jmcpdotcom.com/blog/posts/2021-05-09-live-coding-challenge/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;A few weeks ago I was in a technical interview, and was asked to do a live coding challenge. I was surprised,
because this is the sort of thing that I expect a recruiter and hiring manager to mention ahead of time. Preparation
is very important, and while I know that there are many people for whom live coding is a thrill, there are plenty of
other people for whom it can be a terrifying experience.&lt;/p&gt;
&lt;p&gt;I'm glad to say that I'm not terrified by it, but it's definitely not an ideal environment for me.&lt;/p&gt;
&lt;p&gt;So after a few minutes of me describing what I've done in my career (it seemed pretty clear that the interviewer
hadn't actually read my resume), and a few technical questions, we got into the challenge.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-1" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-1"&gt;&lt;/a&gt;&lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-2" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-2"&gt;&lt;/a&gt;&lt;span class="sd"&gt;For a given string composed of parenthesis ("(", "{", "["), check if the string is valid parenthesis.&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-3" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;1. "()" -- valid&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-4" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;2. "({})" -- valid&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-5" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-5"&gt;&lt;/a&gt;&lt;span class="sd"&gt;3. "(}{)" -- invalid&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-6" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-6"&gt;&lt;/a&gt;&lt;span class="sd"&gt;4. "{()}[{}]" -- valid&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-7" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-7"&gt;&lt;/a&gt;&lt;span class="sd"&gt;5. "({(}))" -- invalid&lt;/span&gt;
&lt;a id="rest_code_94c4ab25a06341f9af9f82a2930d9494-8" name="rest_code_94c4ab25a06341f9af9f82a2930d9494-8"&gt;&lt;/a&gt;&lt;span class="sd"&gt;"""&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I noted immediately that this is an issue which requires the processing function to track state, because you not
only need to determine open and closed pairings, but also what &lt;em&gt;type&lt;/em&gt; it is.&lt;/p&gt;
&lt;p&gt;It took a minute to sketch out the classifications that I needed, talking through my decision process all the while:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-1" name="rest_code_3a321313d6c24950987b2691da17d836-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;OPENS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"("&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"{"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-2" name="rest_code_3a321313d6c24950987b2691da17d836-2"&gt;&lt;/a&gt;&lt;span class="n"&gt;CLOSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-3" name="rest_code_3a321313d6c24950987b2691da17d836-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-4" name="rest_code_3a321313d6c24950987b2691da17d836-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;braces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"{"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-5" name="rest_code_3a321313d6c24950987b2691da17d836-5"&gt;&lt;/a&gt;&lt;span class="n"&gt;parens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"("&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;")"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-6" name="rest_code_3a321313d6c24950987b2691da17d836-6"&gt;&lt;/a&gt;&lt;span class="n"&gt;brackets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"["&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-7" name="rest_code_3a321313d6c24950987b2691da17d836-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-8" name="rest_code_3a321313d6c24950987b2691da17d836-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"braces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;braces&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-9" name="rest_code_3a321313d6c24950987b2691da17d836-9"&gt;&lt;/a&gt;            &lt;span class="s2"&gt;"parens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-10" name="rest_code_3a321313d6c24950987b2691da17d836-10"&gt;&lt;/a&gt;            &lt;span class="s2"&gt;"brackets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;brackets&lt;/span&gt;
&lt;a id="rest_code_3a321313d6c24950987b2691da17d836-11" name="rest_code_3a321313d6c24950987b2691da17d836-11"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I was able to stub out a check function pretty quickly, but got stuck when I went from the stub to implementation,
because I realised that I needed to keep track of what the previous element in the string was.&lt;/p&gt;
&lt;p&gt;Oh no! How do I do that? (A stack, btw)&lt;/p&gt;
&lt;p&gt;Mental blank :(&lt;/p&gt;
&lt;p&gt;I needed time to jog my memory, so I asked the interviewer to tell me about himself, what he
does on the team and a few other questions.&lt;/p&gt;
&lt;p&gt;This, I think, was a very good decision - with the focus of the interview &lt;em&gt;not&lt;/em&gt; on me, I could step back and
think about what basic data types in &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; I could use to implement a stack.&lt;/p&gt;
&lt;p&gt;The data type I needed is indeed pretty basic: a &lt;strong&gt;list()&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; list() lets you push (the append() operation) and pop so with the addition of another data
structure&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_0b9a9725e60347e6a14edd191169b9ca-1" name="rest_code_0b9a9725e60347e6a14edd191169b9ca-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"braces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_0b9a9725e60347e6a14edd191169b9ca-2" name="rest_code_0b9a9725e60347e6a14edd191169b9ca-2"&gt;&lt;/a&gt;           &lt;span class="s2"&gt;"parens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_0b9a9725e60347e6a14edd191169b9ca-3" name="rest_code_0b9a9725e60347e6a14edd191169b9ca-3"&gt;&lt;/a&gt;           &lt;span class="s2"&gt;"brackets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a id="rest_code_0b9a9725e60347e6a14edd191169b9ca-4" name="rest_code_0b9a9725e60347e6a14edd191169b9ca-4"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;and a short function to return the class of the element&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-1" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-2" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-2"&gt;&lt;/a&gt;    &lt;span class="sd"&gt;""" returns whether 'c' is in brackets, braces or parens """&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-3" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;braces&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-4" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-4"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"braces"&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-5" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-5"&gt;&lt;/a&gt;    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;brackets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-6" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-6"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"brackets"&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-7" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_525b4f4fa7894bc7b819cb0174177f57-8" name="rest_code_525b4f4fa7894bc7b819cb0174177f57-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"parens"&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;we're now in a much better position for the algorithm.&lt;/p&gt;
&lt;p&gt;By this time I had also calmed myself down, because everything came together pretty easily for me.&lt;/p&gt;
&lt;p&gt;With the above code blocks already noted, here is the body of the function:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-1" name="rest_code_942deeba85214e24a7b424686a56f4e6-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-2" name="rest_code_942deeba85214e24a7b424686a56f4e6-2"&gt;&lt;/a&gt;    &lt;span class="sd"&gt;""" For the given examples at top, determine validity.&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-3" name="rest_code_942deeba85214e24a7b424686a56f4e6-3"&gt;&lt;/a&gt;&lt;span class="sd"&gt;        Assumption: the input is _only_ braces, parens and brackets&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-4" name="rest_code_942deeba85214e24a7b424686a56f4e6-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-5" name="rest_code_942deeba85214e24a7b424686a56f4e6-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-6" name="rest_code_942deeba85214e24a7b424686a56f4e6-6"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# start&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-7" name="rest_code_942deeba85214e24a7b424686a56f4e6-7"&gt;&lt;/a&gt;    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-8" name="rest_code_942deeba85214e24a7b424686a56f4e6-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-9" name="rest_code_942deeba85214e24a7b424686a56f4e6-9"&gt;&lt;/a&gt;    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-10" name="rest_code_942deeba85214e24a7b424686a56f4e6-10"&gt;&lt;/a&gt;    &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-11" name="rest_code_942deeba85214e24a7b424686a56f4e6-11"&gt;&lt;/a&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-12" name="rest_code_942deeba85214e24a7b424686a56f4e6-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-13" name="rest_code_942deeba85214e24a7b424686a56f4e6-13"&gt;&lt;/a&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-14" name="rest_code_942deeba85214e24a7b424686a56f4e6-14"&gt;&lt;/a&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]:&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-15" name="rest_code_942deeba85214e24a7b424686a56f4e6-15"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;OPENS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-16" name="rest_code_942deeba85214e24a7b424686a56f4e6-16"&gt;&lt;/a&gt;            &lt;span class="c1"&gt;## increment count &amp;amp; add to stack&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-17" name="rest_code_942deeba85214e24a7b424686a56f4e6-17"&gt;&lt;/a&gt;            &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-18" name="rest_code_942deeba85214e24a7b424686a56f4e6-18"&gt;&lt;/a&gt;            &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-19" name="rest_code_942deeba85214e24a7b424686a56f4e6-19"&gt;&lt;/a&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-20" name="rest_code_942deeba85214e24a7b424686a56f4e6-20"&gt;&lt;/a&gt;            &lt;span class="c1"&gt;## closing checks&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-21" name="rest_code_942deeba85214e24a7b424686a56f4e6-21"&gt;&lt;/a&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt;  &lt;span class="n"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-22" name="rest_code_942deeba85214e24a7b424686a56f4e6-22"&gt;&lt;/a&gt;                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"invalid"&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-23" name="rest_code_942deeba85214e24a7b424686a56f4e6-23"&gt;&lt;/a&gt;            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-24" name="rest_code_942deeba85214e24a7b424686a56f4e6-24"&gt;&lt;/a&gt;                &lt;span class="c1"&gt;# decrement count_ (__classof(c))&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-25" name="rest_code_942deeba85214e24a7b424686a56f4e6-25"&gt;&lt;/a&gt;                &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;__classof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-26" name="rest_code_942deeba85214e24a7b424686a56f4e6-26"&gt;&lt;/a&gt;                &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-27" name="rest_code_942deeba85214e24a7b424686a56f4e6-27"&gt;&lt;/a&gt;
&lt;a id="rest_code_942deeba85214e24a7b424686a56f4e6-28" name="rest_code_942deeba85214e24a7b424686a56f4e6-28"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"valid"&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We're playing fast and loose here with input validity checking - there's no "is this an empty string?" and we're not
handling a single-character string, let alone validating that our input &lt;em&gt;only&lt;/em&gt; contains braces, parens and brackets.&lt;/p&gt;
&lt;p&gt;With this main() function, though, we're doing pretty well:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-1" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;## main&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-2" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-2"&gt;&lt;/a&gt;&lt;span class="n"&gt;strings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"""()"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-3" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-3"&gt;&lt;/a&gt;           &lt;span class="sd"&gt;"""({})"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-4" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-4"&gt;&lt;/a&gt;           &lt;span class="sd"&gt;"""(}{)"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-5" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-5"&gt;&lt;/a&gt;           &lt;span class="sd"&gt;"""{()}[{}]"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-6" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-6"&gt;&lt;/a&gt;           &lt;span class="sd"&gt;"""({(}))"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-7" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-7"&gt;&lt;/a&gt;           &lt;span class="sd"&gt;"""](){}"""&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-8" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-8"&gt;&lt;/a&gt;           &lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-9" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-9"&gt;&lt;/a&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-10" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;element&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-11" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-11"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="si"&gt;{element:20}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1cde50fce4e045918e3d624351ad3ce1-12" name="rest_code_1cde50fce4e045918e3d624351ad3ce1-12"&gt;&lt;/a&gt;          &lt;span class="n"&gt;check_valid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;which gives us the following output:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-1" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;()&lt;/span&gt;                   valid
&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-2" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;({})&lt;/span&gt;                 valid
&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-3" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;(}{)&lt;/span&gt;                 invalid
&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-4" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-4"&gt;&lt;/a&gt;&lt;span class="o"&gt;{()}[{}]&lt;/span&gt;             valid
&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-5" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;({(}))&lt;/span&gt;               invalid
&lt;a id="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-6" name="rest_code_a75a37f51bc54d8d84ed5627e9a5196d-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;](){}&lt;/span&gt;                valid
&lt;/pre&gt;&lt;p&gt;Using the criteria specified, the final case is invalid, given that it starts with a terminating rather
than initiating/opening element - there's nothing to balance the element with. However at that point my
time was up and I didn't worry about it.&lt;/p&gt;
&lt;p&gt;My interviewer then asked whether I had considered using recursion to solve the problem.&lt;/p&gt;
&lt;p&gt;I hadn't considered recursion because I generally don't have to for the use-cases I need to write - and
in this particular problem space it didn't seem to me to be an effective use of resources.&lt;/p&gt;
&lt;p&gt;Consider the longest case, &lt;strong&gt;{()}[{}]&lt;/strong&gt;. If you're recursing on the check function, then you'll wind
up calling the function four times, so that's four new stack frames to be created and destroyed. That
doesn't strike me as particularly efficient in time or space. Iterating over the input, however,
avoids all of the setup + teardown overhead.&lt;/p&gt;
&lt;p&gt;Anyway, it was a relatively fun exercise, and I'm glad I did it. I was able to keep a cool head and
buy myself enough time to jog my memory and finish the problem, and it worked the first time (I know,
that _never_ happens!).&lt;/p&gt;
&lt;p&gt;For future encounters like this, I think it's handy to remember these points:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Breathe&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Talk through what you are doing, &lt;em&gt;and why&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you hit a problem, see point 1, and then say that you're stuck and need to think through a particular part of the issue.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you need to &lt;em&gt;stop talking&lt;/em&gt; so you can think, say that that's what you need to do.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It is my impression that if your interviewer is a decent person, they will help you work through
your point of stuckness so that you can complete as much as possible of the task.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>live programming</category><category>programming</category><category>Python</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2021-05-09-live-coding-challenge/</guid><pubDate>Sat, 08 May 2021 16:00:00 GMT</pubDate></item><item><title>Why do I see "Duplicate main class"?</title><link>https://www.jmcpdotcom.com/blog/posts/2021-01-17-duplicate-main-class/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;I've recently started work on improving my skills and knowledge in the &lt;a class="reference external" href="https://adoptopenjdk.net"&gt;Java&lt;/a&gt;
ecosystem, and while working on &lt;a class="reference external" href="https://www.jmcpdotcom.com/blog/posts/2020-12-28-access-tokens"&gt;a previous post&lt;/a&gt; I burned several hours
trying to work out why I was seeing this error:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_5f83f855f9fe457c97c204be41644ea4-1" name="rest_code_5f83f855f9fe457c97c204be41644ea4-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;....&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bearertokenCLI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;java&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;duplicate&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;....&lt;/span&gt; &lt;span class="n"&gt;bearer_token_cli&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bearerTokenCLI&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I didn't find the answers at &lt;a class="reference external" href="https://stackoverflow.com"&gt;StackOverflow&lt;/a&gt; to be very useful, because they
invariably said something along the lines of "clean your project and let the
IDE re-index things, it'll be fine".&lt;/p&gt;
&lt;p&gt;Which is &lt;em&gt;not&lt;/em&gt; a solution - it's like "curing" a memory leak by rebooting the
host. I like to know the &lt;em&gt;why&lt;/em&gt; of a problem.&lt;/p&gt;
&lt;p&gt;I eventually re-re-read the message from the &lt;a class="reference external" href="https://maven.apache.org/plugins/maven-compiler-plugin/"&gt;Maven compiler plugin&lt;/a&gt; and
noticed that it was trying to compile &lt;strong&gt;2&lt;/strong&gt; source files. For an exploratory
project which only had one source file, this was unexpected:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_4eef271578a8411eb87a3ce40ddde8b4-1" name="rest_code_4eef271578a8411eb87a3ce40ddde8b4-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; --- maven-compiler-plugin:3.8.1:compile &lt;span class="o"&gt;(&lt;/span&gt;default-compile&lt;span class="o"&gt;)&lt;/span&gt; @ bearer_token_cli ---
&lt;a id="rest_code_4eef271578a8411eb87a3ce40ddde8b4-2" name="rest_code_4eef271578a8411eb87a3ce40ddde8b4-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; Changes detected - recompiling the module!
&lt;a id="rest_code_4eef271578a8411eb87a3ce40ddde8b4-3" name="rest_code_4eef271578a8411eb87a3ce40ddde8b4-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;[&lt;/span&gt;INFO&lt;span class="o"&gt;]&lt;/span&gt; Compiling &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; files to /home/jmcp/IdeaProjects/bearer-token-cli/target/classes
&lt;/pre&gt;&lt;p&gt;Why did I now have two files? The answer lies in a bit of laziness on my
part. The previous post had hard-coded credentials and URLs, but I really
wanted to start using a &lt;em&gt;getopt()&lt;/em&gt;-like library called &lt;a class="reference external" href="https://picocli.info/"&gt;picocli&lt;/a&gt;, and rather
than &lt;cite&gt;git commit ...&lt;/cite&gt; I just copied my first version of the source to
&lt;cite&gt;bearerTokenCLI-hc-v1.java&lt;/cite&gt; and kept on editing.&lt;/p&gt;
&lt;p&gt;Apart from the relevant information (&lt;em&gt;2 source files&lt;/em&gt;) being in an &lt;cite&gt;[INFO]&lt;/cite&gt;
block rather than in the &lt;cite&gt;[ERROR]&lt;/cite&gt; block, why on earth couldn't it have
printed the names of the files it was compiling along the way?&lt;/p&gt;
&lt;p&gt;If you come across this error, a quick&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_aa7aeeb969f840e1aaa00806c084e8fd-1" name="rest_code_aa7aeeb969f840e1aaa00806c084e8fd-1"&gt;&lt;/a&gt;$ find src/main/java -name &lt;span class="se"&gt;\*&lt;/span&gt;.java &lt;span class="p"&gt;|&lt;/span&gt;xargs grep -i &lt;span class="s2"&gt;"public static void main"&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;should help you find where that erroneous &lt;cite&gt;main&lt;/cite&gt; class is hiding.&lt;/p&gt;
&lt;p&gt;Here's where I get a bit ranty. One of the patterns that we invented and
applied to the Solaris OS/Net (ON) source tree during the development of
#ProjectLullaby was that for every subdirectory which contained source files,
the &lt;cite&gt;Makefile&lt;/cite&gt; specified each file individually.&lt;/p&gt;
&lt;p&gt;There are solid reasons for this, starting with the need to ensure that when
you build part or all of the tree, we do not miss dependencies. Over the years
there were enough instances of "developer changes code, adds new file or
renames/splits old file, FAILS TO CHECK IN NEW FILES, breaks build after
integration" that we forced specificity. You want to add, remove or delete
files that the build depended on? It's ON YOU to make sure we're tracking
them. A broken build meant either a followup changeset (with "(add missing
file)" etc appended to the comment), or getting backed out.&lt;/p&gt;
&lt;p&gt;While I'm enjoying some aspects of developing in &lt;a class="reference external" href="https://adoptopenjdk.net"&gt;Java&lt;/a&gt; and I do like
leaving a lot of heavy lifting to a framework or a toolset, the heavy
reliance in the &lt;a class="reference external" href="https://adoptopenjdk.net"&gt;Java&lt;/a&gt; world on an IDE to do thinking for you leaves me
cold.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>Java</category><category>Maven</category><category>programming</category><category>rant</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2021-01-17-duplicate-main-class/</guid><pubDate>Sat, 16 Jan 2021 04:00:00 GMT</pubDate></item><item><title>Access token retrieval in Python and Java</title><link>https://www.jmcpdotcom.com/blog/posts/2020-12-28-access-tokens/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;At $work I'm part of our API team, enabling access to the &lt;em&gt;rather large&lt;/em&gt;
datasets that we have acquired (and generated) over the years. We make heavy
use of &lt;a class="reference external" href="https://www.postman.com/"&gt;Postman&lt;/a&gt; for testing, but every now and again I find that I want to
do something on the commandline.&lt;/p&gt;
&lt;p&gt;All of our APIs require authorization, for which we use &lt;a class="reference external" href="https://en.wikipedia.org/wiki/OAuth#OAuth_2.0"&gt;OAuth&lt;/a&gt;, and
specifically the &lt;em&gt;Bearer Token&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Rather than having to fire up &lt;a class="reference external" href="https://www.postman.com/"&gt;Postman&lt;/a&gt; to extract a Bearer Token, I decided
to write a &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; script to do it for me, so I could then set an
environment variable and pass that to curl as a command-line argument. I was a
little lazy and hard-coded my clientid and secret - I'm not going to be
requesting anybody else's token!&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-1" name="rest_code_ea72aade94644de4bf886ee67a93901f-1"&gt;&lt;/a&gt;&lt;span class="ch"&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-2" name="rest_code_ea72aade94644de4bf886ee67a93901f-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-3" name="rest_code_ea72aade94644de4bf886ee67a93901f-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-4" name="rest_code_ea72aade94644de4bf886ee67a93901f-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"""https://$AUTHSERVER/access/oauth/token?grant_type=client_credentials&amp;amp;client_id=&lt;/span&gt;&lt;span class="si"&gt;{client_id}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;client_secret=&lt;/span&gt;&lt;span class="si"&gt;{client_secret}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-5" name="rest_code_ea72aade94644de4bf886ee67a93901f-5"&gt;&lt;/a&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nope"&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-6" name="rest_code_ea72aade94644de4bf886ee67a93901f-6"&gt;&lt;/a&gt;&lt;span class="n"&gt;client_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"still_nope"&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-7" name="rest_code_ea72aade94644de4bf886ee67a93901f-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-8" name="rest_code_ea72aade94644de4bf886ee67a93901f-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-9" name="rest_code_ea72aade94644de4bf886ee67a93901f-9"&gt;&lt;/a&gt;                               &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-10" name="rest_code_ea72aade94644de4bf886ee67a93901f-10"&gt;&lt;/a&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"export BEARER=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Authorization: Bearer "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;a id="rest_code_ea72aade94644de4bf886ee67a93901f-11" name="rest_code_ea72aade94644de4bf886ee67a93901f-11"&gt;&lt;/a&gt;      &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I'm taking advantage of the fact that I know through many years of use that
the &lt;a class="reference external" href="https://requests.readthedocs.io/en/master/"&gt;requests&lt;/a&gt; package does a lot of heavy lifting for me, particularly the
JSON decoding.&lt;/p&gt;
&lt;p&gt;Since $work has a shutdown between Christmas and New Year, I figured that I
would spent some time implmenting this in Java. Not because I have a need to,
but because I need to get more &lt;a class="reference external" href="https://adoptopenjdk.net/"&gt;Java&lt;/a&gt; under my belt since $work is a &lt;a class="reference external" href="https://adoptopenjdk.net/"&gt;Java&lt;/a&gt;
shop and cross-pollination / polyglotting is useful.&lt;/p&gt;
&lt;p&gt;The first step was to determine how to send an HTTP GET for a specific URI. A
few searches later and I'd arrived at&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_646fad754e83415fb25717fee6f4059b-1" name="rest_code_646fad754e83415fb25717fee6f4059b-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_646fad754e83415fb25717fee6f4059b-2" name="rest_code_646fad754e83415fb25717fee6f4059b-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_646fad754e83415fb25717fee6f4059b-3" name="rest_code_646fad754e83415fb25717fee6f4059b-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_646fad754e83415fb25717fee6f4059b-4" name="rest_code_646fad754e83415fb25717fee6f4059b-4"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpResponse.BodyHandlers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_646fad754e83415fb25717fee6f4059b-5" name="rest_code_646fad754e83415fb25717fee6f4059b-5"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.URI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;and since it seems cleaner to import the Exceptions that these throw, I added&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_87462c0d1f1b4fd4933bf76189631c37-1" name="rest_code_87462c0d1f1b4fd4933bf76189631c37-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.IOException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_87462c0d1f1b4fd4933bf76189631c37-2" name="rest_code_87462c0d1f1b4fd4933bf76189631c37-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.URISyntaxException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;A bit more hard-coding laziness for the clientid and secret and I had the
beginnings of a solution. (Note that I do &lt;em&gt;not&lt;/em&gt; claim that any of this is using
Best Practices, it's just a building block).&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-1" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-1"&gt;&lt;/a&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;bearerTokenCLI&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-2" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-3" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-3"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;authServer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-4" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-4"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-5" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-6" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-6"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-7" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-8" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-9" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-9"&gt;&lt;/a&gt;            &lt;span class="n"&gt;authServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;URI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-10" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-10"&gt;&lt;/a&gt;                    &lt;span class="s"&gt;"$AUTHSERVER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-11" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-11"&gt;&lt;/a&gt;                    &lt;span class="s"&gt;"/access/oauth/token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-12" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-12"&gt;&lt;/a&gt;                    &lt;span class="s"&gt;"grant_type=client_credentials"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-13" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-13"&gt;&lt;/a&gt;                            &lt;span class="s"&gt;"&amp;amp;client_id=nope"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-14" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-14"&gt;&lt;/a&gt;                            &lt;span class="s"&gt;"&amp;amp;client_secret=still_node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-15" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-15"&gt;&lt;/a&gt;        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URISyntaxException&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-16" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-16"&gt;&lt;/a&gt;            &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received URISyntaxException"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-17" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-17"&gt;&lt;/a&gt;            &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReason&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-18" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-18"&gt;&lt;/a&gt;            &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-19" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-19"&gt;&lt;/a&gt;            &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInput&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-20" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-20"&gt;&lt;/a&gt;        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-21" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-21"&gt;&lt;/a&gt;
&lt;a id="rest_code_58d1ee2e59a14cbda0edfa383afaecab-22" name="rest_code_58d1ee2e59a14cbda0edfa383afaecab-22"&gt;&lt;/a&gt;        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Requesting "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;authServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Ok so far - we've created a new &lt;em&gt;URI&lt;/em&gt; object, caught the specific exception
that it could throw, and (because I'm learning) printing the stringified
version of the URI to stdout.&lt;/p&gt;
&lt;p&gt;Now we need an &lt;cite&gt;HttpRequest&lt;/cite&gt; to send via an &lt;cite&gt;HttpClient&lt;/cite&gt;:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-1" name="rest_code_eb8ce2b30581422e812b3668401fabf3-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authServer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-2" name="rest_code_eb8ce2b30581422e812b3668401fabf3-2"&gt;&lt;/a&gt;&lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newHttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-3" name="rest_code_eb8ce2b30581422e812b3668401fabf3-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-4" name="rest_code_eb8ce2b30581422e812b3668401fabf3-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-5" name="rest_code_eb8ce2b30581422e812b3668401fabf3-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BodyHandlers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-6" name="rest_code_eb8ce2b30581422e812b3668401fabf3-6"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;jiie&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-7" name="rest_code_eb8ce2b30581422e812b3668401fabf3-7"&gt;&lt;/a&gt;    &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-8" name="rest_code_eb8ce2b30581422e812b3668401fabf3-8"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     * Note that this catch() uses Java7++ syntax for handling&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-9" name="rest_code_eb8ce2b30581422e812b3668401fabf3-9"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     * multiple exceptions in the same block&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-10" name="rest_code_eb8ce2b30581422e812b3668401fabf3-10"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-11" name="rest_code_eb8ce2b30581422e812b3668401fabf3-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received java.io.IOException"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-12" name="rest_code_eb8ce2b30581422e812b3668401fabf3-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCause&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-13" name="rest_code_eb8ce2b30581422e812b3668401fabf3-13"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-14" name="rest_code_eb8ce2b30581422e812b3668401fabf3-14"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jiie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStackTrace&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_eb8ce2b30581422e812b3668401fabf3-15" name="rest_code_eb8ce2b30581422e812b3668401fabf3-15"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Assuming we didn't get an exception, we need to check that the HTTP Status
Code of the response is OK, or 200:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-1" name="rest_code_78fe01ceba0245128c233f7f77e11f04-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-2" name="rest_code_78fe01ceba0245128c233f7f77e11f04-2"&gt;&lt;/a&gt;    &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-3" name="rest_code_78fe01ceba0245128c233f7f77e11f04-3"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     * Something went wrong so print the url we requested, status code,&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-4" name="rest_code_78fe01ceba0245128c233f7f77e11f04-4"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     * an error message and the response body as text.&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-5" name="rest_code_78fe01ceba0245128c233f7f77e11f04-5"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-6" name="rest_code_78fe01ceba0245128c233f7f77e11f04-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request was unsuccessful. "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-7" name="rest_code_78fe01ceba0245128c233f7f77e11f04-7"&gt;&lt;/a&gt;            &lt;span class="s"&gt;"Received status code "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-8" name="rest_code_78fe01ceba0245128c233f7f77e11f04-8"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"URL requested was\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;authServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-9" name="rest_code_78fe01ceba0245128c233f7f77e11f04-9"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response body text:\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-10" name="rest_code_78fe01ceba0245128c233f7f77e11f04-10"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_78fe01ceba0245128c233f7f77e11f04-11" name="rest_code_78fe01ceba0245128c233f7f77e11f04-11"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If it isn't ok, we bail out, and otherwise we check for the &lt;em&gt;Content-Type&lt;/em&gt;
header being set to 'application/json'. Why that specific value? If you refer
to the RFCs for OAuth (&lt;a class="reference external" href="https://tools.ietf.org/html/rfc6749"&gt;RFC6749&lt;/a&gt; and &lt;a class="reference external" href="https://tools.ietf.org/html/rfc6750"&gt;RFC6750&lt;/a&gt;) specifically section 5 of
the latter, you see that&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The parameters are included in the entity-body of the HTTP response
using the "application/json" media type as defined by [RFC4627].  The
parameters are serialized into a JavaScript Object Notation (JSON)
structure by adding each parameter at the highest structure level.
Parameter names and string values are included as JSON strings.
Numerical values are included as JSON numbers.  The order of
parameters does not matter and can vary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's check for that type then.&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-1" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-1"&gt;&lt;/a&gt;&lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-2" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-2"&gt;&lt;/a&gt;&lt;span class="cm"&gt; * Check that we've got 'application/json' as the Content-Type.&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-3" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-3"&gt;&lt;/a&gt;&lt;span class="cm"&gt; * Per https://tools.ietf.org/html/rfc7231#section-3.1 we know that&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-4" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-4"&gt;&lt;/a&gt;&lt;span class="cm"&gt; * Content-Type is a semicolon-delimited string of&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-5" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-5"&gt;&lt;/a&gt;&lt;span class="cm"&gt; *     type/subtype;charset;...&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-6" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-6"&gt;&lt;/a&gt;&lt;span class="cm"&gt; * More importantly, we know from https://tools.ietf.org/html/rfc6749#section-5.1&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-7" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-7"&gt;&lt;/a&gt;&lt;span class="cm"&gt; * that the response type MUST be JSON.&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-8" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-8"&gt;&lt;/a&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-9" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-9"&gt;&lt;/a&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;contentTypeHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-10" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentTypeHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-11" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ERROR: Content-Type header is empty!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-12" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-13" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-13"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_fb6abd0bdb8543f691e49fae20ad9384-14" name="rest_code_fb6abd0bdb8543f691e49fae20ad9384-14"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Since contentTypeHeader is a &lt;em&gt;List&amp;lt;T&amp;gt;&lt;/em&gt; we can either iterate over it, or,
since we know that it can only occur once in an HTTP response we can grab
element 0 directly. Here's iteration (and yes, I know we should be confirming
that we've actually &lt;em&gt;got&lt;/em&gt; 'application/json', do not @ me, etc etc):&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_f5234fd4d456483db1f67573bda6fd5b-1" name="rest_code_f5234fd4d456483db1f67573bda6fd5b-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contentTypeHeader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_f5234fd4d456483db1f67573bda6fd5b-2" name="rest_code_f5234fd4d456483db1f67573bda6fd5b-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_f5234fd4d456483db1f67573bda6fd5b-3" name="rest_code_f5234fd4d456483db1f67573bda6fd5b-3"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Actual Content-Type bit:   "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_f5234fd4d456483db1f67573bda6fd5b-4" name="rest_code_f5234fd4d456483db1f67573bda6fd5b-4"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;On the other hand, making use of our knowledge that there's only one
Content-Type header in the response, and per &lt;a class="reference external" href="https://tools.ietf.org/html/rfc7231"&gt;RFC7231&lt;/a&gt; we know the format of
the header, we can take a shortcut and grab the value directly:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-1" name="rest_code_432d0726eaac4a0e960e5086afba2e17-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contentTypeHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-2" name="rest_code_432d0726eaac4a0e960e5086afba2e17-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalsIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-3" name="rest_code_432d0726eaac4a0e960e5086afba2e17-3"&gt;&lt;/a&gt;    &lt;span class="cm"&gt;/* Not JSON! */&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-4" name="rest_code_432d0726eaac4a0e960e5086afba2e17-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type is "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-5" name="rest_code_432d0726eaac4a0e960e5086afba2e17-5"&gt;&lt;/a&gt;            &lt;span class="s"&gt;" not application/json. Exiting"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-6" name="rest_code_432d0726eaac4a0e960e5086afba2e17-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_432d0726eaac4a0e960e5086afba2e17-7" name="rest_code_432d0726eaac4a0e960e5086afba2e17-7"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;So far, so good. It took me several hours to get to this point because I not
only had to refresh my memory of those RFCs, but also realising that a
short-circuit was possible.&lt;/p&gt;
&lt;p&gt;Now we can move onto the response body text. By way of printing out
&lt;cite&gt;response.getClass()&lt;/cite&gt; I know that the response is an instance of &lt;cite&gt;class
jdk.internal.net.http.HttpResponseImpl&lt;/cite&gt;, and visual inspection of it shows
that it's JSON. But how do I turn that into an array that I can pull the
&lt;cite&gt;access_token&lt;/cite&gt; information from?&lt;/p&gt;
&lt;p&gt;At first I tried using Google's &lt;a class="reference external" href="https://www.javadoc.io/static/com.google.code.gson/gson/2.8.6/com.google.gson/module-summary.html"&gt;GSON&lt;/a&gt; but I just couldn't get my head around
it. I need to find and understand more code examples. Until I do that,
however, I turned to &lt;a class="reference external" href="https://github.com/FasterXML/jackson-jr"&gt;Jackson JR&lt;/a&gt;, which I found a lot more straightforward.&lt;/p&gt;
&lt;p&gt;We need another import, this time&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_971b2d4d01ee464fb8b6238c7c765162-1" name="rest_code_971b2d4d01ee464fb8b6238c7c765162-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.fasterxml.jackson.jr.ob.JSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;And then we construct a &lt;em&gt;Map&amp;lt;String, Object&amp;gt;&lt;/em&gt; from the response body:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-1" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-2" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;containerJSON&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;std&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-3" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-3"&gt;&lt;/a&gt;    &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;containerJSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-4" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"export BEARER=\"BEARER "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\"\n"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-5" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-5"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-6" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caught exception "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-7" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-7"&gt;&lt;/a&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Message:\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;a id="rest_code_61fdf22996d643c884bf3db3d3cd8f89-8" name="rest_code_61fdf22996d643c884bf3db3d3cd8f89-8"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;You'll observe that I'm again being a bit lazy here by wrapping this block in
the one &lt;em&gt;try {...} catch (..) {..}&lt;/em&gt; block. Whyso?  Because by this point we
should be certain that we've actually got an &lt;em&gt;access_token&lt;/em&gt; element in the
response, and if we don't then there's something going wrong upstream.&lt;/p&gt;
&lt;p&gt;Finally, how do we build this thing? As much as I'd like to just run &lt;em&gt;javac&lt;/em&gt;
over the source and create a solitary jar, I've found that including external
dependencies is made immensely easier by using a build system like &lt;a class="reference external" href="https://maven.apache.org"&gt;Maven&lt;/a&gt;,
&lt;a class="reference external" href="https://ant.apache.org"&gt;Ant&lt;/a&gt; or &lt;a class="reference external" href="https://gradle.org"&gt;Gradle&lt;/a&gt; in the Java ecosystem. For C, of course, there's no place
like &lt;a class="reference external" href="https://docs.oracle.com/cd/E86824_01/html/E54763/make-1s.html#REFMAN1make-1s"&gt;make(1s)&lt;/a&gt; (ok, or &lt;a class="reference external" href="https://www.gnu.org/software/make/manual/"&gt;GNU Make&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I started with using a &lt;a class="reference external" href="https://maven.apache.org/guides/introduction/introduction-to-archetypes.html"&gt;Maven *archetype*&lt;/a&gt;, added this dependency to
&lt;em&gt;pom.xml&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code xml"&gt;&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-1" name="rest_code_55d927f7a07949a283df48f9c624a7e8-1"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-2" name="rest_code_55d927f7a07949a283df48f9c624a7e8-2"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-3" name="rest_code_55d927f7a07949a283df48f9c624a7e8-3"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.fasterxml.jackson.jr&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-4" name="rest_code_55d927f7a07949a283df48f9c624a7e8-4"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jackson-jr-objects&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-5" name="rest_code_55d927f7a07949a283df48f9c624a7e8-5"&gt;&lt;/a&gt;        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.12.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-6" name="rest_code_55d927f7a07949a283df48f9c624a7e8-6"&gt;&lt;/a&gt;    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_55d927f7a07949a283df48f9c624a7e8-7" name="rest_code_55d927f7a07949a283df48f9c624a7e8-7"&gt;&lt;/a&gt;&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;and added the &lt;a class="reference external" href="https://maven.apache.org/plugins/maven-assembly-plugin/"&gt;Maven Assembly plugin&lt;/a&gt; to the &lt;em&gt;&amp;lt;build&amp;gt;&lt;/em&gt; lifecycle. Then
building was a matter of&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_5ebb15d29a4f43d1837ab2d60818e93e-1" name="rest_code_5ebb15d29a4f43d1837ab2d60818e93e-1"&gt;&lt;/a&gt;$ mvn clean install package assembly:single
&lt;/pre&gt;&lt;p&gt;and then I could run the package with&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_4e9092483bc14fafb6198c3f9626ca97-1" name="rest_code_4e9092483bc14fafb6198c3f9626ca97-1"&gt;&lt;/a&gt;$ java -jar target/bearer_token_cli-1.0-SNAPSHOT-jar-with-dependencies.jar
&lt;/pre&gt;&lt;p&gt;All up, I estimate that researching and writing this in Java took me about 12
hours. Most of which was ecosystem research and exploration. There was only
one syntax issue which tripped me up - I needed an Array and for about 10
minutes was searching through &lt;a class="reference external" href="https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html"&gt;Javadocs&lt;/a&gt; for an appropriate class before I
remembered I could use &lt;cite&gt;String[] arrName&lt;/cite&gt;. Sigh.&lt;/p&gt;
&lt;p&gt;Learning the ecosystem is the thing I'm finding most difficult with Java -
it's &lt;em&gt;huge&lt;/em&gt; and there are so many different classes to solve overlapping
problems. I haven't even begun to work with &lt;cite&gt;@Annotations&lt;/cite&gt; or dependency
injection for my own code yet. Truth be told, after a decade+ of working in
Solaris, the idea that anything could be injected into the code I've written
puts a chill down my spine. I'm sure I'll get past it one day.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>Java</category><category>JWT</category><category>OAuth</category><category>programming</category><category>Python</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2020-12-28-access-tokens/</guid><pubDate>Sun, 27 Dec 2020 17:00:00 GMT</pubDate></item><item><title>I know, I'll use a regex!</title><link>https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;This past week, a colleague asked me for help with a shell script that
he had come across while investigating how we run one of our data ingestion
pipelines. The shell script was designed to clean input CSV files if they
had lines which didn't match a specific pattern.&lt;/p&gt;
&lt;p&gt;Now to start with, the script was run over a directory and used a &lt;em&gt;very&lt;/em&gt;
gnarly bit of shell &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Glob_(programming)"&gt;globbing&lt;/a&gt;  to generate a list of files in a subdirectory.
That list was then iterated over to check for a &lt;cite&gt;.csv&lt;/cite&gt; extension.&lt;/p&gt;
&lt;p&gt;[Please save your eye-rolls and "but couldn't they..." for later].&lt;/p&gt;
&lt;p&gt;Once that list of files had been weeded to only contain CSVs, each of those
files was catted and read line by line to see if the line matched a desired
pattern - using shell regular expression parsing. If the line did not match
the pattern, it was deleted. The matching lines were then written to a new
file.&lt;/p&gt;
&lt;p&gt;[Again, please save your eye-rolls and "but couldn't they..." for later].&lt;/p&gt;
&lt;p&gt;The klaxons went off for my colleague when he saw the regex:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-1" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;NEW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="p"&gt;%.csv&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;_clean.csv&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-2" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-2"&gt;&lt;/a&gt;  &lt;span class="o"&gt;{&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-3" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-3"&gt;&lt;/a&gt;  &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-4" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-4"&gt;&lt;/a&gt;  &lt;span class="nb"&gt;read&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-5" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-5"&gt;&lt;/a&gt;  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; -r line &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-6" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-6"&gt;&lt;/a&gt;  &lt;span class="k"&gt;do&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-7" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-7"&gt;&lt;/a&gt;        &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="si"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-8" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$buffer&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]{&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;-&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-2&lt;span class="o"&gt;])&lt;/span&gt;-&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-2&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="o"&gt;[&lt;/span&gt;^,&lt;span class="o"&gt;]&lt;/span&gt;*,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,.*$ &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-9" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-9"&gt;&lt;/a&gt;        &lt;span class="k"&gt;then&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-10" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-10"&gt;&lt;/a&gt;              &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$buffer&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-11" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-11"&gt;&lt;/a&gt;              &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-12" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-12"&gt;&lt;/a&gt;        &lt;span class="k"&gt;else&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-13" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-13"&gt;&lt;/a&gt;              &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-14" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-14"&gt;&lt;/a&gt;        &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-15" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-15"&gt;&lt;/a&gt;  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;a id="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-16" name="rest_code_a42af1a719fb4516ae880c31ce5b7f7f-16"&gt;&lt;/a&gt;  &lt;span class="o"&gt;}&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NEW&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;My eyes got whiplash. To make it easier to understand, let's put each element of
the pattern on a single line:&lt;/p&gt;
&lt;div class="code"&gt;&lt;table class="codetable"&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-1"&gt;&lt;code data-line-number=" 1"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-1" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-1"&gt;&lt;/a&gt; ^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]{&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;-&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-2&lt;span class="o"&gt;])&lt;/span&gt;-&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-2&lt;span class="o"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;-9&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-2"&gt;&lt;code data-line-number=" 2"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-2" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-2"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-3"&gt;&lt;code data-line-number=" 3"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-3" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-3"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-4"&gt;&lt;code data-line-number=" 4"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-4" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-4"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-5"&gt;&lt;code data-line-number=" 5"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-5" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-5"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-6"&gt;&lt;code data-line-number=" 6"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-6" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-6"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-7"&gt;&lt;code data-line-number=" 7"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-7" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-7"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-8"&gt;&lt;code data-line-number=" 8"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-8" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-8"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-9"&gt;&lt;code data-line-number=" 9"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-9" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-9"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-10"&gt;&lt;code data-line-number="10"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-10" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-10"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-11"&gt;&lt;code data-line-number="11"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-11" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-11"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-12"&gt;&lt;code data-line-number="12"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-12" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-12"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-13"&gt;&lt;code data-line-number="13"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-13" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-13"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-14"&gt;&lt;code data-line-number="14"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-14" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-14"&gt;&lt;/a&gt; &lt;span class="o"&gt;[&lt;/span&gt;^,&lt;span class="o"&gt;]&lt;/span&gt;*,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-15"&gt;&lt;code data-line-number="15"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-15" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-15"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-16"&gt;&lt;code data-line-number="16"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-16" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-16"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-17"&gt;&lt;code data-line-number="17"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-17" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-17"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-18"&gt;&lt;code data-line-number="18"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-18" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-18"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-19"&gt;&lt;code data-line-number="19"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-19" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-19"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-20"&gt;&lt;code data-line-number="20"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-20" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-20"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-21"&gt;&lt;code data-line-number="21"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-21" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-21"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-22"&gt;&lt;code data-line-number="22"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-22" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-22"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-23"&gt;&lt;code data-line-number="23"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-23" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-23"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-24"&gt;&lt;code data-line-number="24"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-24" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-24"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-25"&gt;&lt;code data-line-number="25"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-25" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-25"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-26"&gt;&lt;code data-line-number="26"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-26" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-26"&gt;&lt;/a&gt; &lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;^&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;*&lt;span class="se"&gt;\"&lt;/span&gt;,
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="linenos linenodiv"&gt;&lt;a href="https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/#rest_code_a2c8b959f269425d95ab26eb16c2e463-27"&gt;&lt;code data-line-number="27"&gt;&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;code&gt;&lt;a id="rest_code_a2c8b959f269425d95ab26eb16c2e463-27" name="rest_code_a2c8b959f269425d95ab26eb16c2e463-27"&gt;&lt;/a&gt; .*$
&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Which is really something. The first field matches a date format - "yyyy-mm-dd"
(which is ok), then we have 12 fields where we care that they are enclosed in
double quotes, one field that we want to &lt;em&gt;not&lt;/em&gt; be quoted, another 12 fields
which are quoted again, and any other fields we don't care about.&lt;/p&gt;
&lt;p&gt;Wow.&lt;/p&gt;
&lt;p&gt;I told my colleague that this wasn't a good way of doing things (he agreed).&lt;/p&gt;
&lt;p&gt;There are better ways to achieve this, so let's walk through them.&lt;/p&gt;
&lt;p&gt;Firstly, the shell globbing. There's a Unix command to generate a list of
filesystem entries which match particular criteria. It's called &lt;a class="reference external" href="https://www.gnu.org/software/findutils/manual/html_mono/find.html"&gt;find&lt;/a&gt;. If
we want a list of files which have a 'csv' extension we do this:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_2db4cc40a0ac43b89f513f220e3f20aa-1" name="rest_code_2db4cc40a0ac43b89f513f220e3f20aa-1"&gt;&lt;/a&gt;$ find DIR -type f -name &lt;span class="se"&gt;\*&lt;/span&gt;.csv
&lt;/pre&gt;&lt;p&gt;You can use '.' or '*' or any way of representing a DIRectory in the filesystem.&lt;/p&gt;
&lt;p&gt;Now since we want this in a list to iterate over, let's put it in a variable:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_9e14475bec5642209b963aecbcd59b2e-1" name="rest_code_9e14475bec5642209b963aecbcd59b2e-1"&gt;&lt;/a&gt;$ &lt;span class="nv"&gt;CSVfiles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt; find DIR -type f -name &lt;span class="se"&gt;\*&lt;/span&gt;.csv -o -name &lt;span class="se"&gt;\*&lt;/span&gt;.CSV &lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;(You can redirect stderr to /dev/null, with &lt;em&gt;2&amp;gt;/dev/null&lt;/em&gt; inside the parens if you'd like).&lt;/p&gt;
&lt;p&gt;Now that we've got our list, we can move to the second phase - removing lines
which do not match our pattern. Let's try this first with &lt;a class="reference external" href="https://www.gnu.org/software/gawk"&gt;awk&lt;/a&gt;. Awk has
the concept of a &lt;a class="reference external" href="https://www.gnu.org/software/gawk/manual/gawk.html#Field-Separators"&gt;Field Separator&lt;/a&gt;, and since CSV files are Comma-&lt;em&gt;Separated&lt;/em&gt;-Value
files, let's make use of that feature. We also know that we are only really interested
in two fields - the first (yyyy-mm-dd) and the fourteenth.&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_6653f13ddc394679b71007bf043b5abf-1" name="rest_code_6653f13ddc394679b71007bf043b5abf-1"&gt;&lt;/a&gt;$ awk -F&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'$1 ~ /"[0-9]{4}-([0][0-9]|1[0-2])-([0-2][0-9]|3[01])"/ &amp;amp;&amp;amp;&lt;/span&gt;
&lt;a id="rest_code_6653f13ddc394679b71007bf043b5abf-2" name="rest_code_6653f13ddc394679b71007bf043b5abf-2"&gt;&lt;/a&gt;&lt;span class="s1"&gt;    $14 !~ /".*"/ {print}'&lt;/span&gt; &amp;lt; &lt;span class="nv"&gt;$old&lt;/span&gt; &amp;gt; &lt;span class="nv"&gt;$new&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;That's still rather ugly but considerably easier to read. For the record,
the bare ~ is awk's equals operator, and !~ is not-equals.&lt;/p&gt;
&lt;p&gt;We could also do this with &lt;a class="reference external" href="https://www.gnu.org/software/grep/manual/grep.html"&gt;grep&lt;/a&gt;, but at the cost of using more of that horrible regex.&lt;/p&gt;
&lt;p&gt;In my opinion a better method is to cons up a Python script for this validation
purpose, and we don't need to use the &lt;a class="reference external" href="https://docs.python.org/3.8/library/csv.html"&gt;CSV&lt;/a&gt; module.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-1" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserString&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-2" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-3" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-4" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;infile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/file.csv"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"rw"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-5" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-5"&gt;&lt;/a&gt;&lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-6" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-6"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-7" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-7"&gt;&lt;/a&gt;&lt;span class="n"&gt;linecount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-8" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-9" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-10" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-10"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-11" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-12" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;togo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-13" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-13"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-14" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-14"&gt;&lt;/a&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-15" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-15"&gt;&lt;/a&gt;        &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s2"&gt;"%Y-%m-&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-16" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-16"&gt;&lt;/a&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;_ve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-17" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-17"&gt;&lt;/a&gt;        &lt;span class="n"&gt;togo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-18" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-18"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-19" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-19"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;UserString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isnumeric&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-20" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-20"&gt;&lt;/a&gt;        &lt;span class="n"&gt;togo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-21" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-21"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;togo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-22" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-22"&gt;&lt;/a&gt;        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-23" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-23"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-24" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-24"&gt;&lt;/a&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;linecount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-25" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-25"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# We've modified the input, so have to write out a new version, but&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-26" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-26"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# let's overwrite our input file rather than creating a new instance.&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-27" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-27"&gt;&lt;/a&gt;    &lt;span class="n"&gt;infile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-28" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-28"&gt;&lt;/a&gt;    &lt;span class="n"&gt;infile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-29" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-29"&gt;&lt;/a&gt;
&lt;a id="rest_code_3b1a2a15c3004168a8e590e47adf46fe-30" name="rest_code_3b1a2a15c3004168a8e590e47adf46fe-30"&gt;&lt;/a&gt;&lt;span class="n"&gt;infile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This script is pretty close to how I would write it in C (could you tell?).&lt;/p&gt;
&lt;p&gt;We first open the file (for reading &lt;em&gt;and&lt;/em&gt; writing) and read in every line,
which yields us a list. While it's not the most memory-efficient way of
approaching this problem, it does make processing more efficient because
it's one &lt;cite&gt;read()&lt;/cite&gt;, rather than one-read-per-line. We store the number of lines
that we've read in for comparison at the end of our loop, and then start the
processing.&lt;/p&gt;
&lt;p&gt;Since this is a &lt;a class="reference external" href="https://docs.python.org/3.8/library/csv.html"&gt;CSV&lt;/a&gt; file we know we can &lt;cite&gt;split()&lt;/cite&gt; on the comma, and having
done so, we check that we can parse the first field. We're not assigning to
a variable with &lt;cite&gt;datetime.strptime()&lt;/cite&gt; because we only care that we &lt;em&gt;can&lt;/em&gt;
rather than what the object's value is. The second check is to see that
we cannot find the double apostrophe in the element, and that the content of
the field is in fact numeric. If neither of these checks succeed, we know to
delete the line from our input.&lt;/p&gt;
&lt;p&gt;Finally, if we have in fact had to delete any lines, we rewind our file
(I was going to write pointer, but it's a File object. Told you it was close
to C!) to the start, and write out each line of input with a newline character
before closing the file.&lt;/p&gt;
&lt;p&gt;Whenever I think about regexes, &lt;em&gt;especially&lt;/em&gt; the ones I've written in C
over the years, I think about this quote which &lt;a class="reference external" href="http://regex.info/blog/2006-09-15/247"&gt;Jeffrey Friedl&lt;/a&gt; wrote about
a long time ago:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some people, when confronted with a problem, think
“I know, I'll use regular expressions.”   Now they have two problems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It was true when I first heard it some time during my first year of uni, and
still true today.&lt;/p&gt;
&lt;!-- put references after this point --&gt;</description><category>awk</category><category>data cleaning</category><category>Data Engineering</category><category>grep</category><category>programming</category><category>regex</category><category>regexes</category><category>regexp</category><category>regular expressions</category><category>sed</category><category>software engineering</category><category>SQL</category><guid>https://www.jmcpdotcom.com/blog/posts/2020-08-08-i-know-ill-use-a-regex/</guid><pubDate>Fri, 07 Aug 2020 16:00:00 GMT</pubDate></item><item><title>Common Table Expressions and an ORM</title><link>https://www.jmcpdotcom.com/blog/posts/2020-07-23-common-table-expressions-and-an-orm/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;I solved a gnarly performance problem last week and I'd like to share with you what I learnt while
doing it.&lt;/p&gt;
&lt;p&gt;We received an alert from one of our automated monitoring systems that a particular query being
run via an API was taking too long. And by "too long" I mean &lt;em&gt;minutes&lt;/em&gt;. Since database engines
are optimised to return data to you in milliseconds, this seemed wildly wrong.&lt;/p&gt;
&lt;p&gt;The first step when you are checking a query for performance is to use the query analyzer. This is
a very powerful tool in any database, and helps you see just what the engine is going to do for any
particular bit of SQL. The term you're looking for in the documentation is &lt;a class="reference external" href="https://www.postgresql.org/docs/12/sql-explain.html"&gt;EXPLAIN&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We thought the query was fairly simple. It followed this general form:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_47cfdcbe281a4f869e9928b476042fe6-1" name="rest_code_47cfdcbe281a4f869e9928b476042fe6-1"&gt;&lt;/a&gt;SELECT columns, COUNT&lt;span class="o"&gt;(&lt;/span&gt;SELECT specific column WHERE conditions&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;a id="rest_code_47cfdcbe281a4f869e9928b476042fe6-2" name="rest_code_47cfdcbe281a4f869e9928b476042fe6-2"&gt;&lt;/a&gt;FROM database
&lt;a id="rest_code_47cfdcbe281a4f869e9928b476042fe6-3" name="rest_code_47cfdcbe281a4f869e9928b476042fe6-3"&gt;&lt;/a&gt;WHERE conditions
&lt;a id="rest_code_47cfdcbe281a4f869e9928b476042fe6-4" name="rest_code_47cfdcbe281a4f869e9928b476042fe6-4"&gt;&lt;/a&gt;ORDER BY ordering criteria&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;and gave us not just results with specific conditions, but the count of rows which met those
conditions too.&lt;/p&gt;
&lt;p&gt;You will notice that I did not use &lt;em&gt;first set of conditions&lt;/em&gt; and &lt;em&gt;second set of conditions&lt;/em&gt;. This
is because the selection criteria were in fact the same. That was the first clue.&lt;/p&gt;
&lt;p&gt;A second clue was that of these selection conditions was that we had a range check - is the column's
value between A and B? [We actually had two, but having just one was sufficient to show the problem].
This was expressed as&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_8b843337b906468f9613443ace449c91-1" name="rest_code_8b843337b906468f9613443ace449c91-1"&gt;&lt;/a&gt;columnA &amp;gt;&lt;span class="o"&gt;=&lt;/span&gt; smallervalue AND
&lt;a id="rest_code_8b843337b906468f9613443ace449c91-2" name="rest_code_8b843337b906468f9613443ace449c91-2"&gt;&lt;/a&gt;columnA &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; largervalue
&lt;/pre&gt;&lt;p&gt;So you'll see that for each row we had two comparisons to do. I tried making that condition singular,
by using just &lt;em&gt;&amp;gt;= smallervalue&lt;/em&gt; (I also tried &lt;em&gt;&amp;lt;= largervalue&lt;/em&gt;&amp;gt; - no difference in timing) and while
that did make the query faster, it did not reflect what we need to be able to do, so that was out.&lt;/p&gt;
&lt;p&gt;Back to the query planner. It wasn't happy. It turns out that the most efficient plan it could come up
with was using a &lt;em&gt;Bitmap Index Scan&lt;/em&gt;. Twice. For the same query - but throwing the results of the first
query away before running the whole thing again.&lt;/p&gt;
&lt;p&gt;I knew that there had to be a better way (tm) - and there was. Here's the first thing I learned: the
SQL standard has a thing called a &lt;a class="reference external" href="https://www.postgresql.org/docs/12/queries-with.html"&gt;Common Table Expression&lt;/a&gt; or &lt;strong&gt;CTE&lt;/strong&gt;. This is a way of creating a
temporary table that is used for just that query.&lt;/p&gt;
&lt;p&gt;With this knowledge, I could now re-write the query in a considerably more efficient fashion. (It took
me several days to figure this out - I'm no SQL guru!)&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-1" name="rest_code_e90485881a7d4a5192dbc863799ace31-1"&gt;&lt;/a&gt;WITH better_query AS
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-2" name="rest_code_e90485881a7d4a5192dbc863799ace31-2"&gt;&lt;/a&gt;    &lt;span class="o"&gt;(&lt;/span&gt;SELECT columnname
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-3" name="rest_code_e90485881a7d4a5192dbc863799ace31-3"&gt;&lt;/a&gt;    FROM tablename
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-4" name="rest_code_e90485881a7d4a5192dbc863799ace31-4"&gt;&lt;/a&gt;    WHERE conditions&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-5" name="rest_code_e90485881a7d4a5192dbc863799ace31-5"&gt;&lt;/a&gt;SELECT columns, count&lt;span class="o"&gt;(&lt;/span&gt;better_query.columnname&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-6" name="rest_code_e90485881a7d4a5192dbc863799ace31-6"&gt;&lt;/a&gt;FROM tablename
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-7" name="rest_code_e90485881a7d4a5192dbc863799ace31-7"&gt;&lt;/a&gt;GROUP BY grouping criteria
&lt;a id="rest_code_e90485881a7d4a5192dbc863799ace31-8" name="rest_code_e90485881a7d4a5192dbc863799ace31-8"&gt;&lt;/a&gt;ORDER BY ordering criteria&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Excellent! ... except that that was giving me all the rows in &lt;cite&gt;tablename&lt;/cite&gt; which matched from &lt;cite&gt;better_query&lt;/cite&gt;
rather than the specific rows which met &lt;cite&gt;conditions&lt;/cite&gt;. To solve this niggle I needed a join.&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-1" name="rest_code_30c9832ddbaa41dfb81541a18935a673-1"&gt;&lt;/a&gt;WITH better_query AS
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-2" name="rest_code_30c9832ddbaa41dfb81541a18935a673-2"&gt;&lt;/a&gt;    &lt;span class="o"&gt;(&lt;/span&gt;SELECT columnname
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-3" name="rest_code_30c9832ddbaa41dfb81541a18935a673-3"&gt;&lt;/a&gt;    FROM tablename
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-4" name="rest_code_30c9832ddbaa41dfb81541a18935a673-4"&gt;&lt;/a&gt;    WHERE conditions&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-5" name="rest_code_30c9832ddbaa41dfb81541a18935a673-5"&gt;&lt;/a&gt;SELECT columns, count&lt;span class="o"&gt;(&lt;/span&gt;better_query.columnname&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-6" name="rest_code_30c9832ddbaa41dfb81541a18935a673-6"&gt;&lt;/a&gt;FROM tablename TN
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-7" name="rest_code_30c9832ddbaa41dfb81541a18935a673-7"&gt;&lt;/a&gt;JOIN better_query BQ on BQ.columnname &lt;span class="o"&gt;=&lt;/span&gt; TN.columnname
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-8" name="rest_code_30c9832ddbaa41dfb81541a18935a673-8"&gt;&lt;/a&gt;GROUP BY grouping criteria
&lt;a id="rest_code_30c9832ddbaa41dfb81541a18935a673-9" name="rest_code_30c9832ddbaa41dfb81541a18935a673-9"&gt;&lt;/a&gt;ORDER BY ordering criteria&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The query planner was a lot happier. Happier to the tune of a 20x improvement.&lt;/p&gt;
&lt;p&gt;There might have been a cheer or two.&lt;/p&gt;
&lt;p&gt;So how do we put this into our application codebase?&lt;/p&gt;
&lt;p&gt;While a major reason $employer hired me was my &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; skills, $employer isn't a Python shop
by any stretch of the imagination. We're a &lt;a class="reference external" href="https://adoptopenjdk.net/"&gt;Java&lt;/a&gt; shop, and I'm cross-pollinating :-).&lt;/p&gt;
&lt;p&gt;Part of that process has been learning how we abstract database specificities out of the code - we use an
&lt;a class="reference external" href="https://en.m.wikipedia.org/wiki/Object-relational_mapping"&gt;Object-Relational Mapping&lt;/a&gt; (aka ORM) to cope with using more than one vendor's database. There
are a few options for &lt;a class="reference external" href="https://en.m.wikipedia.org/wiki/List_of_object-relational_mapping_software#Java"&gt;Java ORMs&lt;/a&gt; and the one that we use is &lt;a class="reference external" href="https://www.jooq.org/"&gt;jOOQ&lt;/a&gt;. This is the first time
I've used an ORM (of course I've read about them, but years ago) so this too was something new
that I've learned. Heading to the documentation I read a lot, getting my head around the concepts
and syntax.&lt;/p&gt;
&lt;p&gt;It took me several days of research and hacking (&lt;a class="reference external" href="https://stackoverflow.com"&gt;stackoverflow&lt;/a&gt;, &lt;a class="reference external" href="https://github.com"&gt;github&lt;/a&gt;, come on down!) to work
out the correct syntax.&lt;/p&gt;
&lt;p&gt;Here's the gist of what we needed:&lt;/p&gt;
&lt;pre class="code java"&gt;&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-1" name="rest_code_9ad58bb211454a28804d99093095bd9d-1"&gt;&lt;/a&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;QueryClass&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-2" name="rest_code_9ad58bb211454a28804d99093095bd9d-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-3" name="rest_code_9ad58bb211454a28804d99093095bd9d-3"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;withClauseName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"betterQuery"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-4" name="rest_code_9ad58bb211454a28804d99093095bd9d-4"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;withClauseField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fieldName"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-5" name="rest_code_9ad58bb211454a28804d99093095bd9d-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-6" name="rest_code_9ad58bb211454a28804d99093095bd9d-6"&gt;&lt;/a&gt;    &lt;span class="p"&gt;....&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-7" name="rest_code_9ad58bb211454a28804d99093095bd9d-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-8" name="rest_code_9ad58bb211454a28804d99093095bd9d-8"&gt;&lt;/a&gt;    &lt;span class="n"&gt;CommonTableExpression&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Record1&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;withClause&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClauseName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-9" name="rest_code_9ad58bb211454a28804d99093095bd9d-9"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClauseField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-10" name="rest_code_9ad58bb211454a28804d99093095bd9d-10"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-11" name="rest_code_9ad58bb211454a28804d99093095bd9d-11"&gt;&lt;/a&gt;            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-12" name="rest_code_9ad58bb211454a28804d99093095bd9d-12"&gt;&lt;/a&gt;            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-13" name="rest_code_9ad58bb211454a28804d99093095bd9d-13"&gt;&lt;/a&gt;        &lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-14" name="rest_code_9ad58bb211454a28804d99093095bd9d-14"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-15" name="rest_code_9ad58bb211454a28804d99093095bd9d-15"&gt;&lt;/a&gt;    &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-16" name="rest_code_9ad58bb211454a28804d99093095bd9d-16"&gt;&lt;/a&gt;        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DSL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-17" name="rest_code_9ad58bb211454a28804d99093095bd9d-17"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-18" name="rest_code_9ad58bb211454a28804d99093095bd9d-18"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-19" name="rest_code_9ad58bb211454a28804d99093095bd9d-19"&gt;&lt;/a&gt;    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-20" name="rest_code_9ad58bb211454a28804d99093095bd9d-20"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-21" name="rest_code_9ad58bb211454a28804d99093095bd9d-21"&gt;&lt;/a&gt;    &lt;span class="cm"&gt;/*&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-22" name="rest_code_9ad58bb211454a28804d99093095bd9d-22"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     * append results field to array of column names in the select, then ...&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-23" name="rest_code_9ad58bb211454a28804d99093095bd9d-23"&gt;&lt;/a&gt;&lt;span class="cm"&gt;     */&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-24" name="rest_code_9ad58bb211454a28804d99093095bd9d-24"&gt;&lt;/a&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-25" name="rest_code_9ad58bb211454a28804d99093095bd9d-25"&gt;&lt;/a&gt;    &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Typename&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;resultSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DSLcontext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-26" name="rest_code_9ad58bb211454a28804d99093095bd9d-26"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-27" name="rest_code_9ad58bb211454a28804d99093095bd9d-27"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-28" name="rest_code_9ad58bb211454a28804d99093095bd9d-28"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-29" name="rest_code_9ad58bb211454a28804d99093095bd9d-29"&gt;&lt;/a&gt;            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;withClauseField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-30" name="rest_code_9ad58bb211454a28804d99093095bd9d-30"&gt;&lt;/a&gt;                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tablename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-31" name="rest_code_9ad58bb211454a28804d99093095bd9d-31"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;groupBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-32" name="rest_code_9ad58bb211454a28804d99093095bd9d-32"&gt;&lt;/a&gt;        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-33" name="rest_code_9ad58bb211454a28804d99093095bd9d-33"&gt;&lt;/a&gt;    &lt;span class="p"&gt;....&lt;/span&gt;
&lt;a id="rest_code_9ad58bb211454a28804d99093095bd9d-34" name="rest_code_9ad58bb211454a28804d99093095bd9d-34"&gt;&lt;/a&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;No, it's not simple - but using &lt;a class="reference external" href="https://www.jooq.org/"&gt;jOOQ&lt;/a&gt; does mean that we can keep our code pretty close
to how we would write bare SQL (in this case that would still be rather gnarly), and that
is definitely a win.&lt;/p&gt;
&lt;section id="acknowledgement"&gt;
&lt;h2&gt;Acknowledgement&lt;/h2&gt;
&lt;p&gt;Since this is another SQL-based post, I'd like to acknowledge the massive knowledge and
confidence boost I got from reading &lt;a class="reference external" href="https://twitter.com/b0rk"&gt;Julia Evans&lt;/a&gt; (aka &lt;a class="reference external" href="https://twitter.com/b0rk"&gt;b0rk&lt;/a&gt;)'s SQL zine &lt;a class="reference external" href="https://wizardzines.com/zines/sql/"&gt;Become a SELECT star&lt;/a&gt;.
She has a beautiful style and breaks complex technical concepts down into easily understandable
chunks with great illustrations. Reading that zine advanced my database confidence immensely.&lt;/p&gt;
&lt;!-- put references after this point --&gt;
&lt;/section&gt;</description><category>Data Engineering</category><category>Data Investigation</category><category>Database</category><category>Databases</category><category>Java</category><category>jOOQ software engineering</category><category>Oracle</category><category>ORM</category><category>PostgreSQL</category><category>programming</category><category>SQL</category><guid>https://www.jmcpdotcom.com/blog/posts/2020-07-23-common-table-expressions-and-an-orm/</guid><pubDate>Wed, 22 Jul 2020 11:00:00 GMT</pubDate></item><item><title>A brief introduction to the Python Database API</title><link>https://www.jmcpdotcom.com/blog/posts/2020-06-03-a-brief-introduction-to-the-python-database-api/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;As popular as NoSQL databases currently are, there is still an immense amount
of data in the world for which the relational database is still the most appropriate
way to store, access and manipulate it. For those of us who like to use &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt;
rather than &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Java_Database_Connectivity"&gt;JDBC&lt;/a&gt;, we are fortunate to have bindings for the major databases
which implement the &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0249/"&gt;Python Database API&lt;/a&gt;, aka &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0249/"&gt;PEP249&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This standard is important because it allows you to write your application in a way
that promotes cross-platform (cross-database engine) portability. While you do still
need to be aware of differences between the databases (for example, Oracle's &lt;strong&gt;VARCHAR2&lt;/strong&gt;
vs PostgreSQL's &lt;strong&gt;VARCHAR&lt;/strong&gt;), the essential tasks that you need to accomplish when
making use of a database are abstracted from you.&lt;/p&gt;
&lt;p&gt;In this brief introduction I will show you how to install two popular Python database
binding packages, connect to a database and run queries.&lt;/p&gt;
&lt;section id="installing-the-bits-you-need"&gt;
&lt;h2&gt;Installing the bits you need&lt;/h2&gt;
&lt;p&gt;I assume that you've got &lt;a class="reference external" href="https://www.python.org"&gt;Python&lt;/a&gt; installed on your system already, and that the database
you want to connect to is set up and running. What you need now are the &lt;em&gt;bindings&lt;/em&gt; for
that particular database. For the &lt;a class="reference external" href="https://www.oracle.com/database/technologies"&gt;Oracle database&lt;/a&gt;, this is a package known as &lt;a class="reference external" href="https://oracle.github.io/python-cx_Oracle/"&gt;cx_Oracle&lt;/a&gt;.
For &lt;a class="reference external" href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;, I suggest &lt;a class="reference external" href="https://www.psycopg.org/"&gt;psycopg2&lt;/a&gt; though there are other bindings available.&lt;/p&gt;
&lt;p&gt;You will &lt;strong&gt;definitely&lt;/strong&gt; need to install the pre-packaged database client libraries on your
host (or in your container) as well. For &lt;a class="reference external" href="https://www.oracle.com"&gt;Oracle&lt;/a&gt; look for your platform in the
&lt;a class="reference external" href="https://www.oracle.com/database/technologies/instant-client.html"&gt;Oracle Instant Client&lt;/a&gt; download pages, for &lt;a class="reference external" href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt; on linux you will need &lt;em&gt;libpq-dev&lt;/em&gt;
as supplied by your package manager. On MacOS, please find the appropriate option from
the &lt;a class="reference external" href="https://www.postgresql.org/download/macosx/"&gt;PostgreSQL MacOSX download page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After you've installed these pieces, you can then &lt;code class="docutils literal"&gt;pip install &lt;span class="pre"&gt;--user&lt;/span&gt; cx_Oracle&lt;/code&gt; or
&lt;code class="docutils literal"&gt;pip install &lt;span class="pre"&gt;--user&lt;/span&gt; psycopg2&lt;/code&gt;. My personal preference is to install bindings in a &lt;em&gt;venv&lt;/em&gt;.
For &lt;a class="reference external" href="https://www.oracle.com/solaris/solaris11/"&gt;Solaris 11&lt;/a&gt; you can get the &lt;a class="reference external" href="https://oracle.github.io/python-cx_Oracle/"&gt;cx_Oracle&lt;/a&gt; bits &lt;em&gt;as well as&lt;/em&gt; the &lt;a class="reference external" href="https://www.oracle.com/database/technologies/instant-client.html"&gt;Oracle Instant Client&lt;/a&gt;
by uttering &lt;code class="docutils literal"&gt;pkg install cx_Oracle&lt;/code&gt;. [On a personal note, I made that possible - see the
History tab on the &lt;a class="reference external" href="https://github.com/oracle/solaris-userland/commits/master/components/python/cx_oracle"&gt;Solaris Userland cx_Oracle github&lt;/a&gt;].&lt;/p&gt;
&lt;/section&gt;
&lt;section id="getting-a-connection-to-the-database"&gt;
&lt;h2&gt;Getting a connection to the database&lt;/h2&gt;
&lt;p&gt;Now that you have the correct packages installed, you can start investigating. The first
thing to do is start an interactive interpreter and import the modules you need:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_abfb6788c9434518a054bd547bdd02d2-1" name="rest_code_abfb6788c9434518a054bd547bdd02d2-1"&gt;&lt;/a&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python3&lt;/span&gt;&lt;span class="mf"&gt;.8&lt;/span&gt;
&lt;a id="rest_code_abfb6788c9434518a054bd547bdd02d2-2" name="rest_code_abfb6788c9434518a054bd547bdd02d2-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cx_Oracle&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;(Note that unless you're using Solaris' pre-packaged version, you &lt;strong&gt;must&lt;/strong&gt; point LD_LIBRARY_PATH to where
the module can locate the Instant Client libraries, specifically &lt;cite&gt;libclntsh.so&lt;/cite&gt;)&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_16aa56354cc844bf90df47b8cb53e6cf-1" name="rest_code_16aa56354cc844bf90df47b8cb53e6cf-1"&gt;&lt;/a&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="mf"&gt;3.8&lt;/span&gt;
&lt;a id="rest_code_16aa56354cc844bf90df47b8cb53e6cf-2" name="rest_code_16aa56354cc844bf90df47b8cb53e6cf-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;psycopg2&lt;/span&gt;
&lt;a id="rest_code_16aa56354cc844bf90df47b8cb53e6cf-3" name="rest_code_16aa56354cc844bf90df47b8cb53e6cf-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;psycopg2.extras&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;execute_batch&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We need a connection to the database, which in Oracle terms is a DSN, or &lt;em&gt;Data Source Name&lt;/em&gt;.
This is made up of the username, password, host (either address or IP), port and database instance name.
Put together, the host, port and database instance are what Oracle calls the "service name".&lt;/p&gt;
&lt;p&gt;The common example you will see in Oracle documentation (and on &lt;a class="reference external" href="https://stackoverflow.com"&gt;stackoverflow&lt;/a&gt;!) is&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_869b5525b0cc4179ae908a0b8852d966-1" name="rest_code_869b5525b0cc4179ae908a0b8852d966-1"&gt;&lt;/a&gt;scott/tiger@orcl:1521/orcl
&lt;/pre&gt;&lt;p&gt;I'm a bit over seeing that, so I've created a different username and DBname:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_9b4f019440f848258a8d620dd01b7050-1" name="rest_code_9b4f019440f848258a8d620dd01b7050-1"&gt;&lt;/a&gt;DEMOUSER/DemoDbUser1@dbhost:1521/demodb
&lt;/pre&gt;&lt;p&gt;Since Oracle defaults to using port 1521 on the host, you only need to specify the port number if your
database is listening on a different port.&lt;/p&gt;
&lt;p&gt;An Oracle connection is therefore&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-1" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DEMOUSER"&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-2" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-2"&gt;&lt;/a&gt;&lt;span class="n"&gt;passwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DemoDbUser1"&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-3" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-3"&gt;&lt;/a&gt;&lt;span class="n"&gt;servicename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dbhost:1521/demodb"&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-4" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-5" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-6" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cx_Oracle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;servicename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-7" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-7"&gt;&lt;/a&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;cx_Oracle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DatabaseError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-8" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-8"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""Unable to obtain a connection to &lt;/span&gt;&lt;span class="si"&gt;{servicename}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;servicename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;servicename&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_06ecf7f1fdb04d8280ae41469dea3270-9" name="rest_code_06ecf7f1fdb04d8280ae41469dea3270-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;With &lt;a class="reference external" href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt; we can also specify whether we want SSL enabled.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-1" name="rest_code_978c35b3b41c4f11b112b443de988cfc-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-2" name="rest_code_978c35b3b41c4f11b112b443de988cfc-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-3" name="rest_code_978c35b3b41c4f11b112b443de988cfc-3"&gt;&lt;/a&gt;                                  &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbuser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-4" name="rest_code_978c35b3b41c4f11b112b443de988cfc-4"&gt;&lt;/a&gt;                                  &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbpassword&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-5" name="rest_code_978c35b3b41c4f11b112b443de988cfc-5"&gt;&lt;/a&gt;                                  &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbhost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-6" name="rest_code_978c35b3b41c4f11b112b443de988cfc-6"&gt;&lt;/a&gt;                                  &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-7" name="rest_code_978c35b3b41c4f11b112b443de988cfc-7"&gt;&lt;/a&gt;                                  &lt;span class="n"&gt;sslmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbsslmode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-8" name="rest_code_978c35b3b41c4f11b112b443de988cfc-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OperationalError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dboe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-9" name="rest_code_978c35b3b41c4f11b112b443de988cfc-9"&gt;&lt;/a&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""Unable to obtain a connection to the database.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-10" name="rest_code_978c35b3b41c4f11b112b443de988cfc-10"&gt;&lt;/a&gt;&lt;span class="s2"&gt;          Please check your database connection details&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-11" name="rest_code_978c35b3b41c4f11b112b443de988cfc-11"&gt;&lt;/a&gt;&lt;span class="s2"&gt;          Notify dbadmin@&lt;/span&gt;&lt;span class="si"&gt;{dbhost}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dbhost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;dbhost&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-12" name="rest_code_978c35b3b41c4f11b112b443de988cfc-12"&gt;&lt;/a&gt;          &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_978c35b3b41c4f11b112b443de988cfc-13" name="rest_code_978c35b3b41c4f11b112b443de988cfc-13"&gt;&lt;/a&gt;    &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Once we have the connection, we need a cursor so we can execute statements:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_9c09af6825224ea09ecc582c2a544036-1" name="rest_code_9c09af6825224ea09ecc582c2a544036-1"&gt;&lt;/a&gt;&lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now that we have our connection and a cursor to use, what can we do with it? SQL baby!&lt;/p&gt;
&lt;p&gt;We need a handy dataset to muck around with, so I've got just the thing courtesy of
my previous post about &lt;a class="reference external" href="https://www.jmcpdotcom.com/blog/posts/2019-09-27-microservices-part-1/"&gt;determining your electorate&lt;/a&gt;. My JSON files have a fairly
basic structure reflecting my needs for that project, but that is still sufficient
our purposes here.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-1" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;
&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-2" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;actf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"find-my-electorate/json/ACT.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-3" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;actf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-4" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-4"&gt;&lt;/a&gt;&lt;span class="n"&gt;dict_keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'Brindabella'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ginninderra'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kurrajong'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Murrumbidgee'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Yerrabi'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-5" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;actf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Yerrabi"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-6" name="rest_code_b87ce28d4dd04d2b9d61c1128d3dc3d7-6"&gt;&lt;/a&gt;&lt;span class="n"&gt;dict_keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'jurisdiction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'locality'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'blocks'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'coords'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The 'jurisdiction' key is the state or territory name (ACT, Australian Capital Territory in this case),
the 'locality' is the electorate name, 'blocks' is a list of the  &lt;a class="reference external" href="https://www.abs.gov.au"&gt;Australian Bureau of Statistics&lt;/a&gt;
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Meshblock#Australia"&gt;Mesh Block&lt;/a&gt; which the electorate contains, and 'coords' is a list of the latitude, longitude pairs
which are the boundaries of those blocks.&lt;/p&gt;
&lt;p&gt;Now we need a schema to operate within. This is reasonably simple - if you've done any work with
SQL databases. To start with, we need two &lt;em&gt;SEQUENCE&lt;/em&gt; s, which are database objects from which
any database user may generate a unique integer. This is the easiest and cheapest way that I know
of to generate values for primary key columns. (For a good explanation on them, see the
&lt;a class="reference external" href="https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/CREATE-SEQUENCE.html#GUID-E9C78A8C-615A-4757-B2A8-5E6EFB130571"&gt;Oracle 19c CREATE SEQUENCE&lt;/a&gt; page). After that, we'll create two tables: electorate and geopoints.
(We're going to ignore the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Meshblock#Australia"&gt;Mesh Block&lt;/a&gt; data, it's not useful for this example).&lt;/p&gt;
&lt;pre class="code SQL"&gt;&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-1" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;-- This is the Oracle DB form&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-2" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;GEOPOINTS&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINTS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-3" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINTS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-4" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-5" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATE_SEQ&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-6" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-6"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;LATLONG_SEQ&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-7" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-8" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATE_SEQ&lt;/span&gt; &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;NOMAXVALUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-9" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;LATLONG_SEQ&lt;/span&gt; &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;NOMAXVALUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-10" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-10"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-11" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-11"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-12" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE_PK&lt;/span&gt;   &lt;span class="nb"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;DEMOUSER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ELECTORATE_SEQ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NEXTVAL&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-13" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-13"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE&lt;/span&gt;      &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-14" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-14"&gt;&lt;/a&gt;    &lt;span class="n"&gt;STATENAME&lt;/span&gt;       &lt;span class="n"&gt;VARCHAR2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="c1"&gt;-- Using the abbreviated form&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-15" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-15"&gt;&lt;/a&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-16" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-16"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-17" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-17"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;GEOPOINTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-18" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-18"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LATLONG_PK&lt;/span&gt;      &lt;span class="nb"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;DEMOUSER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LATLONG_SEQ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NEXTVAL&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-19" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-19"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LATITUDE&lt;/span&gt;        &lt;span class="nb"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-20" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-20"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LONGITUDE&lt;/span&gt;       &lt;span class="nb"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-21" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-21"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE_FK&lt;/span&gt;   &lt;span class="nb"&gt;NUMBER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ELECTORATE_PK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-22" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-22"&gt;&lt;/a&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-23" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-23"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-24" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-24"&gt;&lt;/a&gt;&lt;span class="c1"&gt;-- Now for the PostgreSQL form&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-25" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-25"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;GEOPOINTS&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-26" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-26"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-27" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-27"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-28" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-28"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;ELECTORATE_SEQ&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-29" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-29"&gt;&lt;/a&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="k"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;LATLONG_SEQ&lt;/span&gt; &lt;span class="k"&gt;CASCADE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-30" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-30"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-31" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-31"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATE_SEQ&lt;/span&gt; &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;MAXVALUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-32" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-32"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;LATLONG_SEQ&lt;/span&gt; &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;MINVALUE&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;NO&lt;/span&gt; &lt;span class="k"&gt;MAXVALUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-33" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-33"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-34" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-34"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-35" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-35"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE_PK&lt;/span&gt;   &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NEXTVAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ELECTORATE_SEQ'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-36" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-36"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-37" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-37"&gt;&lt;/a&gt;    &lt;span class="n"&gt;STATENAME&lt;/span&gt;       &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="c1"&gt;-- Using the abbreviated form&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-38" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-38"&gt;&lt;/a&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-39" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-39"&gt;&lt;/a&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-40" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-40"&gt;&lt;/a&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;GEOPOINTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-41" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-41"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LATLONG_PK&lt;/span&gt;      &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NEXTVAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LATLONG_SEQ'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-42" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-42"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LATITUDE&lt;/span&gt;        &lt;span class="nb"&gt;NUMERIC&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-43" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-43"&gt;&lt;/a&gt;    &lt;span class="n"&gt;LONGITUDE&lt;/span&gt;       &lt;span class="nb"&gt;NUMERIC&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-44" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-44"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ELECTORATE_FK&lt;/span&gt;   &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;ELECTORATES&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ELECTORATE_PK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;a id="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-45" name="rest_code_c4f14f494c244b02bf719f5dbb0fa6b0-45"&gt;&lt;/a&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We need to execute these statements one by one, using &lt;cite&gt;cursor.execute()&lt;/cite&gt;. Come back once you've
done that and we've got our schema set up.&lt;/p&gt;
&lt;p&gt;[I see that a short time has passed - welcome back]&lt;/p&gt;
&lt;p&gt;Now we need to populate our database. For both connection types, we'll make use of prepared
statements, which allow us to insert, update, delete or select many rows at a time. For the
Oracle connection we'll use the &lt;cite&gt;executemany()&lt;/cite&gt; function, but for PostgreSQL's &lt;a class="reference external" href="https://www.psycopg.org/"&gt;psycopg2&lt;/a&gt;
bindings we'll use &lt;cite&gt;execute_batch()&lt;/cite&gt; instead. (See the &lt;a class="reference external" href="https://www.psycopg.org/docs/extras.html?highlight=prepared#fast-execution-helpers"&gt;psycopg2 website note&lt;/a&gt; about it).&lt;/p&gt;
&lt;p&gt;For the Oracle examples, we'll use a bind variable so that when we &lt;cite&gt;INSERT&lt;/cite&gt; into
the &lt;cite&gt;ELECTORATES&lt;/cite&gt; table we get the &lt;cite&gt;ELECTORATE_PK&lt;/cite&gt; to use in the subsequent &lt;cite&gt;INSERT&lt;/cite&gt; to the
&lt;cite&gt;GEOPOINTS&lt;/cite&gt; table. That saves us a query to get that information. The &lt;a class="reference external" href="https://www.psycopg.org/"&gt;psycopg2&lt;/a&gt; module
does not have this support, unfortunately. Since we have many records to process, I've created
some small functions to enable &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"&gt;DRY&lt;/a&gt;&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-1" name="rest_code_009e643fbf124a0886afb3bc2578cccc-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# Oracle version&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-2" name="rest_code_009e643fbf124a0886afb3bc2578cccc-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;epk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-3" name="rest_code_009e643fbf124a0886afb3bc2578cccc-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;electStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""INSERT INTO ELECTORATES (ELECTORATE, STATENAME)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-4" name="rest_code_009e643fbf124a0886afb3bc2578cccc-4"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                VALUES (:electorate, :statename)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-5" name="rest_code_009e643fbf124a0886afb3bc2578cccc-5"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                RETURNING ELECTORATE_PK INTO :epk"""&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-6" name="rest_code_009e643fbf124a0886afb3bc2578cccc-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;geoStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""INSERT INTO GEOPOINTS(LATITUDE, LONGITUDE, ELECTORATE_FK)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-7" name="rest_code_009e643fbf124a0886afb3bc2578cccc-7"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...              VALUES (:lat, :long, :fk)"""&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-8" name="rest_code_009e643fbf124a0886afb3bc2578cccc-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-9" name="rest_code_009e643fbf124a0886afb3bc2578cccc-9"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;addstate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-10" name="rest_code_009e643fbf124a0886afb3bc2578cccc-10"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;electorate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-11" name="rest_code_009e643fbf124a0886afb3bc2578cccc-11"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;edict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"electorate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"locality"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-12" name="rest_code_009e643fbf124a0886afb3bc2578cccc-12"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                  &lt;span class="s2"&gt;"statename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-13" name="rest_code_009e643fbf124a0886afb3bc2578cccc-13"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                  &lt;span class="s2"&gt;"epk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;epk&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-14" name="rest_code_009e643fbf124a0886afb3bc2578cccc-14"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;electStmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-15" name="rest_code_009e643fbf124a0886afb3bc2578cccc-15"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-16" name="rest_code_009e643fbf124a0886afb3bc2578cccc-16"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;latlong&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"coords"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-17" name="rest_code_009e643fbf124a0886afb3bc2578cccc-17"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latlong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-18" name="rest_code_009e643fbf124a0886afb3bc2578cccc-18"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                            &lt;span class="s2"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latlong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-19" name="rest_code_009e643fbf124a0886afb3bc2578cccc-19"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                            &lt;span class="s2"&gt;"fk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;epk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])})&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-20" name="rest_code_009e643fbf124a0886afb3bc2578cccc-20"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executemany&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;geoStmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-21" name="rest_code_009e643fbf124a0886afb3bc2578cccc-21"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-22" name="rest_code_009e643fbf124a0886afb3bc2578cccc-22"&gt;&lt;/a&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-23" name="rest_code_009e643fbf124a0886afb3bc2578cccc-23"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;allpoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-24" name="rest_code_009e643fbf124a0886afb3bc2578cccc-24"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"act"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nsw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"nt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"qld"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"sa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"tas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"wa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"vic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"federal"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-25" name="rest_code_009e643fbf124a0886afb3bc2578cccc-25"&gt;&lt;/a&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-26" name="rest_code_009e643fbf124a0886afb3bc2578cccc-26"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-27" name="rest_code_009e643fbf124a0886afb3bc2578cccc-27"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;allpoints&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"json/&lt;/span&gt;&lt;span class="si"&gt;{stu}&lt;/span&gt;&lt;span class="s2"&gt;.json"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stu&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="s2"&gt;"r"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_009e643fbf124a0886afb3bc2578cccc-28" name="rest_code_009e643fbf124a0886afb3bc2578cccc-28"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;addstate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allpoints&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;For the PostgreSQL version, we need to subtly change the &lt;cite&gt;INSERT&lt;/cite&gt; statements:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_a7737c2f0b754830bcd5a283a49c00e4-1" name="rest_code_a7737c2f0b754830bcd5a283a49c00e4-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;electStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""INSERT INTO ELECTORATES (ELECTORATE, STATENAME)&lt;/span&gt;
&lt;a id="rest_code_a7737c2f0b754830bcd5a283a49c00e4-2" name="rest_code_a7737c2f0b754830bcd5a283a49c00e4-2"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                VALUES (&lt;/span&gt;&lt;span class="si"&gt;%(electorate)s&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%(statename)s&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;
&lt;a id="rest_code_a7737c2f0b754830bcd5a283a49c00e4-3" name="rest_code_a7737c2f0b754830bcd5a283a49c00e4-3"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                RETURNING ELECTORATE_PK"""&lt;/span&gt;
&lt;a id="rest_code_a7737c2f0b754830bcd5a283a49c00e4-4" name="rest_code_a7737c2f0b754830bcd5a283a49c00e4-4"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;geoStmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""INSERT INTO GEOPOINTS(LATITUDE, LONGITUDE, ELECTORATE_FK)&lt;/span&gt;
&lt;a id="rest_code_a7737c2f0b754830bcd5a283a49c00e4-5" name="rest_code_a7737c2f0b754830bcd5a283a49c00e4-5"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                VALUES (&lt;/span&gt;&lt;span class="si"&gt;%(latitude)s&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%(longitude)s&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="si"&gt;%(fk)s&lt;/span&gt;&lt;span class="s2"&gt;)"""&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Another thing we need to change is the first execute, because bind variables are an
Oracle extension, and we're also going to change from &lt;cite&gt;executemany()&lt;/cite&gt; to &lt;cite&gt;execute_batch()&lt;/cite&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-1" name="rest_code_60e897fb25464af4a7e2e8009e83600a-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;addstate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-2" name="rest_code_60e897fb25464af4a7e2e8009e83600a-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;electorate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-3" name="rest_code_60e897fb25464af4a7e2e8009e83600a-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;edict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"electorate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"locality"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-4" name="rest_code_60e897fb25464af4a7e2e8009e83600a-4"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                  &lt;span class="s2"&gt;"statename"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"jurisdiction"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-5" name="rest_code_60e897fb25464af4a7e2e8009e83600a-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;electStmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-6" name="rest_code_60e897fb25464af4a7e2e8009e83600a-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;epk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-7" name="rest_code_60e897fb25464af4a7e2e8009e83600a-7"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;points&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-8" name="rest_code_60e897fb25464af4a7e2e8009e83600a-8"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;latlong&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;"coords"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-9" name="rest_code_60e897fb25464af4a7e2e8009e83600a-9"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;             &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;"latitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latlong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-10" name="rest_code_60e897fb25464af4a7e2e8009e83600a-10"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;                            &lt;span class="s2"&gt;"longitude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latlong&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="s2"&gt;"fk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;epk&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-11" name="rest_code_60e897fb25464af4a7e2e8009e83600a-11"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;execute_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;geoStmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;points&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_60e897fb25464af4a7e2e8009e83600a-12" name="rest_code_60e897fb25464af4a7e2e8009e83600a-12"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;The rest of the data loading is the same as with Oracle. Since I'm curious about
efficiencies, I did a rough benchmark of the data load with &lt;cite&gt;executemany()&lt;/cite&gt;  as well,
and it was around half the speed of using &lt;cite&gt;execute_batch()&lt;/cite&gt;. YMMV, of course, so always
test your code with &lt;em&gt;your&lt;/em&gt; data and as close to real-world conditions as possible.&lt;/p&gt;
&lt;p&gt;Now that we have the data loaded we can do some investigations. I'm going to show the
PostgreSQL output for this and call out differences with Oracle where necessary.&lt;/p&gt;
&lt;p&gt;While some parts of our state and territory borders follow rivers and mountain ranges, quite a &lt;em&gt;lot&lt;/em&gt; of
them follow specific latitudes and longitudes. The border between Queensland and New South Wales, for
example is mostly along the 29th parallel. Between the Northern Territory and South Australia it's the
26th parallel, and that between South Australia and Western Australia is along meridian 129 East.&lt;/p&gt;
&lt;p&gt;If you go to a mapping service and plug in 29S,141E (the line between Queensland and New South Wales):&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://www.google.com.au/maps/place/29%C2%B000'00.0%22S+141%C2%B000'00.0%22E/@-28.9999953,140.9978113,17z/data=!3m1!4b1!4m5!3m4!1s0x0:0x0!8m2!3d-29!4d141"&gt;&lt;img alt="/images/2020/python-db-api/29s141e-400x400.png" src="https://www.jmcpdotcom.com/blog/images/2020/python-db-api/29s141e-400x400.png"&gt;&lt;/a&gt;
&lt;p&gt;you'll see that the &lt;em&gt;exact&lt;/em&gt; point is not where the boundary is actually drawn. That means we need to use
some fuzziness in our matching.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-1" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""SELECT STATENAME, ELECTORATE FROM ELECTORATES E WHERE ELECTORATE_PK IN&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-2" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-2"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   (SELECT DISTINCT ELECTORATE_FK FROM GEOPOINTS WHERE LATITUDE BETWEEN -29.001 AND -28.995)&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-3" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-3"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   ORDER BY STATENAME, ELECTORATE"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-4" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-4"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-5" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-5"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-6" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{s:6}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{e}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-7" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-7"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-8" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Ballina&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-9" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-9"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Barwon&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-10" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-10"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Clarence&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-11" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-11"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Lismore&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-12" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;England&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-13" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-13"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Northern&lt;/span&gt; &lt;span class="n"&gt;Tablelands&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-14" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-14"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Page&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-15" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-15"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Parkes&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-16" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-16"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Maranoa&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-17" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-17"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Southern&lt;/span&gt; &lt;span class="n"&gt;Downs&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-18" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-18"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Warrego&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-19" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-19"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Giles&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-20" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-20"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Grey&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-21" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-21"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Stuart&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-22" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-22"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Stuart&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-23" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-23"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Durack&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-24" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-24"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Geraldton&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-25" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-25"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Kalgoorlie&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-26" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-26"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Moore&lt;/span&gt;
&lt;a id="rest_code_84f093f2bf88492eb64d8ea93fe64905-27" name="rest_code_84f093f2bf88492eb64d8ea93fe64905-27"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;North&lt;/span&gt; &lt;span class="n"&gt;West&lt;/span&gt; &lt;span class="n"&gt;Central&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Hmm. Not only did I not want SA or WA electorates returned, the database has also given me the &lt;em&gt;federal&lt;/em&gt; electorates as well.
Let's update our electorates table to reflect that jurisdictional issue:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_16e3b7661d3746409c3569db4be4cd98-1" name="rest_code_16e3b7661d3746409c3569db4be4cd98-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""ALTER TABLE ELECTORATES ADD FEDERAL BOOLEAN"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_16e3b7661d3746409c3569db4be4cd98-2" name="rest_code_16e3b7661d3746409c3569db4be4cd98-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Popping into a &lt;cite&gt;psql&lt;/cite&gt; session for a moment, let's see what we have:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-1" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;demodb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt; &lt;span class="se"&gt;\d&lt;/span&gt; electorates
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-2" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-2"&gt;&lt;/a&gt;                                    Table &lt;span class="s2"&gt;"public.electorates"&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-3" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-3"&gt;&lt;/a&gt;    Column     &lt;span class="p"&gt;|&lt;/span&gt;         Type          &lt;span class="p"&gt;|&lt;/span&gt; Collation &lt;span class="p"&gt;|&lt;/span&gt; Nullable &lt;span class="p"&gt;|&lt;/span&gt;               Default
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-4" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-4"&gt;&lt;/a&gt;---------------+-----------------------+-----------+----------+-------------------------------------
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-5" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-5"&gt;&lt;/a&gt; electorate_pk &lt;span class="p"&gt;|&lt;/span&gt; integer               &lt;span class="p"&gt;|&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt; not null &lt;span class="p"&gt;|&lt;/span&gt; nextval&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'electorate_seq'&lt;/span&gt;::regclass&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-6" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-6"&gt;&lt;/a&gt; electorate    &lt;span class="p"&gt;|&lt;/span&gt; character varying&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt; not null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-7" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-7"&gt;&lt;/a&gt; statename     &lt;span class="p"&gt;|&lt;/span&gt; character varying&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="p"&gt;|&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt; not null &lt;span class="p"&gt;|&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-8" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-8"&gt;&lt;/a&gt; federal       &lt;span class="p"&gt;|&lt;/span&gt; boolean               &lt;span class="p"&gt;|&lt;/span&gt;           &lt;span class="p"&gt;|&lt;/span&gt;          &lt;span class="p"&gt;|&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-9" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-9"&gt;&lt;/a&gt;Indexes:
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-10" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-10"&gt;&lt;/a&gt;    &lt;span class="s2"&gt;"electorates_pkey"&lt;/span&gt; PRIMARY KEY, btree &lt;span class="o"&gt;(&lt;/span&gt;electorate_pk&lt;span class="o"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-11" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-11"&gt;&lt;/a&gt;Referenced by:
&lt;a id="rest_code_e90cea1e2db14364b5c0b9ba665bf662-12" name="rest_code_e90cea1e2db14364b5c0b9ba665bf662-12"&gt;&lt;/a&gt;    TABLE &lt;span class="s2"&gt;"geopoints"&lt;/span&gt; CONSTRAINT &lt;span class="s2"&gt;"geopoints_electorate_fk_fkey"&lt;/span&gt; FOREIGN KEY &lt;span class="o"&gt;(&lt;/span&gt;electorate_fk&lt;span class="o"&gt;)&lt;/span&gt; REFERENCES electorates&lt;span class="o"&gt;(&lt;/span&gt;electorate_pk&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now let's populate that column. Unfortunately, though, some state electorates have the same name as federal electorates - and
some electorate names exist in more than one state, too! (I'm looking at you, Bass!). Tempting as it is to zorch our db and
start from scratch, I'm going to take advantage of this information:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;We added the federal electorate list after all the states,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the federal list was constructed starting with the ACT, and therefore&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Federal electorates will have a higher primary key value than all the states and territories.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With that in mind here's the first federal electorate entry:&lt;/p&gt;
&lt;pre class="code shell"&gt;&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-1" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-1"&gt;&lt;/a&gt;&lt;span class="nv"&gt;demodb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt; SELECT ELECTORATE_PK, ELECTORATE, STATENAME FROM ELECTORATES WHERE &lt;span class="nv"&gt;STATENAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ACT'&lt;/span&gt; ORDER BY ELECTORATE_PK, STATENAME&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-2" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-2"&gt;&lt;/a&gt; electorate_pk &lt;span class="p"&gt;|&lt;/span&gt;  electorate  &lt;span class="p"&gt;|&lt;/span&gt; statename
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-3" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-3"&gt;&lt;/a&gt;---------------+--------------+-----------
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-4" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-4"&gt;&lt;/a&gt;             &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Brindabella  &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-5" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-5"&gt;&lt;/a&gt;             &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Ginninderra  &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-6" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-6"&gt;&lt;/a&gt;             &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Kurrajong    &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-7" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-7"&gt;&lt;/a&gt;             &lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Murrumbidgee &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-8" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-8"&gt;&lt;/a&gt;             &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Yerrabi      &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-9" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-9"&gt;&lt;/a&gt;           &lt;span class="m"&gt;416&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Bean         &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-10" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-10"&gt;&lt;/a&gt;           &lt;span class="m"&gt;417&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Canberra     &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-11" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-11"&gt;&lt;/a&gt;           &lt;span class="m"&gt;418&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; Fenner       &lt;span class="p"&gt;|&lt;/span&gt; ACT
&lt;a id="rest_code_92a473cd465f4548bfe84dfec6fe1a86-12" name="rest_code_92a473cd465f4548bfe84dfec6fe1a86-12"&gt;&lt;/a&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt; rows&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Let's check how many electorates have a primary key &lt;em&gt;higher&lt;/em&gt; than Bean:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_7670b2b022af4c27b07f4dad899d55d0-1" name="rest_code_7670b2b022af4c27b07f4dad899d55d0-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""SELECT COUNT(ELECTORATE_PK), MAX(ELECTORATE_PK) FROM ELECTORATES"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_7670b2b022af4c27b07f4dad899d55d0-2" name="rest_code_7670b2b022af4c27b07f4dad899d55d0-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_7670b2b022af4c27b07f4dad899d55d0-3" name="rest_code_7670b2b022af4c27b07f4dad899d55d0-3"&gt;&lt;/a&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;566&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;566&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;And a quick check to see that we do in fact have 151 electorates with that condition:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_b40e64c765a44f2abeedcd8693a9cdc0-1" name="rest_code_b40e64c765a44f2abeedcd8693a9cdc0-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;566&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;415&lt;/span&gt;
&lt;a id="rest_code_b40e64c765a44f2abeedcd8693a9cdc0-2" name="rest_code_b40e64c765a44f2abeedcd8693a9cdc0-2"&gt;&lt;/a&gt;&lt;span class="mi"&gt;151&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We do. Onwards.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-1" name="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""UPDATE ELECTORATES SET FEDERAL = TRUE WHERE ELECTORATE_PK &amp;gt; 415"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-2" name="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-3" name="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""SELECT COUNT(*) FROM ELECTORATES WHERE FEDERAL IS TRUE"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-4" name="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-4"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-5" name="rest_code_4d3cb694879e4cf29f2a93cc70a2ab01-5"&gt;&lt;/a&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;151&lt;/span&gt;&lt;span class="p"&gt;,)]&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Likewise, we'll set the others to &lt;cite&gt;federal=False&lt;/cite&gt;:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_c262ab0087cd4b74b61a28bede615c25-1" name="rest_code_c262ab0087cd4b74b61a28bede615c25-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""UPDATE ELECTORATES SET FEDERAL = FALSE WHERE ELECTORATE_PK &amp;lt; 416"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_c262ab0087cd4b74b61a28bede615c25-2" name="rest_code_c262ab0087cd4b74b61a28bede615c25-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Back to our queries. I want to see both sorts of electorates, but grouped by whether they are federal or
state electorates:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-1" name="rest_code_942574733e6f4f4c973987c88301bea4-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""SELECT E.STATENAME, E.ELECTORATE, E.FEDERAL FROM ELECTORATES E&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-2" name="rest_code_942574733e6f4f4c973987c88301bea4-2"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   WHERE E.ELECTORATE_PK IN&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-3" name="rest_code_942574733e6f4f4c973987c88301bea4-3"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                       (SELECT DISTINCT ELECTORATE_FK FROM GEOPOINTS WHERE LATITUDE BETWEEN -29.001 AND -28.995)&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-4" name="rest_code_942574733e6f4f4c973987c88301bea4-4"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   GROUP BY E.STATENAME, E.ELECTORATE, E.FEDERAL&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-5" name="rest_code_942574733e6f4f4c973987c88301bea4-5"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   ORDER BY STATENAME, ELECTORATE"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-6" name="rest_code_942574733e6f4f4c973987c88301bea4-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fedstate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-7" name="rest_code_942574733e6f4f4c973987c88301bea4-7"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fedstate&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-8" name="rest_code_942574733e6f4f4c973987c88301bea4-8"&gt;&lt;/a&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ballina'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Barwon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Clarence'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Lismore'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'New England'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Northern Tablelands'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Page'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'NSW'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Parkes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'QLD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Maranoa'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'QLD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Southern Downs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'QLD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Warrego'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Giles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Grey'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Stuart'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Durack'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Geraldton'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Kalgoorlie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Moore'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'North West Central'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-9" name="rest_code_942574733e6f4f4c973987c88301bea4-9"&gt;&lt;/a&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-10" name="rest_code_942574733e6f4f4c973987c88301bea4-10"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tfy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-11" name="rest_code_942574733e6f4f4c973987c88301bea4-11"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-12" name="rest_code_942574733e6f4f4c973987c88301bea4-12"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;"yes"&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-13" name="rest_code_942574733e6f4f4c973987c88301bea4-13"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-14" name="rest_code_942574733e6f4f4c973987c88301bea4-14"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-15" name="rest_code_942574733e6f4f4c973987c88301bea4-15"&gt;&lt;/a&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-16" name="rest_code_942574733e6f4f4c973987c88301bea4-16"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;fedstate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-17" name="rest_code_942574733e6f4f4c973987c88301bea4-17"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;fmtstr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="si"&gt;{statename:6}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{electorate:30}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{federal}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-18" name="rest_code_942574733e6f4f4c973987c88301bea4-18"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmtstr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;federal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tfy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-19" name="rest_code_942574733e6f4f4c973987c88301bea4-19"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-20" name="rest_code_942574733e6f4f4c973987c88301bea4-20"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Ballina&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-21" name="rest_code_942574733e6f4f4c973987c88301bea4-21"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Barwon&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-22" name="rest_code_942574733e6f4f4c973987c88301bea4-22"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Clarence&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-23" name="rest_code_942574733e6f4f4c973987c88301bea4-23"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Lismore&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-24" name="rest_code_942574733e6f4f4c973987c88301bea4-24"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;New&lt;/span&gt; &lt;span class="n"&gt;England&lt;/span&gt;                    &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-25" name="rest_code_942574733e6f4f4c973987c88301bea4-25"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Northern&lt;/span&gt; &lt;span class="n"&gt;Tablelands&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-26" name="rest_code_942574733e6f4f4c973987c88301bea4-26"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Page&lt;/span&gt;                           &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-27" name="rest_code_942574733e6f4f4c973987c88301bea4-27"&gt;&lt;/a&gt;&lt;span class="n"&gt;NSW&lt;/span&gt;    &lt;span class="n"&gt;Parkes&lt;/span&gt;                         &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-28" name="rest_code_942574733e6f4f4c973987c88301bea4-28"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Maranoa&lt;/span&gt;                        &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-29" name="rest_code_942574733e6f4f4c973987c88301bea4-29"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Southern&lt;/span&gt; &lt;span class="n"&gt;Downs&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-30" name="rest_code_942574733e6f4f4c973987c88301bea4-30"&gt;&lt;/a&gt;&lt;span class="n"&gt;QLD&lt;/span&gt;    &lt;span class="n"&gt;Warrego&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-31" name="rest_code_942574733e6f4f4c973987c88301bea4-31"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Giles&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-32" name="rest_code_942574733e6f4f4c973987c88301bea4-32"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Grey&lt;/span&gt;                           &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-33" name="rest_code_942574733e6f4f4c973987c88301bea4-33"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Stuart&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-34" name="rest_code_942574733e6f4f4c973987c88301bea4-34"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Durack&lt;/span&gt;                         &lt;span class="n"&gt;yes&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-35" name="rest_code_942574733e6f4f4c973987c88301bea4-35"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Geraldton&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-36" name="rest_code_942574733e6f4f4c973987c88301bea4-36"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Kalgoorlie&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-37" name="rest_code_942574733e6f4f4c973987c88301bea4-37"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Moore&lt;/span&gt;
&lt;a id="rest_code_942574733e6f4f4c973987c88301bea4-38" name="rest_code_942574733e6f4f4c973987c88301bea4-38"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;North&lt;/span&gt; &lt;span class="n"&gt;West&lt;/span&gt; &lt;span class="n"&gt;Central&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I could have added a &lt;cite&gt;WHERE E.FEDERAL = TRUE&lt;/cite&gt; to the query, too.&lt;/p&gt;
&lt;p&gt;Finally, let's see what state electorates in WA and SA are on the border:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-1" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""SELECT STATENAME, ELECTORATE FROM ELECTORATES E&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-2" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-2"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   WHERE ELECTORATE_PK IN&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-3" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-3"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                       (SELECT DISTINCT ELECTORATE_FK FROM GEOPOINTS WHERE LATITUDE BETWEEN -60.00 AND -25.995&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-4" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-4"&gt;&lt;/a&gt;&lt;span class="s2"&gt;                           AND LONGITUDE BETWEEN 128.995 AND 129.1)&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-5" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-5"&gt;&lt;/a&gt;&lt;span class="s2"&gt;...                   AND FEDERAL = FALSE ORDER BY STATENAME, ELECTORATE"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-6" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-6"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-7" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-7"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_res&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-8" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-8"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="si"&gt;{statename:6}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{electorate}&lt;/span&gt;&lt;span class="s2"&gt;"""&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;electorate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-9" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-9"&gt;&lt;/a&gt;&lt;span class="o"&gt;...&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-10" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-10"&gt;&lt;/a&gt;&lt;span class="n"&gt;NT&lt;/span&gt;     &lt;span class="n"&gt;Namatjira&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-11" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-11"&gt;&lt;/a&gt;&lt;span class="n"&gt;SA&lt;/span&gt;     &lt;span class="n"&gt;Giles&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-12" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;Kalgoorlie&lt;/span&gt;
&lt;a id="rest_code_2beae5225a294a6fa13c6369b79d1f66-13" name="rest_code_2beae5225a294a6fa13c6369b79d1f66-13"&gt;&lt;/a&gt;&lt;span class="n"&gt;WA&lt;/span&gt;     &lt;span class="n"&gt;North&lt;/span&gt; &lt;span class="n"&gt;West&lt;/span&gt; &lt;span class="n"&gt;Central&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Why do we have that electorate from the Northern Territory? It's because the coordinates are a bit fuzzy and we had to use a range (the BETWEEN) in our query.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="finally"&gt;
&lt;h2&gt;Finally&lt;/h2&gt;
&lt;p&gt;I apologise, because while this was supposed to be a &lt;em&gt;brief&lt;/em&gt; introduction I did get side-tracked with data investigation.
I suppose that's an occupational hazard since I'm a Data Engineer working for a company which provides GIS-related services.
Anyway, in terms of depth, this was definitely only scratching the surface of what is possible with Python and databases. I
encourage you to go and read the &lt;a class="reference external" href="https://www.python.org/dev/peps/pep-0249/"&gt;Python Database API&lt;/a&gt; as well as the SQL reference manual(s) for your chosen database and
its binding documentation.&lt;/p&gt;
&lt;!-- put references after this point --&gt;
&lt;/section&gt;</description><category>cx_Oracle</category><category>Data Engineering</category><category>Data Investigation</category><category>Database</category><category>Databases</category><category>Oracle</category><category>PEP 249</category><category>PostgreSQL</category><category>programming</category><category>psycopg2</category><category>Python</category><category>software engineering</category><category>SQL</category><guid>https://www.jmcpdotcom.com/blog/posts/2020-06-03-a-brief-introduction-to-the-python-database-api/</guid><pubDate>Tue, 02 Jun 2020 23:37:00 GMT</pubDate></item><item><title>A Silly Bit Of JavaScript</title><link>https://www.jmcpdotcom.com/blog/posts/2019-04-22-a-silly-bit-of-js/</link><dc:creator>jmcp</dc:creator><description>&lt;p&gt;After many years actively avoiding the issue, I've now accepted the
inevitable: I have to learn &lt;code class="docutils literal"&gt;JavaScript&lt;/code&gt; (and &lt;code class="docutils literal"&gt;node.js&lt;/code&gt; too, for that
matter). While I've spent many years doing enterprise software development
at the boundary of kernel and userspace using C and Python, the software stack
&lt;em&gt;above&lt;/em&gt; has just been what I consumed rather than created. It's time for that
to change.&lt;/p&gt;
&lt;p&gt;As it so happens, I'm on &lt;em&gt;gardening leave&lt;/em&gt; until COB on the 1st of May. I
thought my &lt;a class="reference external" href="https://www.jmcpdotcom.com"&gt;homepage&lt;/a&gt; could do with an update and decided to write a function to
display how much time remains:&lt;/p&gt;
&lt;pre class="code javascript"&gt;&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-1" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-1"&gt;&lt;/a&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;secPerDay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-2" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-2"&gt;&lt;/a&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;enddate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"2019-05-01T07:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-3" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-4" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-4"&gt;&lt;/a&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;timeRemaining&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-5" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-5"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;// refers to enddate, which is Date("2019-05-01T07:00:00.000Z")&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-6" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-6"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-7" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-7"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;enddate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-8" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-8"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Gardening leave has finished, I'm a free agent"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-9" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-9"&gt;&lt;/a&gt;    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-10" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-10"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enddate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;secPerDay&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-11" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-11"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-12" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-12"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;frachours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-13" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-13"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frachours&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-14" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-14"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frachours&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-15" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-15"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Gardening leave ends in "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" days, "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hours&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" hours, "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;" minutes."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-16" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-16"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-17" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-17"&gt;&lt;/a&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-18" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-18"&gt;&lt;/a&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;outputTR&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-19" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-19"&gt;&lt;/a&gt;    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"TextDiv"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-20" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-20"&gt;&lt;/a&gt;    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;b&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timeRemaining&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;/b&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-21" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-21"&gt;&lt;/a&gt;    &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"#000000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-22" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-22"&gt;&lt;/a&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-23" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-23"&gt;&lt;/a&gt;
&lt;a id="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-24" name="rest_code_29a8b8cbf8c54ad3bd8fa1c14f951a3d-24"&gt;&lt;/a&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"outputTR()"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Pretty simple, a little bit silly, but does the job. I'm actually excited by
it because it's an opportunity for me to rejig my thinking about what my
"kernel" is - it's the browser.&lt;/p&gt;
&lt;p&gt;As it happens I also picked up a book called
&lt;a class="reference external" href="https://www.amazon.com/gp/product/0691181624"&gt;Data Visualization: A Practical Introduction&lt;/a&gt;
so I can see a fair bit of inquiry into government datasets in my future.&lt;/p&gt;</description><category>JavaScript</category><category>learning</category><category>programming</category><category>software engineering</category><guid>https://www.jmcpdotcom.com/blog/posts/2019-04-22-a-silly-bit-of-js/</guid><pubDate>Sun, 21 Apr 2019 14:00:00 GMT</pubDate></item></channel></rss>