Half-Elf on Tech

Thoughts From a Professional Lesbian

Author: Ipstenu (Mika Epstein)

  • Null and Zero

    Null and Zero

    This is an amusing anecdote.

    When I was working on my cross-check of shows and characters, I got everything working right except one thing. I noticed my count of ‘shows with death’ made a weird pie chart. You see, the number of shows was off by one compared to the total number of shows. I went back and forth, trying to figure out why it was doing that, and in the end I decided that I’d output the list and manually count to see what I was missing.

    I counted, by hand, the shows that I got with the list of ‘all characters are dead’ and that number matched what the code output. Then I subtracted that from the number of ‘shows with any death’ to get the ‘some characters are dead’ count, and that too was okay. This meant that, for some reason, the list of shows where no one died was wrong. But it was subtraction! How could it be wrong!

    Well as it turns out, I forgot that null and zero aren’t the same to a computer, but they tend to be to a human’s brain.

    Now I know this! I know that zero is a number, it’s a value of the known quantity of zero. And I know that ‘null’ is a non value, meaning that it’s a data point that cannot yet be known.

    You can’t math on null. And I knew this. Earlier in my code I’d written $foo = 0 outside of my loop where I check and, as needed, increment it with $foo++ specifically because you can’t add to null.

    Nothing from nothing, carry the nothing…

    But. In my calculations, I checked the following:

    1. If a show has been flagged with ‘death,’ count all the characters.
    2. For each character, if they’re dead, add 1 to the death count.
    3. If the number of characters is the same as the death count, add this show to the list of ‘kill ’em all.’
    4. If the number of characters is greater than the number of dead, and the value of either isn’t 0, add the show to the list of ‘kill some.’

    Then, separately, I checked “If the show has not been flagged with death, add the show to the ‘kill none’ list, because that’s a 1/0 check. And when you don’t think too deeply about that, it sounds fine, right? If they aren’t marked with death they must kill none. And if they don’t kill them all, and they don’t kill some, then the number should be the same as the ‘kill none’ list.

    The problem was that I had one show, just one, where all the characters were alive but it had been flagged with death. This was not an error. The show did kill off a character, but I’d not added the dead character yet. Which meant it ran through the checks like this:

    1. Flagged with death, we count all the characters (8).
    2. If the character is dead, add one to death count (death count is 0).
    3. If the number of characters (8) is the same as the dead (0), add to ‘kill them all’ (false).
    4. If the number of characters (8) is more than the dead (0) (true), add the value of either isn’t 0 (false), add them to ‘kill some.’ (false).

    Which isn’t wrong at all. It just meant that the show didn’t get listed on ‘kill none’ because it was flagged with dead, and it didn’t get listed on ‘kill them all’ because 8 is more than 0, and it didn’t get listed on ‘kill some’ because while 8 is more than 0, dead was 0.

    Oh silly me. The value of death characters was ‘null.’ They simply didn’t exist in a place where they should have.

    The fix? I added the dead character to the site and everything was fixed. But code wise, could I prevent this? I certainly should, since I know better than many that you can’t predict what users are going to do. So it begs the question of why was I checking if character count and dead count were not 0 to begin with? It was, originally, because my ‘no deaths’ list was screwed up and I threw that check in to omit cases where a show was flagged as death but didn’t have death.

    The accuracy of these things depends entirely on your data. Garbage in, garbage out. The best fix would be to check ‘if a show has death and it has no dead characters, adjust the dead count down by one’ but also ‘if a show has no death, but it has dead characters, adjust the live show count down by one.’ I haven’t done that yet. I will soon.

  • Combining Data from Multiple CPTs

    Combining Data from Multiple CPTs

    I wanted to get a list of all TV shows where 100% of the listed characters were dead.

    @Ipstenu … Did I just read that you’re using WordPress to compose a list of dead lesbians in media? I have to say, that’s kind of unique.
    Otto42

    Yes, yes I am. Television media, excluding reality TV.

    The problem is I store the information in two Custom Post Types (shows and characters). And while both shows and characters have a way to track if there are dead, getting that list was frustratingly complicated.

    We wanted, originally, a way to mark a show as having death and a separate way to track each dead character. Since they were separate CPTs, we made two custom taxonomies: dead-character and death. Logically then, I could use WP Query to get a list of all shows with the cliche taxonomy field of ‘death’ checked:

    $dead_shows_query = new WP_Query ( array(
    	'post_type'       => 'post_type_shows',
    	'posts_per_page'  => -1,
    	'post_status'     => array( 'publish', 'draft' ),
    	'tax_query' => array(
    		array(
    			'taxonomy' => 'cliches',
    			'field'    => 'slug',
    			'terms'    => 'death',
    			),
    		),
    	)
    );
    

    I know I could use a different way to get terms, but I don’t want to since I need to count these shows:

    $count_dead_shows = $dead_shows_query->post_count;
    

    But I also need to continue processing. Once I have a list of shows with death, I need to get a list of all characters who are on the show. That data is stored as Custom Meta Data and those don’t have a quick and easy sort method. Worse, the shows are listed as an array.

    Originally it was just a number representing the post ID for the show, and that was pretty easy to check. While we have posts in the dead show query, get a list of all characters with this ‘show ID’ which looks like this:

    	while ( $dead_shows_query->have_posts() ) {
    		$dead_shows_query->the_post();
    		$show_id = $post->ID;
    
    		$character_death_loop = new WP_Query( array(
    			'post_type'       => 'post_type_characters',
    			'post_status'     => array( 'publish', 'draft' ),
    			'orderby'         => 'title',
    			'order'           => 'ASC',
    			'posts_per_page'  => '-1',
    			'meta_query'      => array( array(
    				'key'     => 'the_show',
    				'value'   => $show_id,
    				'compare' => '=',
    			),),
    		) );
    
    		if ($character_death_loop->have_posts() ) {
    			[... do all the magic here ...]
    			wp_reset_query();
    		}
    	}
    

    The problem is that compare line. Once you stop looking for “Does 123 == 123” you have to use ‘IN’ or ‘LIKE’ and, since this is an array of show IDs, we need 'compare' => 'LIKE' for this check. That sounds simple, but there’s one more small problem. If the show ID is 1, then every single show with a 1 in it shows up. In addition, I actually wanted to get a list of all the shows where some characters died, so I couldn’t just check for characters with the meta_query of the show and the tax_query of death.

    My first step in all this was to convert the shows to an array:

    if ( !is_array (get_post_meta($post->ID, 'lezchars_show', true)) ) {
    	$shows_array = array( get_post_meta($post->ID, 'lezchars_show', true) );
    } else {
    	$shows_array = get_post_meta($post->ID, 'lezchars_show', true);
    }
    

    Now I can process everything as an array and check if the show ID is really in the array. If it is, we’re going to record that there is a character in a show with death.

    if ( in_array( $show_id, $shows_array ) ) {
    	$chardeathcount++;
    }
    

    Next we’re going to check if the character has the tag ‘dead-character’ (listed as a custom taxonomy for ‘show cliches’) or not and is in the show:

    if ( has_term( 'dead-character', 'cliches', $post->ID ) && in_array( $show_id, $shows_array ) ) {
    	$fulldeathcount++;
    }
    

    Once we have that data, I need two arrays. The first is for shows where the count of all characters on the show is the same as the ones who are dead. The second is for shows where some characters are dead:

    if ( $fulldeathcount == $chardeathcount ) {
    	$fullshow_death_array[$show_id] = array(
    		'url'    => get_permalink( $show_id ),
    		'name'   => get_the_title( $show_id ),
    		'status' => get_post_status( $show_id ),
    	);
    } elseif ( $fulldeathcount > '0' && $fulldeathcount <= $chardeathcount ) {
    	$someshow_death_array[$show_id] = array(
    		'url'    => get_permalink( $show_id ),
    		'name'   => get_the_title( $show_id ),
    		'status' => get_post_status( $show_id ),
    	);
    

    The full code for that magical snippage looks like this:

    			$fulldeathcount = 0;
    			$chardeathcount = 0;
    
    			while ($character_death_loop->have_posts()) {
    				$character_death_loop->the_post();
    
    				if ( !is_array (get_post_meta($post->ID, 'lezchars_show', true)) ) {
    					$shows_array = array( get_post_meta($post->ID, 'lezchars_show', true) );
    				} else {
    					$shows_array = get_post_meta($post->ID, 'lezchars_show', true);
    				}
    
    				if ( in_array( $show_id, $shows_array ) ) {
    					$chardeathcount++;
    				}
    
    				if ( has_term( 'dead', 'tropes', $post->ID ) && in_array( $show_id, $shows_array ) ) {
    					$fulldeathcount++;
    				}
    			}
    
    			if ( $fulldeathcount == $chardeathcount ) {
    				$fullshow_death_array[$show_id] = array(
    					'url'    => get_permalink( $show_id ),
    					'name'   => get_the_title( $show_id ),
    					'status' => get_post_status( $show_id ),
    				);
    			} elseif ( $fulldeathcount >= '1' && $fulldeathcount <= $chardeathcount ) {
    				$someshow_death_array[$show_id] = array(
    					'url'    => get_permalink( $show_id ),
    					'name'   => get_the_title( $show_id ),
    					'status' => get_post_status( $show_id ),
    				);
    			}
    

    And yes, it works just fine.

  • Because Developers Never Typo

    Because Developers Never Typo

    It’s not a problem. Only admins can use this.

    I’d pushed back on a plugin that wasn’t validating their post input wisely. Instead they just slapped sanitize_text_field() around everything and called it a day.

    One of the myriad reasons I’ll push back on a plugin is improper sanitization. When I say that, I mean they need to sanitize, validate, and escape the input. If I see things like update_option('my_cool_options', $_POST['my_cool_input']); I’ll push back and tell them to please sanitize. But really I tell them this:

    SANITIZE: All instances where generated content is inserted into the database, or into a file, or being otherwise processed by WordPress, the data MUST be properly sanitized for security. By sanitizing your POST data when used to make action calls or URL redirects, you will lessen the possibility of XSS vulnerabilities. You should never have a raw data inserted into the database, even by a update function, and even with a prepare() call.

    VALIDATE: In addition to sanitization, you should validate all your calls. If a $_POST call should only be a number, ensure it’s an int() before you pass it through anything. Even if you’re sanitizing or using WordPress functions to ensure things are safe, we ask you please validate for sanity’s sake. Any time you are adding data to the database, it should be the right data.

    ESCAPE: Similarly, when you’re outputting data, make sure to escape it properly, so it can’t hijack admin screens. There are many esc_*() functions you can use to make sure you don’t show people the wrong data.

    I say it often. Sanitize everything (but especially what you save or process), validate input, escape output.

    I understand though, why someone might naively assume that since only admins can do a thing, it’s ‘safer.’ The truth is admins screw up as much as anyone else. Worse, probably, since admins have more power and often think they know better.

    But the point I was trying to make to this guy was that it doesn’t matter who is inputting the data.

    I’ve told this story before. I used to have a job testing software packages for software I didn’t use. We replied on ‘scripts’ from people to know what to test. One day, John C and I were trying to test some new software and every time we hit a certain screen, we’d crash the box. We tried over and over and it failed. So John called the vendor and explained what we were doing. They walked us through it and it crashed. Since we were a VIP, they said they’d send over a couple developers. When the two guys showed up, one watched us very carefully and was shocked.

    The young dev exclaimed, “Why would you ever input wrong data there?!”

    I eyeballed him. “Why would putting in wrong data crash the computer?”

    The older dev chuckled. “We’ll put in an error check there.”

    The lesson I learned is simple. Never trust input.

  • Obviously Oblivious

    Obviously Oblivious

    So I was asked this…

    @binarygary: .‭@Ipstenu‬ I know you aren’t the patent office…but have you ever declined a plugin for “obviousness”?

    I replied, not in jest, that I had declined some for obliviousness.

    Gary’s plugin was obvious in that it did exactly what it was purported to do, and it did it well. And it had a puny name. I quite liked it and approved it pretty quickly. But the crux of the question is interesting. Have I ever rejected plugins for being too obvious?

    Not that I can remember. In fact, I kind of love the plugins that do exact what they claim to do. Like Rickroll which turns your videos into Rickroll or Logout Roulette which gives you a one in ten chance of being logged out. Those are simple and obvious and a little stupid, but they’re perfectly obvious and only the oblivious would be confused.

    An obvious plugin is possibly the best kind of plugin. My buddy Otto once remarked that well named functions don’t need documentation. If you have a function named reset_the_loop it’s pretty obvious what it does. Now you might need to add some docblox to explain how to use it and what parameters you can pass through to it, but in and of itself, it resets the loop!

    On the flip side, here’s rts_reset which is actually a function I saw recently in a plugin. Okay, what does that do? It was in a class named RTSCode which didn’t help at all. And there was no inline documentation. After a while, I traced everything back and sorted out what the heck the code was doing (it was reseting a query, but only if you passed specific params back).

    In general, a WordPress theme and plugin should be painfully obvious. It’s open source, it’s code anyone can look at, and yes, it should be stupid easy to understand what it does and why. That’s the meaning behind ‘open’ in so many ways. Open for everyone. Open to be forked and learned from and studied and made more perfect. Or at least more fun.

    I like obvious things. Obvious is good.

  • More Chart.js Fun

    More Chart.js Fun

    The statistics page on my TV database site is pretty fun. It’s the one I learned how to use Chart.js for in the first place.

    I wanted to add in some pie charts, but first I upgraded the code to the 2.0-beta and refined the PHP on my stats. Originally I just had some pretty basic bar charts for category statistics. Now I have some pie charts to show characters by role (by which I mean are they a main character, a guest, or a recurring character).

    Better Category Stats

    Let’s talk about some better PHP first. The stuff I had before worked, but it could have been better. Here’s the better PHP:

    $count_characters = wp_count_posts( 'post_type_characters' )->publish + wp_count_posts( 'post_type_characters' )->draft ;
    $tropes = get_terms('chartags');
    
    $trope_terms_array = array();
    foreach ( $tropes as $trope ) {
    	$trope_terms_array[$trope->slug] = array( 'count' => $trope->count, 'name'  => $trope->name, 'url' => get_term_link( $trope ) );
    }
    

    What I did here was take the data and make a single array for it which gives me the data structured as follows:

    Array
    (
        [addict] => Array
            (
                [count] => 2
                [name] => Addict
                [url] => http://example.com/tropes/addict/
            )
    
        [athlete] => Array
            (
                [count] => 3
                [name] => Athlete
                [url] => http://example.com/tropes/athlete/
            )
    )
    

    This makes my array much smaller and simpler to run through. Next I changed how I call the data in my javascript:

    	labels : [<?php
    		$unset = array('foo','bar','baz');
    		foreach ( $unset as $item ) {
    			unset($trope_terms_array[$item]);
    		}
    
    		foreach ( $trope_terms_array as $trope ) {
    			echo '"'.$trope['name'].'", ';
    		}
    	?>],
    

    The unsetting at the top is a quick run to remove the tropes I don’t need for this chart because I’m displaying them in the pie chart. See? It all comes together!

    Pie No. 1 – Sexuality

    There are two pie charts. One is checking how many characters are gay, straight, or bisexual. I’m sure eventually I’ll be add asexual, but that isn’t today. Anyway, that chart is surprisingly simple. Since I’d already improved the PHP call for category stats, and sexuality is saved as a character taxonomy, I was able to do this simply as follows:

    <script>
    // Piechart for sexuality stats
    var pieSexdata = {
        labels: [
            "Gay",
            "Straight",
            "Bisexual"
        ],
        datasets: [
            {
                data: [ <?php echo '
    	            "'.( $count_characters - $trope_terms_array['straight']['count'] - $trope_terms_array['bisexual']['count'] ).'",
    	            "'.$trope_terms_array['straight']['count'].'",
    	            "'.$trope_terms_array['bisexual']['count'].'"
    	            '; ?>],
                backgroundColor: [
                    "#7d3255",
                    "#327A7D",
                    "#32557D"
                ],
                hoverBackgroundColor: [
                    "#B18499",
                    "#BCD4D5",
                    "#ABB9CA"
                ]
            }]
    };
    
    var ctx = document.getElementById("pieSex").getContext("2d");
    var pieSex = new Chart(ctx,{
        type:'doughnut',
        data: pieSexdata,
        options: {}
    });
    </script>
    

    The default assumption is that any character being added is a homosexual. The reason ‘straight’ is there is for a character who was presented as gay, but that turned out to be a fantasy sequence. Thanks, Roseanne. With that in mind, calculating the number of gay characters was a matter of subtracting the straight and bisexual. And yes, I named the chart pieSexdata on purpose.

    Pie No. 2 – Character Role

    The second pie chart was a lot harder. You see, I’d chosen to save the ‘role’ as a custom meta field in the post. There’s a dropdown for ‘Main’ or ‘Recurring’ or ‘Guest’ and it defaults to ‘None’ if you don’t fill it out. Right now everyone has a role but I coded in a failsafe.

    This code took me a while to sort out, but as soon as I realized how simple it was, I made a loop so I didn’t have to repeat code:

    $roles = array( 'regular', 'recurring', 'guest' );
    $roles_array = array();
    foreach ( $roles as $role ) {
    	$args = array(
    		'post_type'       => 'post_type_characters',
    		'posts_per_page'  => -1,
    		'post_status'     => array( 'publish', 'draft' ),
    		'meta_query'      => array(
    			array(
    				'key'     => 'chars_type',
    				'value'   => $role,
    				'compare' => '=',
    			),
    		),
        );
    	$thisrole = new WP_Query($args);
    	$roles_array[$role] = $thisrole->post_count;
    }
    

    This produces a nice array:

    Array
    (
        [regular] => 147
        [recurring] => 37
        [guest] => 23
    )
    

    I wanted it to be an array since I can see this expanding sooner or later. The pie chart code looks very much the same as the one for sexuality, and all that’s really different is how I’m calling the data and doing the math for how many characters have no listed role.

        labels: [
            "Main Character",
            "Recurring Character",
            "Guest Character",
            "No Role"
        ],
        datasets: [
            {
                data: [ <?php echo '
    	            "'.$roles_array['regular'].'",
    	            "'.$roles_array['recurring'].'",
    	            "'.$roles_array['guest'].'",
    	            "'.( $count_characters - $roles_array['guest'] - $roles_array['recurring'] - $roles_array['regular'] ).'",
    	            '; ?>],
    

    What’s Next?

    Things are shaping up nicely, but I want to add in better labels. I’d like if they show the percentage when you hover over them on pie charts, and if they could link to the taxonomy pages for the bar charts. But I haven’t quite sorted out how to do that yet.

    I also have to blame Tracy for this, because she’s the one who wanted stats like that in the first place.

  • Fauxgo and Rickroll

    Fauxgo and Rickroll

    April Fools is today. I hate April Fools ‘jokes’ as pretty much all of them are cruel.

    Now that we have that out of the way, I’d like to tell you about two plugins I created that are pretty much useless but educational.

    Rickroll

    Released in the WordPress.org plugin directory, this plugin changes all your videos to the official Rickroll video. All. Your. Videos. The point it was made for is that you really can run a filter to do some pretty impressive things with videos. Including replace them. You can take the logic of this plugin and apply it by filtering (say) all videos embedded in comments made by a specific person. That would really mess with them. Or you could possibly call YouTube’s api, check the rating of the video, and if it’s over a certain amount, show a default instead.

    Download: WordPress.org – Rickroll

    Fauxgo

    This plugin replaces the WordPress logo with a Fauxgo. Why? Someone complained to me that you couldn’t rebrand WordPress entirely. So I did. The thing about this plugin was that it was deucedly complicated to make work right. Most of the trouble was the CSS is ‘weird.’ But once you have it installed, everything looks different ‘ish’ and suddenly you’re not sure what the logo is anymore.

    I plan to edit this and write it as using an SVG icon instead of the icon font.

    Download: Github – Fauxgo