999Running Repetitive Jobs on OSX

Traditionally Unix systems use crontab to schedule and run repetitive jobs, OSX uses launchd, an init and operating system service management daemon.

Here is a script that call my index.php every 120 seconds. (I am download snow webcams in the index.php.)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>org.trembl.snow</string>
  <key>ServiceDescription</key>
  <string>Getting Snow Pictures</string>
  <key>ProgramArguments</key>
  <array>
    <string>/Applications/XAMPP/bin/php</string>
    <string>/Applications/XAMPP/xamppfiles/htdocs/snow/index.php</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>StartInterval</key>
  <integer>120</integer>
</dict>
</plist>

In Xcode, the same script looks like this:

Troubleshooting

I made a couple of errors, trying to get the script to run.

  1. Permissions sudo chown <user>:staff org.trembl.snow.plist
    Incorrect permissions also cause the Load failed: 122: Path had bad ownership/permissions

  2. The name of the file and the Label need to match. org.trembl.snow and org.trembl.snow.plist

  3. The plist need to be placed into ~/Library/LaunchAgents.

  4. If your Program has arguments, they need to be specified in an array:

    <key>ProgramArguments</key>
    <array>
    <string>/Applications/XAMPP/bin/php</string>
    <string>/Applications/XAMPP/xamppfiles/htdocs/snow/index.php</string>
    </array>
  5. Both strings need to be absolute. I got this error: Load failed: 122: Path had bad ownership/permissions, which was caused by only saying index.php instead of the whole absolute path.

  6. Use launchctl bootstrap and not the obsolete launchctl load.

  7. Launch with sudo launchctl bootstrap gui/`id -u` ~/Library/LaunchAgents/org.trembl.snow.plist

  8. Stop: sudo launchctl bootout gui/`id -u` ~/Library/LaunchAgents/org.trembl.snow.plist

997Flask – [Errno 48] Address already in use

Problem:

Stopping the flask server with ⌃-Z (CRTL-Z), instead of the correct ⌃-C (CRTL-C), results in the server still bound to port 5000:

OSError: [Errno 48] Address already in use

Problem: After Flask crash, the port can still be occupied.

Solution:

Check who is using port 5000

sudo lsof -i:5000
kill $PID
kill -9 $PID

Success:

[2]  + killed     flask run

996Playing/Pausing Embedded Vimeo Videos with custom JS

Embedded Vimeo in HTML Page:

<iframe id="vi" src="https://player.vimeo.com/video/123"></iframe>

Pause:

var msg = JSON.stringify({method: "pause"}

Play:

var msg = JSON.stringify({method: "play"}

jQuery:

$("#vi")[0].contentWindow.postMessage(msg, "*")

Pure JS:

document.getElementById("vi").contentWindow.postMessage(msg, "*")

994Strategy for importing SQLite3 Database into MySQL

I am surprised that there is not a simple import function in mysql or phpMyAdmin, I did the following:

  • In SQLite3, export tables as CSV
  • In phpMyAdmin, create DB, and import table as CSV

Make sure you check the box that converst the first line into the table structure.

Table Size/Execution Time Limit

When importing a larger table (in my case ~300M), the phpMyAdmin was complaining about memory (which I fixed), but then the script time out.

Solution? Import directly from mysql

Open the XAMPP mysql binary:

cd /Applications/XAMPP/xamppfiles/bin 
./mysql -u root -p
LOAD DATA LOCAL INFILE '/path/to/table.csv' 
INTO TABLE mydb.mytable FIELDS TERMINATED 
BY ',' ENCLOSED BY '"' LINES TERMINATED 
BY '\n' IGNORE 1 LINES;

(Line breaks are added for legibility.)

  • mydb.mytable are mydb name and mytable table
  • IGNORE 1 LINES ignores the first line with the headers

992Returning output of print_r in PHP

Use it to capture output and print it in one go. For example when debugging server function with an ajax call.

$message = "hello!\n";

$a = array(
  'one' => 1,
  'two' => 2
);

$message .= print_r($a, true);

print_r($output);

prints:


/*
hello!
Array
        (
            [one] => 1
            [two] => 2
)
*/

990Typing non-breaking spaces in OSX

In HTML the non-breaking space is evoked with &nbsp;

If you can not use &nbsp; and you need a nbsp type Option + Space.

Non-breaking Space as shown in Textmate

988Reset Local Git Repo

git reset --hard origin/main

main is the name your main branch.

986Displaying Text Data in a New Window

Problem: Data is stored in an array, you can not reload the page, you need to export it from the JS console.

Solution:

Create a New Window

var w = window.open()

Add a basic HTML header

w.document.writeln("<html><body><pre>")

Add your data

Assuming your data lives in an array called data, and has a sub-array with 2 values.

data.forEach(d => { w.document.writeln(d[0] + ", " + d[1])  })

Add a CSV Header

w.document.writeln("timestamp, temp")

Clear

w.document.body.innerHTML = "";

985Customising Pagination in WordPress

Getting and Customising Pagination:

function getPagination() {
  global $wp_query;
  $a = paginate_links(array(
    'current' => max(1, get_query_var('paged')),
    'total' => $wp_query->max_num_pages,
    'type' => 'array',
    'aria_current' => false,
    'show_all' => true,
    'end_size' => 2,
    'mid_size' => 2,
    'prev_next' => true,
    'prev_text' => '<div class="pg_arrow left"></div>',
    'next_text' => '<div class="pg_arrow right"></div>',
  ));
  if (empty($a)) return false;
  foreach($a as &$l) {
    $l = str_replace('prev page-numbers', 'pg_item', $l);
    $l = str_replace('next page-numbers', 'pg_item', $l);
    $l = str_replace('page-numbers', 'pg_item pg_num', $l);
    $l = str_replace(' aria-current=""', '', $l);
    $l = str_replace('span', 'a', $l);
    $l = str_replace('current', 'active', $l);
  }
  return $a;
}

Calling:

if spaces are needed in front

foreach(getPagination() as $p) {
  echo "   " . $p . "\n";
}

no spaces

echo implode("\n", getPagination());

984WordPress – Getting $post from blog home

Problem:

ACFs in sidebar-top.php in home.php are not displaying. $post is set to the first Post, rather than the home.php page.

Solution:

Check is is_home(), assign $post to get_option('page_for_posts').

if (is_home()) {
  $post = get_option('page_for_posts');
}