tag:blogger.com,1999:blog-61574082101252616842024-03-27T16:53:35.412-07:00The Prolific Programmer -- on the web....Free codeThe Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.comBlogger234125tag:blogger.com,1999:blog-6157408210125261684.post-89958166648100700892023-10-18T14:59:00.006-07:002023-10-18T14:59:47.833-07:00How to Implement Basic Authentication in Flask<pre>
<code>
if not request.headers.get("Authorization"):
return jsonify({"error": "Need auth header"})
auth_hdr = base64.urlsafe_b64decode(
request.headers.get("authorization").replace("Basic ", "")
).decode()
username, password = auth_hdr.split(":")
with open("flatFileForPasswords") as fin:
reader = csv.reader(fin)
for r in reader:
if r[0] == username:
if r[1] != password:
return jsonify({"error": "Wrong auth info"})
</code></pre>
<p>For when I forget how to do this.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-40787904536917426412023-10-17T21:09:00.000-07:002023-10-17T21:09:03.253-07:00How to add a Row to a Dataframe in Pandas<p>This is a triivial task that, ought not to warrant its own blogpost, but it took me almost 24 hours to figure out, so here we are:
<code>
df.loc[len(df)] = np.nan
</code>
</p> The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-7718475307071199512023-02-13T13:09:00.000-08:002023-02-13T13:09:47.729-08:00How to Forecast Time Series in R<p>I was pointed to <a href="https://github.com/Akai01/caretForecast">caretForecast</a> to <tt>machine learning algorithms for time series data forecasting</tt> and decided to post this as a tutorial on how I did it (and before I forget). We'll use the UK Unemployment rate from <a href="https://fred.stlouisfed.org/series/UNRTUKA">FRED</a> as our data. Without further ado, then:</p><pre><code>
require(caretForecast)
require(caret)
require( quantmod)
i <- 8
retail.wide <- getSymbols('UNRTUKA', auto.assign=F, src='FRED')
dtlist <- caretForecast::split_ts(as.ts(retail.wide), test_size = 12)
training_data <- dtlist$train
testing_data <- dtlist$test
fit <- caretForecast::ARml(training_data, max_lag = 12, caret_method = "glmboost",
verbose = FALSE)
fc <- caretForecast::forecast(fit, h = length(testing_data), level = c(80,95))
caretForecast::accuracy(fc, testing_data)
</code>
Your output will look like:
<blockquote>
initial_window = NULL. Setting initial_window = 225
> > ME RMSE MAE MPE MAPE MASE
Training set 7.265863e-16 1.353875 0.9509708 -12.93998 25.39738 0.9920345
Test set 1.349505e+00 1.832183 1.4355957 18.10500 19.86195 1.4975859
ACF1 Theil's U
Training set 0.1524037 NA
Test set 0.6623072 1.997636
</blockquote>
</pre>
<p>But, what we're really interested in are the forecasted values, which are embedded in fc, so let's have a look:
<pre>
> fc
Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
246 4.745729 3.340558 6.150899 2.168297 7.323160
247 4.766436 3.361266 6.171607 2.189005 7.343867
248 4.797869 3.392698 6.203040 2.220438 7.375300
249 4.860259 3.455089 6.265430 2.282828 7.437690
250 4.937558 3.532388 6.342729 2.360127 7.514990
251 5.013973 3.608803 6.419144 2.436542 7.591405
252 5.094964 3.689794 6.500135 2.517533 7.672395
253 5.174285 3.769114 6.579456 2.596854 7.751716
254 5.237659 3.832488 6.642829 2.660228 7.815090
255 5.298847 3.893677 6.704018 2.721416 7.876279
256 5.361814 3.956644 6.766985 2.784383 7.939245
257 5.416543 4.011372 6.821714 2.839112 7.993974
</pre>
The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-85598984665503259252023-01-05T01:13:00.002-08:002023-01-05T01:13:49.976-08:00Why the GRA and why now?<p>I caught up on <a href="https://www.youtube.com/@GeorgeGallowayOfficial">George</a> this morning, while debating which cheese to devour (said debate continues as I write this piece). His rant midweek was about the Scottish National Party's <a href="https://www.parliament.scot/bills-and-laws/bills/gender-recognition-reform-scotland-bill">Gender Reassignment Act</a>.</p><p>My thesis rests on several planks, the first of which is that the loyal opposition is more about loyalty to the power brokers and less about opposing their policy prescriptions, especially along pocketbook issues, like adjusting economic levers. Even foreign policy, which realistically speaking should not be the primary concern of the homeless who litter the streets of the United States and Britain, is a realm of policy where the two sides of Congress or Parliament are one.</p><p>However, every now and again, there are elections, where a number of candidates have to differentiate themselves. So, if your voting records are 99 percent identical, how do you do it? There are certain differences which we cannot (yet) gloss over. Namely, that I have <a href="https://hasan.d8u.us/me.png">brown skin</a> and <a href="https://en.wikipedia.org/wiki/File:George_Galloway_2007-02-24,_02.jpg">Mr Galloway</a> does not. Or, perhaps, it is that <a href="https://en.wikipedia.org/wiki/File:Kamala_Harris_Vice_Presidential_Portrait.jpg">Mrs Harris</a> is a woman and <a href="https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Mike_Pence_official_Vice_Presidential_portrait.jpg/330px-Mike_Pence_official_Vice_Presidential_portrait.jpg">Mr Pence</a> is not.</p><p>In other words, elections circa 2022 are fought on social issues, when most working class voters are concerned with economic issues.</p><p>Boundary commissions have made it so that the working class doesn't have much say in elections. And confronted with the choice of one candidate who promises to bomb Kyiv and the other to bomb Mariupol, it's no wonder they choose the third option -- opt out of the system altogether.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0The Hague, Netherlands52.0704978 4.300699923.760263963821153 -30.855550100000002 80.380731636178837 39.4569499tag:blogger.com,1999:blog-6157408210125261684.post-87499794168367251672022-12-19T22:09:00.002-08:002022-12-19T22:09:30.409-08:00How to Make ChatGPT Produce Images with a Transparent Background<p>Friday, I ran into <a href="https://www.linkedin.com/in/lesliepound">TalkAnimate founder Leslie Pound</a> in Los Angeles and played with <a href="https://chat.openai.com/">chatGPT</a>'s image search. We found it was too clever-by-half, in that when we asked for an image on a transparent background, it found on with how the photo editing packages render transparent backgrounds -- ie the checkerboard background. Well, I was.able to render the image with a truly transparent background and I've <a href="https://units.d8u.us/image\?text=transparent%20background%20chess%20board">been able to replace the background with a transparent version</a>, just alter the text value in the URL. Leave a comment if you encounter any trouble.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-48883567667506940462022-07-25T21:03:00.000-07:002022-07-25T21:03:17.219-07:00How to Simulate Turnout in Tunisia's Constitutional Referendum Using R and the TidyverseOn coming home, I looked at <a href="https://r-bloggers.com">R-bloggers</a>, while vegetating over the Giants game. I have a keen interest in Middle Eastern politics and specifically, the machinations in North Africa's Maghreb region.. So, it is with keen interest that I read that Tunisia is having a <a href="https://www.robertkubinec.com/post/tunisia_polls/">constitutional referendum</a> and that he's run a few simulations of the referendum. I've copied the run below:<br/>
<code>
require(tidyverse)<br/>
require(ggthemes) # we need these two and our man forgot to include them.<br/>
<br/>
N <- 7000000<br/>
<br/>
N -> number.of.voters # for clarity purposes<br/>
<br/>
stations <- 4500<br/>
vote_assign <- sample(1:stations,number.of.voters,replace=T, prob=sample(1:3,stations,replace=T))<br/>
# loop over turnout, sample polls, estimate turnout<br/>
<br/>
over_turnout <- parallel::mclapply(seq(.05,.5,by=.1), function(t) {<br/>
# polling station varying turnout rates<br/>
<br/>
station_rates <- rbeta(n=number.of.voters,t*20,(1-t)*20)<br/>
<br/>
# randomly let voters decide to vote depending on true station-level turnout rate<br/>
<br/>
pop_turnout <- lapply(1:stations, function(s) {<br/>
tibble(turnout=rbinom(n=sum(vote_assign==s), size=1,prob = station_rates[s]), station=s)}) %>% bind_rows<br/>
<br/>
over_samples <- lapply(1:1000, function(i) {<br/>
<br/>
# sample 100 random polling stations 1,000 times<br/>
sample_station <- sample(1:stations, size=50)<br/>
<br/>
turn_est <- mean(pop_turnout$turnout[pop_turnout$station %in% sample_station])<br/>
<br/>
return(tibble(mean_est=turn_est, experiment=i))<br/>
}) %>% bind_rows %>% <br/>
mutate(Turnout=t) <br/>
<br/>
over_samples<br/>
},mc.cores=10) %>% bind_rows
over_turnout_biased %>% <br/>
group_by(Turnout) %>% <br/>
summarize(pop_est=mean(mean_est), low_est=quantile(mean_est,.05), high_est=quantile(mean_est, .95)) %>% <br/>
ggplot(aes(y=pop_est,x=Turnout)) +<br/>
geom_pointrange(aes(ymin=low_est, ymax=high_est),size=.5,fatten=1) +<br/>
geom_abline(slope=1,intercept=0,linetype=2,colour="red") +<br/>
theme_economist() + # I prefer this to theme_tufte, as Robert employed <br/>
theme(text=element_text(family="")) +<br/>
labs(y="Estimated Turnout",x="True Turnout", caption=stringr::str_wrap("Comparison of Mourakiboun estimated (y axis) versus actual turnout (x axis). Red line shows where true and estimated values are equal. Based on biased samples of 50 polling stations with higher turnout stations more likely to be sampled. However, simulation assumes no problems with recording votes.")) +<br/>
ylim(c(0,0.5)) +<br/>
xlim(c(0,0.5))<br/>
</code>
From our experience in dealing with Brexit, referenda tend to be very hard to predict if the country isn't accustomed to having them regularly. And, Robert does acknowledge this.The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-85305440113319278532022-05-10T11:52:00.005-07:002023-10-17T21:22:44.339-07:00How I Hire<p>I've interviewed dozens of people for many positions. Some I waved through, a handful I rejected at every one of my various employers. I've had a few cry out of frustration at not being able to solve the problem, I've had a handful who've been resourceful and found the answers on the web in real time. I'm here to tell readers that knowing how to factor a polynomial (or not, as the case may be) is not the reason candidates are asked questions like "what is 2 to the 32nd power?" or "Why are manhole covers round?".</p><p>If you think that grinding on LeetCode or doing hackerrank perfectly is the end-all, be-all of your interview preparation, you will not be bumped ahead. If you feel that your graduating from valedectorian at Choate entitles you to a job, you are not going to get ahead.</p><p>Now, what will and, more importantly, why will it get you the job and keep you there?</p><p>We'll start with the "why" first. So, you went to Stanford. Congrats, you are one of 16,914 individuals who go to Stanford every year. What makes you deserving over any of them? You have a 4.0 GPA, what does that tell me? It tells me that you can give the grader what they want to see for 4 years straight. While your skills at reading people's intentions are commendable, how do I know that you'll continue to do this for us? You need to convince me (along with every other person) that you are the sort of person whose name I will look forward to seeing daily in my inbox -- you probably can gather where this is going...</p><p>What I want to know is "what makes you unique?" and not just nother hapless Cardinal, who found me on a list of alumni and reached out because we pay well enough to let you afford a monthly trip to a gentlemen's club. Do you like to travel? Was your home destroyed by a bomb in 1986? Did you grow up without parents? How did you get interested in this sort of work? You want me to remember you as a person, so that, when I look at your CV in a few days, the fact that you were the Stanford graduate that skiied Whistler at age 5 pops into my head (or whatever it is).</p> The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-66339803174116304882020-09-23T11:49:00.001-07:002020-09-23T11:49:17.809-07:00How to Track ISP Uptime<div>The perl code below uses <a href="http://www.insecure.org/nmap">nmap</a> (through the wonderful <a href="https://metacpan.org/pod/Nmap::Scanner">Nmap::Scanner</a> module) and <a href="https://metacpan.org/pod/Text::CSV_XS">Text::CSV_XS</a> -- though <a href="https://metacpan.org/pod/Text::CSV">Text::CSV</a> will work if you modify the import line:
<code><pre>
use strict; use warnings; use diagnostics;
use Getopt::Long;
use Nmap::Scanner;
use Text::CSV_XS;
my $host = 'hasan.d8u.us';
GetOptions ('host=s' => \$host) or $host = 'hasan.d8u.us';
my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1 });
my $last = 'up';
my $scanner = new Nmap::Scanner;
$scanner->register_port_found_event(\&host_found);
$scanner->register_no_ports_open_event(\&host_found);
do {
$scanner->scan("-p 22 --system-dns -Pn $host");
sleep 30;
} while 1;
sub host_found {
my $self = shift;
my $host = shift;
my $port = shift;
my $time = time();
if ($host->status ne $last) {
$csv->say($time, $host->status);
$last = $host->status;
}
}
</pre>
</code>
</div>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Beverly Hills, CA, USA34.0736204 -118.40035635.7633865638211574 -153.5566063 62.383854236178848 -83.2441063tag:blogger.com,1999:blog-6157408210125261684.post-58659868014140450152020-08-31T21:25:00.002-07:002020-08-31T21:25:34.012-07:00How to solve fizzbuzz<p>I was on a phone screen today and was given the famous (or infamous) <a href="https://rosettacode.org/wiki/FizzBuzz">FizzBuzz</a> problem. I will provide a strategy to solve the class of problems that fizzbuzz represents, namely, take two numbers, <i>a</i> and <i>b</i> both positive. The question is phrased thusly:</p>
<blockquote>Generate numbers 1 to n, print "fizz" for every number divisible by <i>a</i>, "buzz" for every number divisible by <i>b</i>, and "fizzbuzz" for every number divisible by <i>a*b</i>. If not, print the number itself.</blockquote>
<p>The key here is to start with the most restrictive case first, the <i>a*b</i>, before tackling the larger of a and b as an else if clause and the default case being the final line of the loop. Hence, in pseudocode:</p>
<code>
for number in range(1, infinity):<br/>
if number % (a*b) == 0:<br/>
console_print 'fizzbuzz'<br/>
else if number % b:<br/>
console_print 'buzz'<br/>
else if number % a:<br/>
conosle_print 'fizz'<br/>
else:<br/>
console_print number<br/>
</code>
<p>This code isn't in any language, but is probably easy-enough to translate to whatever language you need to. Also, it is general enough to (hopefully) enumerate the general case. I do, however, welcome further questions and feedback. Many thanks!</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Orderly Health39.772236500000012 -104.983883211.462002663821167 -140.14013319999998 68.082470336178858 -69.8276332tag:blogger.com,1999:blog-6157408210125261684.post-31847665577340329512020-08-17T00:50:00.003-07:002020-08-17T00:50:54.412-07:00How to Add Grain in the Gimp<p>I have long wanted to learn how to retouch photos I've taken. One of the reasons I pay for <a href="https://hd1.smugmug.com">Smugmug</a> is that it won't mess with my creations. So, I took <a href="https://hd1.smugmug.com/Playing-with-the-Gimp/n-3v2nLr/i-K497KGj">Terry's photos</a>, <a href="https://hd1.smugmug.com/Playing-with-the-Gimp/n-3v2nLr/i-d4XwDJx/A">adjusted its saturation</a>, before finally <a href="https://hd1.smugmug.com/Playing-with-the-Gimp/n-3v2nLr/i-n2GhTtJ/A">adding grain</a>. Here's how I did it:<ul><li>To allow for easy redo, you should start off by duplicating the layer, under the layers menu or by hitting CMD-SHFT-D. This will result in a new layer entitled "[filename] copy" with an identical thumbnail to the [filename] layer.<li>Under the Colours menu, choose "Saturation".<li>This will popup a dialog with options, check the "Split View" and adjust the line to the main part of the image to see how it will look.<li>Set the saturation to 0. You'll have to tab away to see your results in the image.<li>Save this setting for later use if you're happy with it.<li>Now the image should look like the second one linked -- all colour removed. Have a cup of coffee<li>Back now? Good... We will now add grain to the image. Under the "Filters" menu, choose "Noise" and "RGB Noise".<li>Unclick "Independent RGB" and adjust the value to suit, watching the preview after tabbing away from the field.<li>When you're done, you should have something looking like my third linked image.</ul>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Marin Home37.8982182 -122.50031119.5879843638211568 -157.6565611 66.208452036178841 -87.3440611tag:blogger.com,1999:blog-6157408210125261684.post-59823005028063802262020-05-24T20:48:00.000-07:002020-05-24T20:48:14.640-07:00How I Do Data Science<p>At a high level , a data scientist does one of 3 things. Given a time series, we want to determine one of three things:<ol type="1"><li/>What happened before?<li/>What the values were in the middle?<li/>What comes afterwards?</ol>To estimate all three, data must be fit into an equation. I just set up a <a href="https://hd1-units.herokuapp.com/tests">web service</a> where you can post a delimited list of numbers, walk away, have it come up with histograms of the various fits and be notified by email when it's ready -- look for the fit tests for sample calls. The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com3tag:blogger.com,1999:blog-6157408210125261684.post-46616197977028157032020-02-21T18:26:00.000-08:002020-02-21T18:26:04.814-08:00How to Predict the Stock Market?<p>I just completed a <a href="https://gist.github.com/hasandiwan/6c8335c27edc615af78d4595765b0d86">script</a> that leverages two of my Json endpoints on <a href="https://hd1-units.herokuapp.com/">units</a> to make stock market predictions. The script is open-sourced on that gist and forms part of my <a href="https://raw.githubusercontent.com/hasandiwan/hd1-units/master/src/test/java/us/d8u/balance/Tests.java?token=AKSFUTKVTYD4EBUTYW2FSVS6KCISI">test suite</a> for the project.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com1tag:blogger.com,1999:blog-6157408210125261684.post-17630781512270010412019-12-21T11:30:00.000-08:002019-12-24T07:08:36.833-08:00How to Reverse-Geocode Addresses for Free<p>I just added functionality to <a href="https://hd1-units.herokuapp.com">units</a> to reverse-geocode addresses. My test method for this code follows:<br/><br/>
<code lang="java">
<pre>
@Test
public void geocodeWorks() {
Map<String, String> expected = new Gson().fromJson(
"[{\"request\":\"2017 North Beverly Glen Blvd,
Los Angeles, CA 90077, USA\",\"displayName\":\"Emerging Market Services, 2017,
North Beverly Glen Boulevard, Beverly Glen, Westwood, Los Angeles, Los Angeles
County, California, 90077, United States of America\",\"latitude\":\"34.1056855
\",\"source\":\"\",\"longitude\":\"-118.4461788\"}]",
new TypeToken<List<Map<String, String>>>() {
}.getType());
StringMap actual = this.restTemplate.postForObject("/geocode",
Collections.singletonMap("addr",
"2017 North Beverly Glen Blvd,
Los Angeles, CA 90077, USA"),
StringMap.class);
Assert.assertEquals(expected, actual);
}
</pre></code>
There's no rate limit on this API (for now), so use it to your heart's content. And you can view the up-to-date <a href="https://bitbucket.org/hd1/jpassword/raw/master/src/test/java/us/d8u/balance/Tests.java">test code</a>, as they evolve here.
The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Barcelona, Spain41.3850639 2.173403499999949441.1944764 1.8506799999999495 41.5756514 2.4961269999999494tag:blogger.com,1999:blog-6157408210125261684.post-8070793095701941222019-12-10T07:15:00.000-08:002019-12-10T07:15:17.416-08:00 'Staggering' New Data Shows Income Of Top 1% Has Grown 100 Times Faster Than Bottom 50% Since 1970 <p>I found a time series regarding <a href="https://commondreams.org">common dreams</a> varying income classes share of GDP and <a href="https://imgur.com/a/A65De1r">visualized</a> it, in an, I feel, more illustrative way than the <a href="https://www.commondreams.org/news/2019/12/09/staggering-new-data-shows-income-top-1-has-grown-100-times-faster-bottom-50-1970">original visualization</a>. I'd appreciate your feedback on making my visualization clearer.</p><pre>
income <- read.csv('/tmp/income.csv', header=TRUE, row.names=NULL)
colnames(income) <- c('Year', '0-50pct', '50-90pct', '90-99pct', '99-99.9pct', '99.9-99.99pct', '99.99-100pct')
ggplot(income, aes(x=Year)) + geom_line(aes(y=income[,2]), colblue')
</pre>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Carrer d'en Roca, 9, 08002 Barcelona, Spain41.382111712436327 2.173149592359891441.381608712436325 2.1720765923598915 41.382614712436329 2.1742225923598912tag:blogger.com,1999:blog-6157408210125261684.post-78915612463084064512019-12-09T03:34:00.000-08:002019-12-09T19:33:19.999-08:00How to Script the Web For Free<div>This weekend, I decided to revive units functionality to parse HTML to JSON and put it up <a href="https://hd1-balance.herokuapp.com/json?page=https%3A%2F%2Fbitbucket.org%2Fhd1%2Fjpassword%2Fraw%2F509351e3cd1f08c0df546942d381804f5ad4d6f9%2Fsrc%2Fmain%2Fresources%2Fpublic%2Ftest.html">here</a>. Will enhance it further shortly, by <strike>adding an error message and usage instructions</strike>. Knock yourselves out and let me know if you encounter any issues. </div>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0Home41.3850639 2.173403499999949441.1944764 1.8506799999999495 41.5756514 2.4961269999999494tag:blogger.com,1999:blog-6157408210125261684.post-43455447000434197482019-11-13T02:43:00.002-08:002019-11-13T13:27:30.511-08:00How to visualize a user's most Recent Subreddits<script src="https://gist.github.com/hasandiwan/391e9cc31c73b1f580992e8531ef2d89.js"></script>
The above script lets you see which subreddits a given reddit user has been posting in over their last 100 submissions -- comments and posts. You'll need matplotlib, pandas, and requests to use it. Enjoy!The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-6011011561460883812019-05-10T20:02:00.001-07:002019-05-10T20:02:56.127-07:00How to Use Gamlss with Ggplot2<p>I was exploring <a href="https://gamlss.org">Gamlss</a> and seeing if I could plot the resulting models with <a href="https://ggplot2.tidyverse.org">ggplot2</a>. While a gamlss model is not, strictly speaking, coercible to data.frame, nor is there a fortify method included, I wrote a quick method and am providing it here, gratis: fortify.gamlss <- function(gamlss.obj) { return(data.frame(x=c(1:length(gamlss.obj$y)), y=gamlss.obj$y)) }</p>
<p>I would appreciate it if <a href="http://gamlss.com">those</a> <a href="http://had.co.nz">responsible</a> would incorporate this into the tree. Many thanks!</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-42554570316876609902019-03-26T21:43:00.001-07:002019-03-26T21:43:34.854-07:00How I wrote my own CI System<div>This evening, while playing with <a href="https://whereishasan.herokuapp.com">whereishasan</a>, my upstream ISP connection went down. Diagnosing the problem, I had no idea -- all my tests were failing and I had no idea why -- unbound was fine -- it had the server in its cache, the local network was working fine, yet the remote host was frozen. So, I rebooted the router. Then waited, waited, waited, as it flushed its cache and decided to do its DHCP handshake. Then everything was fine -- I updated the location, and my tests passed again. To prevent this in the future, I whipped up a cronjob to run my unit tests and email me when they failed using <a href="http://caspian.dotconf.net/menu/Software/SendEmail/">sendEmail</a>. Slotted it into the daily cron on my local server, ran a few tests and, happy it worked, I relaxed. Now to figure out how to make it run on push and kill the process if tests fail.</div>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-87092933742814258642019-02-05T23:21:00.002-08:002019-02-05T23:21:51.941-08:00Substrings Working on sqlTime<p>Subject has the tl;dr. Here are the details. I wanted to be able to type <tt>sqlTime Madrid</tt> and get the local time. However, the actual argument I needed to use for this was <tt>Europe/Madrid</tt>. That's a bit annoying.</p><p>So, as with most of my passion projects, annoyance is the mother of invention. First things first, how would I handle this. <a href="http://www.regex.info">Regular expressions</a> came to mind. Shortly thereafter <a href="https://www.pcre.org">PCRE</a> popped into my head. This library provides <a href="http://cpan.perl.org">Perl</a>-compatible regular expressions in C. It's <a href="https://vcs.pcre.org/pcre2/">open source</a> and its <a href="https://www.pcre.org/licence.txt">license</a> is acceptable. However, I determined it to be too heavy to incorporate for what is only implementing substring matching.</p><p>Then I found that <a href="http://www.postgresql.org">postgres</a>, which the system is built on, supports a <a href="https://www.postgresql.org/docs/9.3/functions-matching.html">plethora of functions for matching strings</a>. Problem solved?</p><p>Perhaps not, I then tried the <a href="https://www.freebsd.org/cgi/man.cgi?query=regex">regex</a> library from 4.4 BSD, which is to be found everywhere I'd use this setup. Again, it was overkill.</p><p>Finally, I had <i>un ballon du rouge</i> in me and went back to first principles. What did I aim to accomplish here? Essentially I wanted to allow the city name instead of having to specify the continent. Since the city name would certainly be in the timezone name, I needed a substring function. <tt>man -k substring</tt> on my system yielded <tt>strstr, strcasestr, strnstr, strcasestr_l(3) - locate a substring in a string</tt>. <tt>char *strcasestr(const char *big, const char *little);</tt> is the provided prototype for the case insensitive substring function; strstr is the case sensitive one. And the functions <tt>locates the first occurrence of the null-terminated string little in the null-terminated string big.</tt> and return a pointer to the start of the substring and null if it's not found. A few if statements later and <tt>sqlTime Madrid</tt> was returning the correct output. I then committed the code, updated the <a href="https://gist.github.com/hasandiwan/caca847fce9c034643e02e653eaaff7b">gist</a>, wrote this post, and decided to get to bed. If you'd like to try it out, leave me your platform and I'll send you a binary.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-50488446846539964792019-01-15T01:02:00.001-08:002019-01-15T01:02:43.585-08:00How to know what time it is<p>I've been working on a <a href="https://gist.github.com/hasandiwan/caca847fce9c034643e02e653eaaff7b">world clock</a> over the past week and I've finally got it to the point where I feel like I can release it. Sample runs follow and the source is linked to above:
<pre>
% ../sqlTime
Africa/Abidjan, 00:00:00
Africa/Accra, 00:00:00
Africa/Addis_Ababa, 03:00:00
Africa/Algiers, 01:00:00
Africa/Asmara, 03:00:00
Africa/Asmera, 03:00:00
Africa/Bamako, 00:00:00
Africa/Bangui, 01:00:00
Africa/Banjul, 00:00:00
Africa/Bissau, 00:00:00
Africa/Blantyre, 02:00:00
Africa/Brazzaville, 01:00:00
Africa/Bujumbura, 02:00:00
Africa/Cairo, 02:00:00
Africa/Casablanca, 01:00:00
Africa/Ceuta, 01:00:00
Africa/Conakry, 00:00:00
Africa/Dakar, 00:00:00
Africa/Dar_es_Salaam, 03:00:00
Africa/Djibouti, 03:00:00
Africa/Douala, 01:00:00
Africa/El_Aaiun, 01:00:00
Africa/Freetown, 00:00:00
Africa/Gaborone, 02:00:00
Africa/Harare, 02:00:00
Africa/Johannesburg, 02:00:00
Africa/Juba, 03:00:00
Africa/Kampala, 03:00:00
Africa/Khartoum, 02:00:00
Africa/Kigali, 02:00:00
Africa/Kinshasa, 01:00:00
Africa/Lagos, 01:00:00
Africa/Libreville, 01:00:00
Africa/Lome, 00:00:00
Africa/Luanda, 01:00:00
Africa/Lubumbashi, 02:00:00
Africa/Lusaka, 02:00:00
Africa/Malabo, 01:00:00
Africa/Maputo, 02:00:00
Africa/Maseru, 02:00:00
Africa/Mbabane, 02:00:00
Africa/Mogadishu, 03:00:00
Africa/Monrovia, 00:00:00
Africa/Nairobi, 03:00:00
Africa/Ndjamena, 01:00:00
Africa/Niamey, 01:00:00
Africa/Nouakchott, 00:00:00
Africa/Ouagadougou, 00:00:00
Africa/Porto-Novo, 01:00:00
Africa/Sao_Tome, 01:00:00
Africa/Timbuktu, 00:00:00
Africa/Tripoli, 02:00:00
Africa/Tunis, 01:00:00
Africa/Windhoek, 02:00:00
America/Adak, -10:00:00
America/Anchorage, -09:00:00
America/Anguilla, -04:00:00
America/Antigua, -04:00:00
America/Araguaina, -03:00:00
America/Argentina/Buenos_Aires, -03:00:00
America/Argentina/Catamarca, -03:00:00
America/Argentina/ComodRivadavia, -03:00:00
America/Argentina/Cordoba, -03:00:00
America/Argentina/Jujuy, -03:00:00
America/Argentina/La_Rioja, -03:00:00
America/Argentina/Mendoza, -03:00:00
America/Argentina/Rio_Gallegos, -03:00:00
America/Argentina/Salta, -03:00:00
America/Argentina/San_Juan, -03:00:00
America/Argentina/San_Luis, -03:00:00
America/Argentina/Tucuman, -03:00:00
America/Argentina/Ushuaia, -03:00:00
America/Aruba, -04:00:00
America/Asuncion, -03:00:00
America/Atikokan, -05:00:00
America/Atka, -10:00:00
America/Bahia, -03:00:00
America/Bahia_Banderas, -06:00:00
America/Barbados, -04:00:00
America/Belem, -03:00:00
America/Belize, -06:00:00
America/Blanc-Sablon, -04:00:00
America/Boa_Vista, -04:00:00
America/Bogota, -05:00:00
America/Boise, -07:00:00
America/Buenos_Aires, -03:00:00
America/Cambridge_Bay, -07:00:00
America/Campo_Grande, -03:00:00
America/Cancun, -05:00:00
America/Caracas, -04:00:00
America/Catamarca, -03:00:00
America/Cayenne, -03:00:00
America/Cayman, -05:00:00
America/Chicago, -06:00:00
America/Chihuahua, -07:00:00
America/Coral_Harbour, -05:00:00
America/Cordoba, -03:00:00
America/Costa_Rica, -06:00:00
America/Creston, -07:00:00
America/Cuiaba, -03:00:00
America/Curacao, -04:00:00
America/Danmarkshavn, 00:00:00
America/Dawson, -08:00:00
America/Dawson_Creek, -07:00:00
America/Denver, -07:00:00
America/Detroit, -05:00:00
America/Dominica, -04:00:00
America/Edmonton, -07:00:00
America/Eirunepe, -05:00:00
America/El_Salvador, -06:00:00
America/Ensenada, -08:00:00
America/Fort_Nelson, -07:00:00
America/Fort_Wayne, -05:00:00
America/Fortaleza, -03:00:00
America/Glace_Bay, -04:00:00
America/Godthab, -03:00:00
America/Goose_Bay, -04:00:00
America/Grand_Turk, -05:00:00
America/Grenada, -04:00:00
America/Guadeloupe, -04:00:00
America/Guatemala, -06:00:00
America/Guayaquil, -05:00:00
America/Guyana, -04:00:00
America/Halifax, -04:00:00
America/Havana, -05:00:00
America/Hermosillo, -07:00:00
America/Indiana/Indianapolis, -05:00:00
America/Indiana/Knox, -06:00:00
America/Indiana/Marengo, -05:00:00
America/Indiana/Petersburg, -05:00:00
America/Indiana/Tell_City, -06:00:00
America/Indiana/Vevay, -05:00:00
America/Indiana/Vincennes, -05:00:00
America/Indiana/Winamac, -05:00:00
America/Indianapolis, -05:00:00
America/Inuvik, -07:00:00
America/Iqaluit, -05:00:00
America/Jamaica, -05:00:00
America/Jujuy, -03:00:00
America/Juneau, -09:00:00
America/Kentucky/Louisville, -05:00:00
America/Kentucky/Monticello, -05:00:00
America/Knox_IN, -06:00:00
America/Kralendijk, -04:00:00
America/La_Paz, -04:00:00
America/Lima, -05:00:00
America/Los_Angeles, -08:00:00
America/Louisville, -05:00:00
America/Lower_Princes, -04:00:00
America/Maceio, -03:00:00
America/Managua, -06:00:00
America/Manaus, -04:00:00
America/Marigot, -04:00:00
America/Martinique, -04:00:00
America/Matamoros, -06:00:00
America/Mazatlan, -07:00:00
America/Mendoza, -03:00:00
America/Menominee, -06:00:00
America/Merida, -06:00:00
America/Metlakatla, -09:00:00
America/Mexico_City, -06:00:00
America/Miquelon, -03:00:00
America/Moncton, -04:00:00
America/Monterrey, -06:00:00
America/Montevideo, -03:00:00
America/Montreal, -05:00:00
America/Montserrat, -04:00:00
America/Nassau, -05:00:00
America/New_York, -05:00:00
America/Nipigon, -05:00:00
America/Nome, -09:00:00
America/Noronha, -02:00:00
America/North_Dakota/Beulah, -06:00:00
America/North_Dakota/Center, -06:00:00
America/North_Dakota/New_Salem, -06:00:00
America/Ojinaga, -07:00:00
America/Panama, -05:00:00
America/Pangnirtung, -05:00:00
America/Paramaribo, -03:00:00
America/Phoenix, -07:00:00
America/Port-au-Prince, -05:00:00
America/Port_of_Spain, -04:00:00
America/Porto_Acre, -05:00:00
America/Porto_Velho, -04:00:00
America/Puerto_Rico, -04:00:00
America/Punta_Arenas, -03:00:00
America/Rainy_River, -06:00:00
America/Rankin_Inlet, -06:00:00
America/Recife, -03:00:00
America/Regina, -06:00:00
America/Resolute, -06:00:00
America/Rio_Branco, -05:00:00
America/Rosario, -03:00:00
America/Santa_Isabel, -08:00:00
America/Santarem, -03:00:00
America/Santiago, -03:00:00
America/Santo_Domingo, -04:00:00
America/Sao_Paulo, -02:00:00
America/Scoresbysund, -01:00:00
America/Shiprock, -07:00:00
America/Sitka, -09:00:00
America/St_Barthelemy, -04:00:00
America/St_Johns, -03:30:00
America/St_Kitts, -04:00:00
America/St_Lucia, -04:00:00
America/St_Thomas, -04:00:00
America/St_Vincent, -04:00:00
America/Swift_Current, -06:00:00
America/Tegucigalpa, -06:00:00
America/Thule, -04:00:00
America/Thunder_Bay, -05:00:00
America/Tijuana, -08:00:00
America/Toronto, -05:00:00
America/Tortola, -04:00:00
America/Vancouver, -08:00:00
America/Virgin, -04:00:00
America/Whitehorse, -08:00:00
America/Winnipeg, -06:00:00
America/Yakutat, -09:00:00
America/Yellowknife, -07:00:00
Antarctica/Casey, 08:00:00
Antarctica/Davis, 07:00:00
Antarctica/DumontDUrville, 10:00:00
Antarctica/Macquarie, 11:00:00
Antarctica/Mawson, 05:00:00
Antarctica/McMurdo, 13:00:00
Antarctica/Palmer, -03:00:00
Antarctica/Rothera, -03:00:00
Antarctica/South_Pole, 13:00:00
Antarctica/Syowa, 03:00:00
Antarctica/Troll, 00:00:00
Antarctica/Vostok, 06:00:00
Arctic/Longyearbyen, 01:00:00
Asia/Aden, 03:00:00
Asia/Almaty, 06:00:00
Asia/Amman, 02:00:00
Asia/Anadyr, 12:00:00
Asia/Aqtau, 05:00:00
Asia/Aqtobe, 05:00:00
Asia/Ashgabat, 05:00:00
Asia/Ashkhabad, 05:00:00
Asia/Atyrau, 05:00:00
Asia/Baghdad, 03:00:00
Asia/Bahrain, 03:00:00
Asia/Baku, 04:00:00
Asia/Bangkok, 07:00:00
Asia/Barnaul, 07:00:00
Asia/Beirut, 02:00:00
Asia/Bishkek, 06:00:00
Asia/Brunei, 08:00:00
Asia/Calcutta, 05:30:00
Asia/Chita, 09:00:00
Asia/Choibalsan, 08:00:00
Asia/Chongqing, 08:00:00
Asia/Chungking, 08:00:00
Asia/Colombo, 05:30:00
Asia/Dacca, 06:00:00
Asia/Damascus, 02:00:00
Asia/Dhaka, 06:00:00
Asia/Dili, 09:00:00
Asia/Dubai, 04:00:00
Asia/Dushanbe, 05:00:00
Asia/Famagusta, 02:00:00
Asia/Gaza, 02:00:00
Asia/Harbin, 08:00:00
Asia/Hebron, 02:00:00
Asia/Ho_Chi_Minh, 07:00:00
Asia/Hong_Kong, 08:00:00
Asia/Hovd, 07:00:00
Asia/Irkutsk, 08:00:00
Asia/Istanbul, 03:00:00
Asia/Jakarta, 07:00:00
Asia/Jayapura, 09:00:00
Asia/Jerusalem, 02:00:00
Asia/Kabul, 04:30:00
Asia/Kamchatka, 12:00:00
Asia/Karachi, 05:00:00
Asia/Kashgar, 06:00:00
Asia/Kathmandu, 05:45:00
Asia/Katmandu, 05:45:00
Asia/Khandyga, 09:00:00
Asia/Kolkata, 05:30:00
Asia/Krasnoyarsk, 07:00:00
Asia/Kuala_Lumpur, 08:00:00
Asia/Kuching, 08:00:00
Asia/Kuwait, 03:00:00
Asia/Macao, 08:00:00
Asia/Macau, 08:00:00
Asia/Magadan, 11:00:00
Asia/Makassar, 08:00:00
Asia/Manila, 08:00:00
Asia/Muscat, 04:00:00
Asia/Nicosia, 02:00:00
Asia/Novokuznetsk, 07:00:00
Asia/Novosibirsk, 07:00:00
Asia/Omsk, 06:00:00
Asia/Oral, 05:00:00
Asia/Phnom_Penh, 07:00:00
Asia/Pontianak, 07:00:00
Asia/Pyongyang, 09:00:00
Asia/Qatar, 03:00:00
Asia/Qyzylorda, 06:00:00
Asia/Rangoon, 06:30:00
Asia/Riyadh, 03:00:00
Asia/Saigon, 07:00:00
Asia/Sakhalin, 11:00:00
Asia/Samarkand, 05:00:00
Asia/Seoul, 09:00:00
Asia/Shanghai, 08:00:00
Asia/Singapore, 08:00:00
Asia/Srednekolymsk, 11:00:00
Asia/Taipei, 08:00:00
Asia/Tashkent, 05:00:00
Asia/Tbilisi, 04:00:00
Asia/Tehran, 03:30:00
Asia/Tel_Aviv, 02:00:00
Asia/Thimbu, 06:00:00
Asia/Thimphu, 06:00:00
Asia/Tokyo, 09:00:00
Asia/Tomsk, 07:00:00
Asia/Ujung_Pandang, 08:00:00
Asia/Ulaanbaatar, 08:00:00
Asia/Ulan_Bator, 08:00:00
Asia/Urumqi, 06:00:00
Asia/Ust-Nera, 10:00:00
Asia/Vientiane, 07:00:00
Asia/Vladivostok, 10:00:00
Asia/Yakutsk, 09:00:00
Asia/Yangon, 06:30:00
Asia/Yekaterinburg, 05:00:00
Asia/Yerevan, 04:00:00
Atlantic/Azores, -01:00:00
Atlantic/Bermuda, -04:00:00
Atlantic/Canary, 00:00:00
Atlantic/Cape_Verde, -01:00:00
Atlantic/Faeroe, 00:00:00
Atlantic/Faroe, 00:00:00
Atlantic/Jan_Mayen, 01:00:00
Atlantic/Madeira, 00:00:00
Atlantic/Reykjavik, 00:00:00
Atlantic/South_Georgia, -02:00:00
Atlantic/St_Helena, 00:00:00
Atlantic/Stanley, -03:00:00
Australia/ACT, 11:00:00
Australia/Adelaide, 10:30:00
Australia/Brisbane, 10:00:00
Australia/Broken_Hill, 10:30:00
Australia/Canberra, 11:00:00
Australia/Currie, 11:00:00
Australia/Darwin, 09:30:00
Australia/Eucla, 08:45:00
Australia/Hobart, 11:00:00
Australia/LHI, 11:00:00
Australia/Lindeman, 10:00:00
Australia/Lord_Howe, 11:00:00
Australia/Melbourne, 11:00:00
Australia/NSW, 11:00:00
Australia/North, 09:30:00
Australia/Perth, 08:00:00
Australia/Queensland, 10:00:00
Australia/South, 10:30:00
Australia/Sydney, 11:00:00
Australia/Tasmania, 11:00:00
Australia/Victoria, 11:00:00
Australia/West, 08:00:00
Australia/Yancowinna, 10:30:00
Brazil/Acre, -05:00:00
Brazil/DeNoronha, -02:00:00
Brazil/East, -02:00:00
Brazil/West, -04:00:00
CST6CDT, -06:00:00
Canada/Atlantic, -04:00:00
Canada/Central, -06:00:00
Canada/Eastern, -05:00:00
Canada/Mountain, -07:00:00
Canada/Newfoundland, -03:30:00
Canada/Pacific, -08:00:00
Canada/Saskatchewan, -06:00:00
Canada/Yukon, -08:00:00
Chile/Continental, -03:00:00
Chile/EasterIsland, -05:00:00
EST5EDT, -05:00:00
Egypt, 02:00:00
Etc/GMT, 00:00:00
Etc/GMT+0, 00:00:00
Etc/GMT+1, -01:00:00
Etc/GMT+10, -10:00:00
Etc/GMT+11, -11:00:00
Etc/GMT+12, -12:00:00
Etc/GMT+2, -02:00:00
Etc/GMT+3, -03:00:00
Etc/GMT+4, -04:00:00
Etc/GMT+5, -05:00:00
Etc/GMT+6, -06:00:00
Etc/GMT+7, -07:00:00
Etc/GMT+8, -08:00:00
Etc/GMT+9, -09:00:00
Etc/GMT-0, 00:00:00
Etc/GMT-1, 01:00:00
Etc/GMT-10, 10:00:00
Etc/GMT-11, 11:00:00
Etc/GMT-12, 12:00:00
Etc/GMT-13, 13:00:00
Etc/GMT-14, 14:00:00
Etc/GMT-2, 02:00:00
Etc/GMT-3, 03:00:00
Etc/GMT-4, 04:00:00
Etc/GMT-5, 05:00:00
Etc/GMT-6, 06:00:00
Etc/GMT-7, 07:00:00
Etc/GMT-8, 08:00:00
Etc/GMT-9, 09:00:00
Etc/GMT0, 00:00:00
Etc/Greenwich, 00:00:00
Etc/UCT, 00:00:00
Etc/UTC, 00:00:00
Etc/Universal, 00:00:00
Etc/Zulu, 00:00:00
Europe/Amsterdam, 01:00:00
Europe/Andorra, 01:00:00
Europe/Astrakhan, 04:00:00
Europe/Athens, 02:00:00
Europe/Belfast, 00:00:00
Europe/Belgrade, 01:00:00
Europe/Berlin, 01:00:00
Europe/Bratislava, 01:00:00
Europe/Brussels, 01:00:00
Europe/Bucharest, 02:00:00
Europe/Budapest, 01:00:00
Europe/Busingen, 01:00:00
Europe/Chisinau, 02:00:00
Europe/Copenhagen, 01:00:00
Europe/Dublin, 00:00:00
Europe/Gibraltar, 01:00:00
Europe/Guernsey, 00:00:00
Europe/Helsinki, 02:00:00
Europe/Isle_of_Man, 00:00:00
Europe/Istanbul, 03:00:00
Europe/Jersey, 00:00:00
Europe/Kaliningrad, 02:00:00
Europe/Kiev, 02:00:00
Europe/Kirov, 03:00:00
Europe/Lisbon, 00:00:00
Europe/Ljubljana, 01:00:00
Europe/London, 00:00:00
Europe/Luxembourg, 01:00:00
Europe/Madrid, 01:00:00
Europe/Malta, 01:00:00
Europe/Mariehamn, 02:00:00
Europe/Minsk, 03:00:00
Europe/Monaco, 01:00:00
Europe/Moscow, 03:00:00
Europe/Nicosia, 02:00:00
Europe/Oslo, 01:00:00
Europe/Paris, 01:00:00
Europe/Podgorica, 01:00:00
Europe/Prague, 01:00:00
Europe/Riga, 02:00:00
Europe/Rome, 01:00:00
Europe/Samara, 04:00:00
Europe/San_Marino, 01:00:00
Europe/Sarajevo, 01:00:00
Europe/Saratov, 04:00:00
Europe/Simferopol, 03:00:00
Europe/Skopje, 01:00:00
Europe/Sofia, 02:00:00
Europe/Stockholm, 01:00:00
Europe/Tallinn, 02:00:00
Europe/Tirane, 01:00:00
Europe/Tiraspol, 02:00:00
Europe/Ulyanovsk, 04:00:00
Europe/Uzhgorod, 02:00:00
Europe/Vaduz, 01:00:00
Europe/Vatican, 01:00:00
Europe/Vienna, 01:00:00
Europe/Vilnius, 02:00:00
Europe/Volgograd, 04:00:00
Europe/Warsaw, 01:00:00
Europe/Zagreb, 01:00:00
Europe/Zaporozhye, 02:00:00
Europe/Zurich, 01:00:00
GB-Eire, 00:00:00
GMT+0, 00:00:00
GMT-0, 00:00:00
Greenwich, 00:00:00
Hongkong, 08:00:00
Iceland, 00:00:00
Indian/Antananarivo, 03:00:00
Indian/Chagos, 06:00:00
Indian/Christmas, 07:00:00
Indian/Cocos, 06:30:00
Indian/Comoro, 03:00:00
Indian/Kerguelen, 05:00:00
Indian/Mahe, 04:00:00
Indian/Maldives, 05:00:00
Indian/Mauritius, 04:00:00
Indian/Mayotte, 03:00:00
Indian/Reunion, 04:00:00
Israel, 02:00:00
Jamaica, -05:00:00
Japan, 09:00:00
Kwajalein, 12:00:00
Libya, 02:00:00
MST7MDT, -07:00:00
Mexico/BajaNorte, -08:00:00
Mexico/BajaSur, -07:00:00
Mexico/General, -06:00:00
NZ-CHAT, 13:45:00
Navajo, -07:00:00
PST8PDT, -08:00:00
Pacific/Apia, 14:00:00
Pacific/Auckland, 13:00:00
Pacific/Bougainville, 11:00:00
Pacific/Chatham, 13:45:00
Pacific/Chuuk, 10:00:00
Pacific/Easter, -05:00:00
Pacific/Efate, 11:00:00
Pacific/Enderbury, 13:00:00
Pacific/Fakaofo, 13:00:00
Pacific/Fiji, 12:00:00
Pacific/Funafuti, 12:00:00
Pacific/Galapagos, -06:00:00
Pacific/Gambier, -09:00:00
Pacific/Guadalcanal, 11:00:00
Pacific/Guam, 10:00:00
Pacific/Honolulu, -10:00:00
Pacific/Johnston, -10:00:00
Pacific/Kiritimati, 14:00:00
Pacific/Kosrae, 11:00:00
Pacific/Kwajalein, 12:00:00
Pacific/Majuro, 12:00:00
Pacific/Marquesas, -09:30:00
Pacific/Midway, -11:00:00
Pacific/Nauru, 12:00:00
Pacific/Niue, -11:00:00
Pacific/Norfolk, 11:00:00
Pacific/Noumea, 11:00:00
Pacific/Pago_Pago, -11:00:00
Pacific/Palau, 09:00:00
Pacific/Pitcairn, -08:00:00
Pacific/Pohnpei, 11:00:00
Pacific/Ponape, 11:00:00
Pacific/Port_Moresby, 10:00:00
Pacific/Rarotonga, -10:00:00
Pacific/Saipan, 10:00:00
Pacific/Samoa, -11:00:00
Pacific/Tahiti, -10:00:00
Pacific/Tarawa, 12:00:00
Pacific/Tongatapu, 13:00:00
Pacific/Truk, 10:00:00
Pacific/Wake, 12:00:00
Pacific/Wallis, 12:00:00
Pacific/Yap, 10:00:00
Poland, 01:00:00
Portugal, 00:00:00
Singapore, 08:00:00
Turkey, 03:00:00
US/Alaska, -09:00:00
US/Aleutian, -10:00:00
US/Arizona, -07:00:00
US/Central, -06:00:00
US/East-Indiana, -05:00:00
US/Eastern, -05:00:00
US/Hawaii, -10:00:00
US/Indiana-Starke, -06:00:00
US/Michigan, -05:00:00
US/Mountain, -07:00:00
US/Pacific, -08:00:00
US/Samoa, -11:00:00
Universal, 00:00:00
posixrules, -05:00:00
% ../sqlTime CSTa
Invalid timezone CSTa
% ../sqlTime Europe/Madrid
2019-01-15 10:00:50.570864
</pre>
<p>In other words, ¡hola España!</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-91012357092836852292019-01-08T16:03:00.000-08:002019-01-08T16:03:05.737-08:00Python API Wrapper for CurrencyStack<p>In my never-ending quest to make the web digestible by machine, I encountered <a href="https://currencystack.io">currencystack</a> on <a href="https://producthunt.com">ProductHunt</a>. I noticed they have <a href="https://currencystack.io/docs">a JSON API</a>. I <a href="https://twitter.com/jscapitalLLC/status/1082349352171884552">tweeted them offering my services</a>. A rate was agreed upon, and a few hours later, <a href="https://bitbucket.org/hd1/currencystack/">it was done</a>. And I have another line on my CV.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com1tag:blogger.com,1999:blog-6157408210125261684.post-21414632867171940312018-12-05T12:14:00.000-08:002018-12-05T12:14:45.571-08:00How to Fix SSL Validation Errors on Heroku<div dir="ltr" style="text-align: left;" trbidi="on">Over the past few weeks, we started getting errors in regards to SSL on heroku postgresql. So, we disabled it and our tests are happy again.</div>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-29991529821714840252018-11-12T16:54:00.000-08:002018-11-12T16:54:21.180-08:00How to use SSL in Spring-boot and LetsEncrypt Easily<p>When developing <a href="https://hd1.bitbucket.io/geonotes.html">Geonotes</a>, I ran into the issue of <a href="https://developers.google.com/web/updates/2016/04/geolocation-on-secure-contexts-only">Chrome not allowing non-HTTPS access to geolocation</a>. The geonotes backend is Java, which generally implies that its robust, but needs to be tweaked a great deal for every use case -- read, not something I enjoy doing. After a brief look at <a href="https://www.google.com/search?q=ssl+spring+boot&oq=ssl+spring">available workarounds</a> and trying a few out, I wanted to pull my hair out, but found there wasn't much left to pull. In the back of my mind somewhere, I recalled there was a way to set up an HTTPS-to-HTTP proxy, thus avoiding the need to <a href="https://www.thomasvitale.com/https-spring-boot-ssl-certificate/">mess with keytool</a> and the rest of it, <a href="https://stunnel.org">stunnel</a>. Just needed to add a cronjob for <a href="https://certbot.eff.org/">certbot</a> to <a href="https://certbot.eff.org/docs/using.html#manual">renew certificates</a> and <a href="http://openssl.org">Openssl</a> commands to convert the issued certificate to one that <a href="https://docs.oracle.com/cd/B14098_01/core.1012/b13995/sslmid.htm">the J2ee middleware</a> likes. Without further ado, then.</p><p>Stunnel creates an proxy server on the localhost and handles the SSL to plaintext conversion. Indeed, the configuration is cake:<pre>
pid = /home/ec2-user/postgisweb/stunnel.pid
cert = /home/ec2-user/postgisweb/ssl.pem
[geonotes]
accept = 0.0.0.0:8443
connect = 127.0.0.1:8080
</pre></p><p>8080 is my HTTP port, and I want to run SSL on 8443. The wildcard IP is necessary because we want stunnel to listen on all IP addresses on the server, but want HTTP allowed only on localhost (else there's no point in SSL in the first place). The other interesting part is that ssl.pem is constructed using <tt>cat /usr/local/etc/letsencrypt/live/hasan.d8u.us/privkey.pem /usr/local/etc/letsencrypt/live/hasan.d8u.us/fullchain.pem | tee append ~ec2-user/postgisweb/ssl.pem</tt>. Stick the command in a cron job and have it run often -- I have mine set to every 3 hours.</p><p>We verify our work, by pointing our browser to port 8443 and checking if there are SSL issues. There are not. And we're set.</p>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com18tag:blogger.com,1999:blog-6157408210125261684.post-91969767007384689452018-10-18T02:20:00.000-07:002018-10-18T02:20:31.268-07:00How to Speed Up Diffbot<div><a href="https://diffbot.com">Diffbot</a> is an AI startup that provides knowledge as a service to power intelligent applications. As with most such services, its reach (by default) exceeds its grasp in terms of performance. <a href="https://fiskkit.com">We</a> use it to clean up articles that are added to our system, as do a <a href="https://www.diffbot.com/showcase/">number of others</a>. We found it slow -- indeed when I took it out, processing time improved by almost 50%. In this piece, I will share what the magic parameters I found are to accomplish this. Hopefully, you will also find it useful.</div><div>I stumbled upon a <a href="http://support.diffbot.com/automatic-apis/improving-api-performance-and-response-times/">support article</a> entitled <i>Improving API Response Times</i>, from which these parameters are sourced. Setting the <em>discussion</em> parameter to <strong>false</strong> will remove parsing of any comments or reviews. The second (and key) use case is the <em>paging</em> parameter. Setting this to false downloads only the first page of a multipage article.</div><div>A site like Fiskkit can not use the first page of a multipage article, but your use case may not require it. Enjoy, and if you'd like to hire me, I'm always <a href="https://hd1.bitbucket.io/CV.pdf">available for contract work</a>.</div>The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com0tag:blogger.com,1999:blog-6157408210125261684.post-72376779933470292422018-09-10T15:21:00.000-07:002018-09-10T15:21:56.481-07:00How to Sort Java-generated JSON<div dir="ltr" style="text-align: left;" trbidi="on">
I was looking for a way to sort JSON by key. Why? It's easier to read and locate if you know it's order is consistent. I'm using <a href="https://www.baeldung.com/jackson">Jackson</a> as my parser, in <a href="https://spring.io/">Spring boot</a>. There are several ways I'm able to see to accomplish this:<br />
<ul type="*">
<li>Spring has an <a href="https://docs.spring.io/spring-boot/docs/1.1.2.RELEASE/reference/html/common-application-properties.html">application.properties/application.yml</a> setting -- http.mappers.json-sort-keys -- which does not work with Jackson.</li>
<li>Jackson's ObjectMapper may be configured to <a href="https://github.com/FasterXML/jackson-databind/wiki/Serialization-features">sort keys</a> using the ORDER_MAP_ENTRIES_BY_KEYS property. This worked, but there has got to be an easier way to dot this, which is... </li>
<li>Java itself has a TreeMap, whose default key comparator is lexical, meaning that you get this functionality out of the box. This is the approach I ended up using.</li>
</ul>
</div>
The Prolific Programmerhttp://www.blogger.com/profile/09631681950518688098noreply@blogger.com1