Author Archives: Tom Woodward

RVArts.org Version II

RVArts.org Version II

Lots of remakes going on lately. That’s good in that it means people still want to work with me (now our group) after doing it once and secondly they’re seeing things to change and improve which is how things get better. The harder part to figure out is that in you never really finish anything so you have to keep that partial snowball effect in mind as you figure out how much work you can take on. Or I suggest you do. I at least pretend I do but really if it’s cool and interesting enough I just say yes.1

RVArts.org Version II
So anyway, the old RVArts site was decent but it was an early work in terms of my capacity as someone who makes websites.2

This remake is a mix of technical and visual changes. Technically . . .

This remake is about harvesting the content students create in a Facebook group and then using it with different material students create in WordPress. Right now the data is pulled from the Facebook group events into WordPress through the Event Aggregator which ties into Event Calendar Pro. Later in the game we opted to jump events right back to Facebook so we could have skipped all that and just used the FB API but sometimes you find that stuff out too late. There are also a variety of weird things in Facebook that are hard to decipher- like the different behavior between groups and pages with events, the way internally vs externally created events work, and the display of events in the group based on where they originated. I was not and am not a fan.

Step One – custom page template for the home page

<?php
/**
 * Template Name: Full Width Home Page
 *
 * Template for displaying a page without sidebar even if a sidebar widget is published.
 *
 * @package understrap
 */
get_header();
$container = get_theme_mod( 'understrap_container_type' );
?>
<h1 id="events">Upcoming Events</h1>
<div class="wrapper" id="date-wrapper">
	<div class="container" id="content">
          <div class="row" id="dayContent">
          </div>        
          <div class="row" id="monthContent">
          </div>
          <div class="row" id="otherContent">
          </div>
    </div>

</div><!-- Wrapper end -->

<?php get_footer(); ?>

That’s the simple shell that the javascript is going to populate. There’s a handy JSON API from the Events Calendar which I can grab at https://rvarts.org/wp-json/tribe/events/v1/events?per_page=20.

fetch(
  "https://rvarts.org/wp-json/tribe/events/v1/events?per_page=20"
)
  .then(function(response) {
  // Convert to JSON
  return response.json();
})
  .then(function(data) {
  // GOOD!
  data = data.events;
  for (i = 0; i < data.length; i++) {
   //console.log(data[i].title);
   var destination = getDestination(data[i]);
    writeEvents(data[i], destination);
  } 
}).then(function(){
  colorRand(); 
});

That brings me the data and runs it through various functions which I’ll break down below. I should probably do this without a for loop but I just haven’t taken the time to go the other route yet.3

This little chunk makes our squares.

function writeEvents(data, destination) {
var match =  classDate(data); //set our size
  var post = jQuery(destination).append(
    jQuery(
      '<div id="' +
      data.id +
      '" class="event '+classDate(data)+ '"' +
      '><a href="'+data.website+'"><div class="event-bg h-100"><div class="card event-content h-100"><img src="'+featuredImg(data)+'" alt="'+data.title+'"><h2 class="card-title event-title">' +
      data.title +
      '</h2>'+theDate(data)+'</div></div></a></div>' 
    ) 
  );
}

This little bit decides in which div we want to put them.

function getDestination (data) {
   var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  if (thisMonth+thisDay == month+day ){
      return '#dayContent'//the this day box
      }
  if (thisMonth == month) {
    return '#monthContent'//the month box
  } else {
    return '#otherContent'//the not this day or this month box
  }
}

This piece writes out the date and moves the time from 24 style to 12 hr am/pm.

function theDate(data){
  var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var year = data.start_date_details.year;
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  var hour = data.start_date_details.hour;
  var meridian = 'AM'
  if (hour > 12){
    hour = hour -12;
    meridian = 'PM';
  }
  var minute = data.start_date_details.minutes;
  return '<div class="date">'+ month + '/' + day + '@' + hour + ':' +minute + meridian + "</div>";
}

An additional function to add bootstrap elements depending on the date.

function classDate(data){
  var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  if (thisMonth+thisDay == month+day ){
      return 'col-md-12'
      }
  if (thisMonth == month) {
    return 'col-md-6'
  } else {
    return 'col-md-4'
  }
  
}

This little piece randomly assigns a class to give the different color patterns at the bottom of each element.

function colorRand(){
  var colors = ['black','blue','green','pink','red','yellow'];
  var dates = document.querySelectorAll('.card'); 
  for (i = 0; i < dates.length; i++){
     var color = colors[Math.floor(Math.random()*colors.length)];
     dates[i].classList.add(color);
  }
}

That’s most of them but it’s all in one piece on Github here.

The visual also changed quite a bit. Matt came up with the design there with some input from John Freyer and Jill Ware (faculty members) and Kim (VCU student) came up with a brand guide. I particularly like the animation on hover with the hashed shadow background. It’s a lot cleaner and more fun than the older version.


1 Insert insane laughter here.

2 I still hesitate to use titles like web developer. I don’t think it’s imposter syndrome but I just see how big everything is. Plus I don’t really want that box (however big).

3 Programming feels a lot like eating health. Stuff is changing all the time and you end up with a vague sense of guilt about extra stuff you have’t done yet.

RVArts.org Version II

RVArts.org Version II

Lots of remakes going on lately. That’s good in that it means people still want to work with me (now our group) after doing it once and secondly they’re seeing things to change and improve which is how things get better. The harder part to figure out is that in you never really finish anything so you have to keep that partial snowball effect in mind as you figure out how much work you can take on. Or I suggest you do. I at least pretend I do but really if it’s cool and interesting enough I just say yes.1

RVArts.org Version II
So anyway, the old RVArts site was decent but it was an early work in terms of my capacity as someone who makes websites.2

This remake is a mix of technical and visual changes. Technically . . .

This remake is about harvesting the content students create in a Facebook group and then using it with different material students create in WordPress. Right now the data is pulled from the Facebook group events into WordPress through the Event Aggregator which ties into Event Calendar Pro. Later in the game we opted to jump events right back to Facebook so we could have skipped all that and just used the FB API but sometimes you find that stuff out too late. There are also a variety of weird things in Facebook that are hard to decipher- like the different behavior between groups and pages with events, the way internally vs externally created events work, and the display of events in the group based on where they originated. I was not and am not a fan.

Step One – custom page template for the home page

<?php
/**
 * Template Name: Full Width Home Page
 *
 * Template for displaying a page without sidebar even if a sidebar widget is published.
 *
 * @package understrap
 */
get_header();
$container = get_theme_mod( 'understrap_container_type' );
?>
<h1 id="events">Upcoming Events</h1>
<div class="wrapper" id="date-wrapper">
	<div class="container" id="content">
          <div class="row" id="dayContent">
          </div>        
          <div class="row" id="monthContent">
          </div>
          <div class="row" id="otherContent">
          </div>
    </div>

</div><!-- Wrapper end -->

<?php get_footer(); ?>

That’s the simple shell that the javascript is going to populate. There’s a handy JSON API from the Events Calendar which I can grab at https://rvarts.org/wp-json/tribe/events/v1/events?per_page=20.

fetch(
  "https://rvarts.org/wp-json/tribe/events/v1/events?per_page=20"
)
  .then(function(response) {
  // Convert to JSON
  return response.json();
})
  .then(function(data) {
  // GOOD!
  data = data.events;
  for (i = 0; i < data.length; i++) {
   //console.log(data[i].title);
   var destination = getDestination(data[i]);
    writeEvents(data[i], destination);
  } 
}).then(function(){
  colorRand(); 
});

That brings me the data and runs it through various functions which I’ll break down below. I should probably do this without a for loop but I just haven’t taken the time to go the other route yet.3

This little chunk makes our squares.

function writeEvents(data, destination) {
var match =  classDate(data); //set our size
  var post = jQuery(destination).append(
    jQuery(
      '<div id="' +
      data.id +
      '" class="event '+classDate(data)+ '"' +
      '><a href="'+data.website+'"><div class="event-bg h-100"><div class="card event-content h-100"><img src="'+featuredImg(data)+'" alt="'+data.title+'"><h2 class="card-title event-title">' +
      data.title +
      '</h2>'+theDate(data)+'</div></div></a></div>' 
    ) 
  );
}

This little bit decides in which div we want to put them.

function getDestination (data) {
   var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  if (thisMonth+thisDay == month+day ){
      return '#dayContent'//the this day box
      }
  if (thisMonth == month) {
    return '#monthContent'//the month box
  } else {
    return '#otherContent'//the not this day or this month box
  }
}

This piece writes out the date and moves the time from 24 style to 12 hr am/pm.

function theDate(data){
  var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var year = data.start_date_details.year;
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  var hour = data.start_date_details.hour;
  var meridian = 'AM'
  if (hour > 12){
    hour = hour -12;
    meridian = 'PM';
  }
  var minute = data.start_date_details.minutes;
  return '<div class="date">'+ month + '/' + day + '@' + hour + ':' +minute + meridian + "</div>";
}

An additional function to add bootstrap elements depending on the date.

function classDate(data){
  var today = new Date();
  var thisMonth = today.getMonth()+1;
  var thisDay = today.getDate()+1;
  //get event date
  var month = data.start_date_details.month;
  var day = data.start_date_details.day;
  if (thisMonth+thisDay == month+day ){
      return 'col-md-12'
      }
  if (thisMonth == month) {
    return 'col-md-6'
  } else {
    return 'col-md-4'
  }
  
}

This little piece randomly assigns a class to give the different color patterns at the bottom of each element.

function colorRand(){
  var colors = ['black','blue','green','pink','red','yellow'];
  var dates = document.querySelectorAll('.card'); 
  for (i = 0; i < dates.length; i++){
     var color = colors[Math.floor(Math.random()*colors.length)];
     dates[i].classList.add(color);
  }
}

That’s most of them but it’s all in one piece on Github here.

The visual also changed quite a bit. Matt came up with the design there with some input from John Freyer and Jill Ware (faculty members) and Kim (VCU student) came up with a brand guide. I particularly like the animation on hover with the hashed shadow background. It’s a lot cleaner and more fun than the older version.


1 Insert insane laughter here.

2 I still hesitate to use titles like web developer. I don’t think it’s imposter syndrome but I just see how big everything is. Plus I don’t really want that box (however big).

3 Programming feels a lot like eating health. Stuff is changing all the time and you end up with a vague sense of guilt about extra stuff you have’t done yet.

ANTH101 v 3.ish

ANTH101 v 3.ish

“You cannot think your way into a new way of living. You have to live your way into a new way of thinking.”
– Mike Wesch

You can’t beat that quote as a way to frame a course and it’s nice to consider how digital content supports that kind of perspective on learning/living. It’s also a key consideration in how I think about building courses like this. You have to do it. You shouldn’t expect to be perfect the first time, or the second, or ever really but if you’re doing it right improving it should be worth the investment. You should get some joy out of the process and it should alleviate things that cause you pain.

I’ve had the opportunity to work with Mike Wesch and Ryan Klataske at Kansas State over the last few years1 on the ANTH101 site. It’s been an interesting progression over time as the course has continued to evolve. We’ve gone done a variety of paths and dealt with human and technical issues.

It has been interesting to participate in the ongoing co-evolution of aesthetics, mechanics, and content. It’s also a scenario where I wish I’d have done a much better job with screenshots so I could more accurately show you how the site has evolved.2

After an initial meeting at Kansas State (our first meeting), we’ve done all of this work through a mix of video calls and Google Docs. You can see the editing stats on one of the Google Docs we used below. Lots of edits, lots of comments, and a decent span of time for one document.

The course is meant to be used with a large number of students. In today’s world that usually means teaching assistants running smaller groups. The intent was also to use this across institutions. In both cases you’re working extra hard to limit support issues. In this case we don’t have a centralized IT group for support and we couldn’t easily do single sign on between institutions. We did a number of things to try to make life easier. We tried simplifying the backend. We moved posts to the front-end and automated, limited, or forced categories, featured images etc. It definitely improved things but support for teachers and students remained an issue.

The other major issue was the number of students. It was meant to be a large enrollment course and so we end up with a very large number of authors writing to the database during fairly short windows of time. That always creates issues with WordPress (and any other system I’ve used). We struggled with this and were moving towards a setup similar to the way we manage rampages– that is a multisite installation with a sharded database with cohorts of authors writing at the site level. That ended up getting derailed because it was going to take another big chunk of serious infrastructure re-working and wouldn’t solve other some other support problems.

So the big question became whether we could move the student portion to another service. We took a look at many things and ended up coming back around to Instagram. I know. I have feelings about the service and Facebook and all that stuff but it’s also a tool currently in use by most of the students already. It made sense in a number of ways and outsourced a chunk of support issues to an external service so we went with that. Part of that migration process was using Site Sucker on the gigantic old site to create an archive which ended up being about 11GB and 167,000 files. As you might imagine this took a good bit of time to run.

Making this move necessitated thinking through the things we needed to enable in the new environment. The more traditional content (Lessons, Challenges) pages would be relatively straight forward but the clan/group page that was tied into BuddyPress was one of those things that needed custom work. It gave the smaller cohorts a nice way to see just their cohort and enabled the leader of the group to communicate directly with that group. Since we were cutting out BuddyPress and the student content was going to be created elsewhere we needed to work out how to do this. Instagram has taken actions to make their API much more of a hassle to work with even for simply reading public data. As a result, we opted to go with the AccessPress Instagram Feed Pro plugin. That gave us some options to display content by username or hashtag without getting API key approval etc. This plugin creates shortcodes but the parameters are held in the database and the ID of the element is the only thing that’s visible in the shortcode. So you’re not doing something like [insta user=”tom”] or [insta tag=”foo”] but [insta id=”3″]. The other complication here was that Mike likes to have more flexibility in the authoring process so we were also working with the shortcodes created by Visual Composer. Some of these shortcodes are very odd.

The mechanization of this process became somewhat involved as a result. Any time you’re working with multiple shortcode plugins things become more involved.

Step One: Get the User Information

We opted to initiate the course page creation through the user signup process. To create a course page you needed to register for the WordPress site. That would enable us to get information about the course hashtag and the faculty user Instagram account to use as variables in the page creation. The developer version of Gravity Forms gives you a nice way to automate user registration and tie in various additional user metadata fields. You can see the preferences interface for that below.
ANTH101 v 3.ish

In order to make those fields visible in the user profile view for normal users I added the following via the plugin. I opted to do all of this through a plugin rather than altering the functions.php file of the theme. The hope was that it would be fairly portable in case the theme changed later. I doubt that it really makes much difference one way or another.

//MAKE USER META VISIBLE & HIDE SOME OTHER STUFF
function modify_user_meta_methods($profile_fields) {

  // Add new fields
  $profile_fields['faculty_insta'] = 'Faculty Instagram Account';//add stuff
  $profile_fields['insta_hash'] = 'Course Instagram Hashtag';
  $profile_fields['course_name'] = 'Course Name';
  $profile_fields['school'] = 'School';
  $profile_fields['visible'] = 'Visible';
  $profile_fields['course_img'] = 'Course Image';
  //$profile_fields['clan_page'] = 'Clan Page ID'; would make the id visible and editable - nice for testing but not needed
  unset($profile_fields['aim']);//remove stuff
  unset($profile_fields['yim']);
  unset($profile_fields['jabber']);
  unset($profile_fields['url']);


  return $profile_fields;
}
add_filter('user_contactmethods', 'modify_user_meta_methods');

This essentially gives us all the variables we need to create a custom course page. Now we need to make the page and merge in the variables.

Mike made a template that looked the way he wanted and had the desired functionality. The view source of that looked like so . . .

[vc_row full_width="stretch_row_content td-stretch-content"][vc_column][tdm_block_column_title tds_title="tds_title3" title_text="JTIza2Fwc2NsYW4=" title_size="tdm-title-bg" tds_title2-title_color="#8224e3" tds_title2-line_height="10" content_align_horizontal="content-horiz-center" tds_title3-subtitle_text="Ms. Shearer's Anthropology Class"][vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column][td_block_24 limit="1" custom_title=""][td_block_2 offset="1"][/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]

<hr />

<h3><span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from Ms. Shearer</span></h3>
[ap_instagram_feed_pro id="26"]

<hr />

<h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #Kapsclan Students</span></h3>
[ap_instagram_feed_pro id="27"]

[/vc_column_text][/vc_column][/vc_row]

You can see a combination of the the ap_instagram_feed_pro shortcodes and the vc_ Viscual composer shortcodes. You might note some odd things about the VC element in the first chunk- in particular title_text=”JTIza2Fwc2NsYW4=”. It’s some sort of encoding of regular text but I never did figure out what the encoding method was. In any case, it added some hassle at a point when I wasn’t in the mood for it. As a result, I went to the published post and copied out the HTML that shortcode created rather than spending more time messing with that nonsense. That is an example of either creative problem solving or laziness. Possibly both.

In any case, the post content of my course page now starts to look like this with some variables in there where I need them.

[vc_row full_width="stretch_row_content td-stretch-content"][vc_column]
<div class="vc_row td_uid_1_5a523c3ba9b10_rand  wpb_row td-pb-row"><div class="vc_column td_uid_2_5a523c3ba9d8b_rand  wpb_column vc_column_container tdc-column td-pb-span12"><div class="wpb_wrapper">
<div class="tdm_block td_block_wrap tdm_block_column_title td_uid_3_5a523c3ba9f60_rand tdm-content-horiz-center td-pb-border-top td_block_template_3" data-td-block-uid="td_uid_3_5a523c3ba9f60">
<div class="td-block-row"><div class="td-block-span12 tdm-col">
<div class="tds-title tds-title3  td-fix-index td_uid_4_5a523c3bab26c">
<h2 class="tdm-title tdm-title-bg clan-title">#' . $course_insta .'</h2>
<div class="tdm-title-line"></div><div class="tdm-title-sub">' . $course_title . '</div></div></div></div></div>
<div class="wpb_wrapper td_block_empty_space td_block_wrap vc_empty_space td_uid_5_5a523c3bab3dd_rand " style="height: 32px"></div></div></div></div>[vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column]<div class="anth-course">[td_block_2 autors_id="' . $faculty_ID . '"]</div>
[/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]<hr /><h3>
<span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from ' . $faculty_insta . '</span></h3>[ap_instagram_feed_pro id="'. $faculty_insta_id .'"]<hr /><h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #' . $course_insta . ' Students</span></h3>[ap_instagram_feed_pro id="' . $insta_hash_id .'"][/vc_column_text][/vc_column][/vc_row]

Now I have to do a few things to make this work. First, I have to duplicate the Instagram shortcode elements from the database, work in the new variables, and then insert them into the database. They’re pretty ugly looking things3 but we’re only changing a few variables and they’re just serialized arrays so that makes life easier. This is what they look like in the database in the wp_instagram_feeds table. Yuck.

a:166:{s:9:"feed_name";s:8:"KapsClan";s:9:"feed_type";s:3:"tag";s:34:"recent_media_blocked_caption_words";s:0:"";s:34:"recent_media_allowed_caption_words";s:0:"";s:17:"any_user_username";s:0:"";s:39:"any_user_username_blocked_caption_words";s:0:"";s:39:"any_user_username_allowed_caption_words";s:0:"";s:8:"tag_name";s:8:"kapsclan";s:25:"tag_blocked_caption_words";s:0:"";s:25:"tag_allowed_caption_words";s:0:"";s:32:"user_likes_blocked_caption_words";s:0:"";s:32:"user_likes_allowed_caption_words";s:0:"";s:7:"sort_by";s:4:"date";s:10:"image_size";s:19:"standard_resolution";s:20:"instagram_image_link";s:1:"1";s:19:"instagram_user_link";s:1:"1";s:23:"instagram_user_username";s:1:"1";s:10:"like_count";s:1:"1";s:13:"comment_count";s:1:"1";s:20:"counter_type_options";s:1:"3";s:15:"enable_lightbox";s:1:"1";s:15:"lightbox_layout";s:17:"apif_own_lightbox";s:12:"image_number";s:3:"200";s:18:"theme_accent_color";s:0:"";s:22:"hover_image_text_color";s:0:"";s:16:"instagram_mosaic";s:15:"masonry_layout5";s:25:"grid_layout_no_of_columns";s:10:"apif-col-4";s:28:"grid_layout_animation_effect";s:21:"apif-image-zoom-style";s:30:"grid_layout_animate_css_effect";s:9:"slideInUp";s:37:"grid_layout_load_more_button_position";s:11:"apif-center";s:33:"grid_layout_load_more_button_text";s:9:"Load More";s:39:"grid_layout_load_more_button_text_color";s:0:"";s:45:"grid_layout_load_more_button_background_color";s:0:"";s:41:"grid_layout_load_more_button_border_color";s:0:"";s:45:"grid_layout_load_more_button_hover_text_color";s:0:"";s:56:"grid_layout_load_more_button_text_hover_background_color";s:0:"";s:47:"grid_layout_load_more_button_hover_border_color";s:0:"";s:33:"grid_layout_load_more_button_icon";s:2:"14";s:29:"masonary_layout_no_of_columns";s:10:"apif-col-4";s:32:"masonary_layout_animation_effect";s:18:"apif-left-to-right";s:32:"masonary_layout_background_color";s:0:"";s:28:"masonary_layout_border_color";s:0:"";s:28:"masonary_layout_border_width";s:1:"0";s:34:"masonary_layout_animate_css_effect";s:8:"fadeInUp";s:41:"masonary_layout_load_more_button_position";s:11:"apif-center";s:37:"masonary_layout_load_more_button_text";s:9:"Load More";s:43:"masonary_layout_load_more_button_text_color";s:0:"";s:49:"masonary_layout_load_more_button_background_color";s:0:"";s:45:"masonary_layout_load_more_button_border_color";s:0:"";s:49:"masonary_layout_load_more_button_hover_text_color";s:0:"";s:60:"masonary_layout_load_more_button_text_hover_background_color";s:0:"";s:51:"masonary_layout_load_more_button_hover_border_color";s:0:"";s:37:"masonary_layout_load_more_button_icon";s:2:"14";s:30:"masonary_layout1_no_of_columns";s:10:"apif-col-4";s:33:"masonary_layout1_background_color";s:0:"";s:27:"masonary_layout1_text_color";s:0:"";s:29:"masonary_layout1_border_color";s:0:"";s:29:"masonary_layout1_border_width";s:0:"";s:35:"masonary_layout1_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout1_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout1_load_more_button_text";s:9:"Load More";s:44:"masonary_layout1_load_more_button_text_color";s:0:"";s:50:"masonary_layout1_load_more_button_background_color";s:0:"";s:46:"masonary_layout1_load_more_button_border_color";s:0:"";s:50:"masonary_layout1_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout1_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout1_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout1_load_more_button_icon";s:2:"14";s:30:"masonary_layout2_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout2_background_color";s:0:"";s:27:"masonary_layout2_text_color";s:0:"";s:29:"masonary_layout2_border_color";s:0:"";s:29:"masonary_layout2_border_width";s:0:"";s:35:"masonary_layout2_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout2_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout2_load_more_button_text";s:9:"Load More";s:44:"masonary_layout2_load_more_button_text_color";s:0:"";s:50:"masonary_layout2_load_more_button_background_color";s:0:"";s:46:"masonary_layout2_load_more_button_border_color";s:0:"";s:50:"masonary_layout2_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout2_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout2_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout2_load_more_button_icon";s:2:"14";s:30:"masonary_layout3_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout3_background_color";s:0:"";s:27:"masonary_layout3_text_color";s:0:"";s:29:"masonary_layout3_border_color";s:0:"";s:29:"masonary_layout3_border_width";s:0:"";s:35:"masonary_layout3_animate_css_effect";s:6:"fadeIn";s:42:"masonary_layout3_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout3_load_more_button_text";s:9:"Load More";s:44:"masonary_layout3_load_more_button_text_color";s:0:"";s:50:"masonary_layout3_load_more_button_background_color";s:0:"";s:46:"masonary_layout3_load_more_button_border_color";s:0:"";s:50:"masonary_layout3_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout3_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout3_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout3_load_more_button_icon";s:2:"14";s:30:"masonary_layout4_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout4_background_color";s:0:"";s:27:"masonary_layout4_text_color";s:0:"";s:29:"masonary_layout4_border_color";s:0:"";s:29:"masonary_layout4_border_width";s:0:"";s:35:"masonary_layout4_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout4_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout4_load_more_button_text";s:9:"Load More";s:44:"masonary_layout4_load_more_button_text_color";s:0:"";s:50:"masonary_layout4_load_more_button_background_color";s:0:"";s:46:"masonary_layout4_load_more_button_border_color";s:0:"";s:50:"masonary_layout4_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout4_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout4_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout4_load_more_button_icon";s:2:"14";s:30:"masonary_layout5_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout5_background_color";s:0:"";s:27:"masonary_layout5_text_color";s:0:"";s:29:"masonary_layout5_border_color";s:0:"";s:29:"masonary_layout5_border_width";s:0:"";s:35:"masonary_layout5_animate_css_effect";s:6:"fadeIn";s:42:"masonary_layout5_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout5_load_more_button_text";s:9:"Load More";s:44:"masonary_layout5_load_more_button_text_color";s:0:"";s:50:"masonary_layout5_load_more_button_background_color";s:0:"";s:46:"masonary_layout5_load_more_button_border_color";s:0:"";s:50:"masonary_layout5_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout5_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout5_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout5_load_more_button_icon";s:2:"14";s:35:"instagram_layout_animate_css_effect";s:6:"fadeIn";s:30:"instagram_layout_show_time_ago";s:1:"1";s:30:"instagram_layout_show_comments";s:1:"1";s:31:"instagram_layout_comments_count";s:1:"8";s:35:"instagram_layout_show_image_caption";s:1:"1";s:42:"instagram_layout_load_more_button_position";s:11:"apif-center";s:38:"instagram_layout_load_more_button_text";s:9:"Load More";s:44:"instagram_layout_load_more_button_text_color";s:0:"";s:50:"instagram_layout_load_more_button_background_color";s:0:"";s:46:"instagram_layout_load_more_button_border_color";s:0:"";s:50:"instagram_layout_load_more_button_hover_text_color";s:0:"";s:61:"instagram_layout_load_more_button_text_hover_background_color";s:0:"";s:52:"instagram_layout_load_more_button_hover_border_color";s:0:"";s:38:"instagram_layout_load_more_button_icon";s:2:"14";s:32:"round_image_layout_no_of_columns";s:10:"apif-col-4";s:35:"round_image_layout_animation_effect";s:16:"circle-rotateyrs";s:37:"round_image_layout_hover_border_width";s:0:"";s:37:"round_image_layout_hover_border_color";s:0:"";s:37:"round_image_layout_animate_css_effect";s:8:"rotateIn";s:44:"round_image_layout_load_more_button_position";s:11:"apif-center";s:40:"round_image_layout_load_more_button_text";s:0:"";s:46:"round_image_layout_load_more_button_text_color";s:0:"";s:52:"round_image_layout_load_more_button_background_color";s:0:"";s:48:"round_image_layout_load_more_button_border_color";s:0:"";s:52:"round_image_layout_load_more_button_hover_text_color";s:0:"";s:63:"round_image_layout_load_more_button_text_hover_background_color";s:0:"";s:54:"round_image_layout_load_more_button_hover_border_color";s:0:"";s:40:"round_image_layout_load_more_button_icon";s:2:"14";s:33:"grid_rotator_layout_no_of_columns";s:1:"4";s:30:"grid_rotator_layout_no_of_rows";s:1:"2";s:38:"slider_layout_show_next_previous_color";s:0:"";s:34:"slider_1_layout_show_image_caption";s:1:"1";s:46:"slider_1_layout_image_caption_background_color";s:0:"";s:40:"slider_1_layout_show_next_previous_color";s:0:"";s:40:"slider_3_layout_show_next_previous_color";s:0:"";s:29:"slider_layout_4_show_time_ago";s:1:"1";s:34:"slider_layout_4_show_image_caption";s:1:"1";s:32:"slider_layout_4_navigation_color";s:0:"";}

But I know which two we’re going to use as template and what pieces we need to alter so we never have to look at that again. Next up is setting a generic function to grab our template data.

function getInstagramTemplate($row){
    global $wpdb;    
    $instaData = $wpdb->get_row( "SELECT `feed_settings` FROM  `wp_instagram_feeds` WHERE `id` = " . $row);
    return $instaData;
}

Now we want to grab one template for the instructor (get data by instagram user name) and one template for the course (get data by instagram hashtag). We’ll grab the variables from the user account, change our array values, serialize them and stick them back in the database table.

    $og_insta_data_faculty = getInstagramTemplate(26);//get template with ID 26 - the faculty one
    $og_insta_data_course = getInstagramTemplate(27);//get template with ID 27 -  the hashtag one
    
    $instaDataArrayFaculty = unserialize($og_insta_data_faculty->feed_settings);   //make them arrays
    $instaDataArrayCourse = unserialize($og_insta_data_course->feed_settings);

    //get data from user
    
    $faculty_ID = $user_id;
    $faculty_name = get_the_author_meta('nickname', $faculty_ID);
    $faculty_insta = get_the_author_meta('faculty_insta', $faculty_ID);//instagram account name
    $course_insta = get_the_author_meta('insta_hash', $faculty_ID);//instagram course hash tag
    $course_insta = str_ireplace('#', '', $course_insta);//remove hashtag if it's in there
    $school = get_the_author_meta('school', $faculty_ID);//school

 //alter instagram elements for faculty
    $instaDataArrayFaculty["feed_name"] = '@' . $faculty_name . ' feed - course is #' . $course_insta ;
    $instaDataArrayFaculty["any_user_username"] = $faculty_insta; 


  //alter instagram elements for course
    $instaDataArrayCourse["feed_name"] = '#' . $course_insta . ' feed - faculty is @' . $faculty_name;
    $instaDataArrayCourse["tag_name"] = $course_insta;

    //reserialize the instagram elements
    $instaDataArrayFaculty = maybe_serialize($instaDataArrayFaculty);
    $instaDataArrayCourse = maybe_serialize($instaDataArrayCourse);
    
    //insert into database
    $faculty_insta_id = insertInstagramData($instaDataArrayFaculty);//id for faculty instagram plugin shortcode
    $insta_hash_id = insertInstagramData($instaDataArrayCourse);//id for course instagram plugin shortcode

Ok, that takes care of making new instagram shortcodes with the right variables. The rest of the code integrates a few other variables.

 //create post
    $course_title = get_the_author_meta('course_name', $faculty_ID);
    $public = get_the_author_meta('visible', $faculty_ID);

    $featured_img = get_the_author_meta('course_img', $faculty_ID);

    $post_content = '[vc_row full_width="stretch_row_content td-stretch-content"][vc_column]<div class="vc_row td_uid_1_5a523c3ba9b10_rand  wpb_row td-pb-row"><div class="vc_column td_uid_2_5a523c3ba9d8b_rand  wpb_column vc_column_container tdc-column td-pb-span12"><div class="wpb_wrapper"><div class="tdm_block td_block_wrap tdm_block_column_title td_uid_3_5a523c3ba9f60_rand tdm-content-horiz-center td-pb-border-top td_block_template_3" data-td-block-uid="td_uid_3_5a523c3ba9f60"><div class="td-block-row"><div class="td-block-span12 tdm-col"><div class="tds-title tds-title3  td-fix-index td_uid_4_5a523c3bab26c"><h2 class="tdm-title tdm-title-bg clan-title">#' . $course_insta .'</h2><div class="tdm-title-line"></div><div class="tdm-title-sub">' . $course_title . '</div></div></div></div></div><div class="wpb_wrapper td_block_empty_space td_block_wrap vc_empty_space td_uid_5_5a523c3bab3dd_rand " style="height: 32px"></div></div></div></div>[vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column]<div class="anth-course">[td_block_2 autors_id="' . $faculty_ID . '"]</div>[/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]<hr /><h3><span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from ' . $faculty_insta . '</span></h3>[ap_instagram_feed_pro id="'. $faculty_insta_id .'"]<hr /><h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #' . $course_insta . ' Students</span></h3>[ap_instagram_feed_pro id="' . $insta_hash_id .'"][/vc_column_text][/vc_column][/vc_row]';



// Create the new course page 
$course_post = array(
  'post_title'    => $course_title,
  'post_content'  => $post_content,
  'post_status'   => 'publish',
  'post_author'   => $faculty_ID,
  'post_type'     => 'page',
  'tags_input'    => $public,//I wrote this in two places after deciding to make tags possible for pages because the query loop was more pleasant
   'meta_input'   => array(
        'public' => $public,//visible in public display of courses 
        'school' => $school,//associated school
    ),
);
 
// Insert the post into the database
$new_post_id = wp_insert_post( $course_post );

add_post_meta($new_post_id, 'clan_page', $new_post_id, true);//set the id of the clan page to the custom field in case we need to update it
<a href="https://yapinte5340.blogspot.com/2017/06/case-study-1-anth101com-anthropology.html"></a>
makeFeatured($new_post_id, $featured_img);//make the featured image

Now all of this ends up being trigger by account creation. Which will lead you to believe that you’ll use the user_register action to drive it. That will result in much suffering and your user metadata fields will always come back empty despite propagating when you go to look at the user through the normal WordPress backend or in the database. The action you need to use is gform_user_registered and that makes total sense once you realize it exists.

Now we’re operational and I added some pieces to make user profile updates update the course page in case people make mistakes and want to change elements. That’s not super clean in that it duplicates Instagram entries rather than fixing them but it works.

The result is a page that shows the author posts on this WordPress, the author’s posts on Instagram, and the course hashtag posts on Instagram – all tied to the user profile.

ANTH101 v 3.ish

I think it ended up working out pretty well. There are a number of ways I can/should make the code cleaner and DRY-er but you can check out the whole thing if you want on GitHub.

I do encourage you to go check out the ANTH101 site as it just keeps improving and Mike has continued to expand the resources available. It’s also one of those course sites where the student work inevitable draws me in. I always click on at least one.

As part of preparing to write this post, I also wandered around the Internet to see what other people had written. There are some pretty interesting reviews from different people- a few of which I include below.

I think Professor Iderweis is a bit harsh on themselves but it’s a beautiful description of the ANTH101 community and the tolerance for ongoing development.

anth101.com models for me what a living, breathing course can look like. When I look back to when I taught Drugs and Behavior and Intro Psych, I see the classes I taught as boring and lifeless. We as a class didn’t create our own stories together, and we lost the opportunities to make connections. I really like how well-integrated all the various media for each lesson and challenge are on the website. And the more I dig into this site, the more I am personally challenged? to design my classes to have more of an open pedagogy behind them and being okay with my classes being in the “beta” stage every week.

Another teacher wrote

Thinking about my own teaching and learning, this is something that I would like to get better at. As a learner, I was drawn into the videos and probably spent too much time clicking around, rewatching, and thinking about the themes of the week. It was welcoming, creative, and interesting. In many ways, it is like my current class on digital storytelling. There are weekly themes, challenges, and input from our teacher via podcasts, videos, and social media. My students would enjoy this format as well. They love to do real world, authentic, project based learning and when framed properly, they dive right in. That said, as a teacher, I would be interested in seeing that backend of this type of educational structure. I worry that it might be somewhat unmanageable to create, track, give feedback, and grade the class. What would the pace be if everyone was moving along at different rates and exploring different projects? It is one thing to have a class of 20-30 college students, but having 130 high school freshman is a different animal. I think that I could take aspects of this class and incorporate them slowly over time and build towards something larger – step by step…

There’s lots to unpack in that comment especially in light of the scale of the ANTH101 course.

Megan Pendergast notes-

This open source class is an example of challenging the traditional classroom, and I love it! The teacher is providing the students with the resources to learn new lessons, and then challenging them to apply these lesson to their lives. Talk about complete engagement! I believe this class is a WIN for education. Additionally, if a teacher wanted to mimic this class or parts of it, the resources are available to do so. I think participating in the challenges one day (when I’m not in the middle of a graduate degree) would be very rewarding.

Elsa Pranger had more to say about the ease of interaction.

I was really impressed with how easy the layout of the course seems. It also seems very manageable. On a very basic level, there were a lot of parallels with format for this course we are currently in. I think after taking this course, I would be better equipped to participate in the project-based, online model. The information also looks very interesting to me. As someone who is not always into the newest digital, techy advances, I definitely do appreciate when technology is used to connect people. Now, I know we like to think we are way more connected than truly are, but courses like this offer those authentic experiences that go cross-culturally as well. This also reminds me of our video about campfire storytelling in this weeks playlist as they tried to get stories across cultures. In a similar fashion, ANTH101 is trying to really look at what makes us human, how we interact, and then have the students implement these authentic interactions through a free, online course.

And finally there’s an interview with Mike about this course that’s well worth reading here.


1 The earliest email I see is Aug. 7, 2016. It looks like around 357 emails.

2 This has long been a goal of mine but I think I’m actually in a place now where I can make it happen. It’s also a textbook example of something that I have to offload to technology as I always never get the kind of documentation I want.

3 When I first saw them I had no idea what they were and asked Jeff.

ANTH101 v 3.ish

ANTH101 v 3.ish

“You cannot think your way into a new way of living. You have to live your way into a new way of thinking.”
– Mike Wesch

You can’t beat that quote as a way to frame a course and it’s nice to consider how digital content supports that kind of perspective on learning/living. It’s also a key consideration in how I think about building courses like this. You have to do it. You shouldn’t expect to be perfect the first time, or the second, or ever really but if you’re doing it right improving it should be worth the investment. You should get some joy out of the process and it should alleviate things that cause you pain.

I’ve had the opportunity to work with Mike Wesch and Ryan Klataske at Kansas State over the last few years1 on the ANTH101 site. It’s been an interesting progression over time as the course has continued to evolve. We’ve gone done a variety of paths and dealt with human and technical issues.

It has been interesting to participate in the ongoing co-evolution of aesthetics, mechanics, and content. It’s also a scenario where I wish I’d have done a much better job with screenshots so I could more accurately show you how the site has evolved.2

After an initial meeting at Kansas State (our first meeting), we’ve done all of this work through a mix of video calls and Google Docs. You can see the editing stats on one of the Google Docs we used below. Lots of edits, lots of comments, and a decent span of time for one document.

The course is meant to be used with a large number of students. In today’s world that usually means teaching assistants running smaller groups. The intent was also to use this across institutions. In both cases you’re working extra hard to limit support issues. In this case we don’t have a centralized IT group for support and we couldn’t easily do single sign on between institutions. We did a number of things to try to make life easier. We tried simplifying the backend. We moved posts to the front-end and automated, limited, or forced categories, featured images etc. It definitely improved things but support for teachers and students remained an issue.

The other major issue was the number of students. It was meant to be a large enrollment course and so we end up with a very large number of authors writing to the database during fairly short windows of time. That always creates issues with WordPress (and any other system I’ve used). We struggled with this and were moving towards a setup similar to the way we manage rampages– that is a multisite installation with a sharded database with cohorts of authors writing at the site level. That ended up getting derailed because it was going to take another big chunk of serious infrastructure re-working and wouldn’t solve other some other support problems.

So the big question became whether we could move the student portion to another service. We took a look at many things and ended up coming back around to Instagram. I know. I have feelings about the service and Facebook and all that stuff but it’s also a tool currently in use by most of the students already. It made sense in a number of ways and outsourced a chunk of support issues to an external service so we went with that. Part of that migration process was using Site Sucker on the gigantic old site to create an archive which ended up being about 11GB and 167,000 files. As you might imagine this took a good bit of time to run.

Making this move necessitated thinking through the things we needed to enable in the new environment. The more traditional content (Lessons, Challenges) pages would be relatively straight forward but the clan/group page that was tied into BuddyPress was one of those things that needed custom work. It gave the smaller cohorts a nice way to see just their cohort and enabled the leader of the group to communicate directly with that group. Since we were cutting out BuddyPress and the student content was going to be created elsewhere we needed to work out how to do this. Instagram has taken actions to make their API much more of a hassle to work with even for simply reading public data. As a result, we opted to go with the AccessPress Instagram Feed Pro plugin. That gave us some options to display content by username or hashtag without getting API key approval etc. This plugin creates shortcodes but the parameters are held in the database and the ID of the element is the only thing that’s visible in the shortcode. So you’re not doing something like [insta user=”tom”] or [insta tag=”foo”] but [insta id=”3″]. The other complication here was that Mike likes to have more flexibility in the authoring process so we were also working with the shortcodes created by Visual Composer. Some of these shortcodes are very odd.

The mechanization of this process became somewhat involved as a result. Any time you’re working with multiple shortcode plugins things become more involved.

Step One: Get the User Information

We opted to initiate the course page creation through the user signup process. To create a course page you needed to register for the WordPress site. That would enable us to get information about the course hashtag and the faculty user Instagram account to use as variables in the page creation. The developer version of Gravity Forms gives you a nice way to automate user registration and tie in various additional user metadata fields. You can see the preferences interface for that below.
ANTH101 v 3.ish

In order to make those fields visible in the user profile view for normal users I added the following via the plugin. I opted to do all of this through a plugin rather than altering the functions.php file of the theme. The hope was that it would be fairly portable in case the theme changed later. I doubt that it really makes much difference one way or another.

//MAKE USER META VISIBLE & HIDE SOME OTHER STUFF
function modify_user_meta_methods($profile_fields) {

  // Add new fields
  $profile_fields['faculty_insta'] = 'Faculty Instagram Account';//add stuff
  $profile_fields['insta_hash'] = 'Course Instagram Hashtag';
  $profile_fields['course_name'] = 'Course Name';
  $profile_fields['school'] = 'School';
  $profile_fields['visible'] = 'Visible';
  $profile_fields['course_img'] = 'Course Image';
  //$profile_fields['clan_page'] = 'Clan Page ID'; would make the id visible and editable - nice for testing but not needed
  unset($profile_fields['aim']);//remove stuff
  unset($profile_fields['yim']);
  unset($profile_fields['jabber']);
  unset($profile_fields['url']);


  return $profile_fields;
}
add_filter('user_contactmethods', 'modify_user_meta_methods');

This essentially gives us all the variables we need to create a custom course page. Now we need to make the page and merge in the variables.

Mike made a template that looked the way he wanted and had the desired functionality. The view source of that looked like so . . .

[vc_row full_width="stretch_row_content td-stretch-content"][vc_column][tdm_block_column_title tds_title="tds_title3" title_text="JTIza2Fwc2NsYW4=" title_size="tdm-title-bg" tds_title2-title_color="#8224e3" tds_title2-line_height="10" content_align_horizontal="content-horiz-center" tds_title3-subtitle_text="Ms. Shearer's Anthropology Class"][vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column][td_block_24 limit="1" custom_title=""][td_block_2 offset="1"][/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]

<hr />

<h3><span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from Ms. Shearer</span></h3>
[ap_instagram_feed_pro id="26"]

<hr />

<h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #Kapsclan Students</span></h3>
[ap_instagram_feed_pro id="27"]

[/vc_column_text][/vc_column][/vc_row]

You can see a combination of the the ap_instagram_feed_pro shortcodes and the vc_ Viscual composer shortcodes. You might note some odd things about the VC element in the first chunk- in particular title_text=”JTIza2Fwc2NsYW4=”. It’s some sort of encoding of regular text but I never did figure out what the encoding method was. In any case, it added some hassle at a point when I wasn’t in the mood for it. As a result, I went to the published post and copied out the HTML that shortcode created rather than spending more time messing with that nonsense. That is an example of either creative problem solving or laziness. Possibly both.

In any case, the post content of my course page now starts to look like this with some variables in there where I need them.

[vc_row full_width="stretch_row_content td-stretch-content"][vc_column]
<div class="vc_row td_uid_1_5a523c3ba9b10_rand  wpb_row td-pb-row"><div class="vc_column td_uid_2_5a523c3ba9d8b_rand  wpb_column vc_column_container tdc-column td-pb-span12"><div class="wpb_wrapper">
<div class="tdm_block td_block_wrap tdm_block_column_title td_uid_3_5a523c3ba9f60_rand tdm-content-horiz-center td-pb-border-top td_block_template_3" data-td-block-uid="td_uid_3_5a523c3ba9f60">
<div class="td-block-row"><div class="td-block-span12 tdm-col">
<div class="tds-title tds-title3  td-fix-index td_uid_4_5a523c3bab26c">
<h2 class="tdm-title tdm-title-bg clan-title">#' . $course_insta .'</h2>
<div class="tdm-title-line"></div><div class="tdm-title-sub">' . $course_title . '</div></div></div></div></div>
<div class="wpb_wrapper td_block_empty_space td_block_wrap vc_empty_space td_uid_5_5a523c3bab3dd_rand " style="height: 32px"></div></div></div></div>[vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column]<div class="anth-course">[td_block_2 autors_id="' . $faculty_ID . '"]</div>
[/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]<hr /><h3>
<span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from ' . $faculty_insta . '</span></h3>[ap_instagram_feed_pro id="'. $faculty_insta_id .'"]<hr /><h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #' . $course_insta . ' Students</span></h3>[ap_instagram_feed_pro id="' . $insta_hash_id .'"][/vc_column_text][/vc_column][/vc_row]

Now I have to do a few things to make this work. First, I have to duplicate the Instagram shortcode elements from the database, work in the new variables, and then insert them into the database. They’re pretty ugly looking things3 but we’re only changing a few variables and they’re just serialized arrays so that makes life easier. This is what they look like in the database in the wp_instagram_feeds table. Yuck.

a:166:{s:9:"feed_name";s:8:"KapsClan";s:9:"feed_type";s:3:"tag";s:34:"recent_media_blocked_caption_words";s:0:"";s:34:"recent_media_allowed_caption_words";s:0:"";s:17:"any_user_username";s:0:"";s:39:"any_user_username_blocked_caption_words";s:0:"";s:39:"any_user_username_allowed_caption_words";s:0:"";s:8:"tag_name";s:8:"kapsclan";s:25:"tag_blocked_caption_words";s:0:"";s:25:"tag_allowed_caption_words";s:0:"";s:32:"user_likes_blocked_caption_words";s:0:"";s:32:"user_likes_allowed_caption_words";s:0:"";s:7:"sort_by";s:4:"date";s:10:"image_size";s:19:"standard_resolution";s:20:"instagram_image_link";s:1:"1";s:19:"instagram_user_link";s:1:"1";s:23:"instagram_user_username";s:1:"1";s:10:"like_count";s:1:"1";s:13:"comment_count";s:1:"1";s:20:"counter_type_options";s:1:"3";s:15:"enable_lightbox";s:1:"1";s:15:"lightbox_layout";s:17:"apif_own_lightbox";s:12:"image_number";s:3:"200";s:18:"theme_accent_color";s:0:"";s:22:"hover_image_text_color";s:0:"";s:16:"instagram_mosaic";s:15:"masonry_layout5";s:25:"grid_layout_no_of_columns";s:10:"apif-col-4";s:28:"grid_layout_animation_effect";s:21:"apif-image-zoom-style";s:30:"grid_layout_animate_css_effect";s:9:"slideInUp";s:37:"grid_layout_load_more_button_position";s:11:"apif-center";s:33:"grid_layout_load_more_button_text";s:9:"Load More";s:39:"grid_layout_load_more_button_text_color";s:0:"";s:45:"grid_layout_load_more_button_background_color";s:0:"";s:41:"grid_layout_load_more_button_border_color";s:0:"";s:45:"grid_layout_load_more_button_hover_text_color";s:0:"";s:56:"grid_layout_load_more_button_text_hover_background_color";s:0:"";s:47:"grid_layout_load_more_button_hover_border_color";s:0:"";s:33:"grid_layout_load_more_button_icon";s:2:"14";s:29:"masonary_layout_no_of_columns";s:10:"apif-col-4";s:32:"masonary_layout_animation_effect";s:18:"apif-left-to-right";s:32:"masonary_layout_background_color";s:0:"";s:28:"masonary_layout_border_color";s:0:"";s:28:"masonary_layout_border_width";s:1:"0";s:34:"masonary_layout_animate_css_effect";s:8:"fadeInUp";s:41:"masonary_layout_load_more_button_position";s:11:"apif-center";s:37:"masonary_layout_load_more_button_text";s:9:"Load More";s:43:"masonary_layout_load_more_button_text_color";s:0:"";s:49:"masonary_layout_load_more_button_background_color";s:0:"";s:45:"masonary_layout_load_more_button_border_color";s:0:"";s:49:"masonary_layout_load_more_button_hover_text_color";s:0:"";s:60:"masonary_layout_load_more_button_text_hover_background_color";s:0:"";s:51:"masonary_layout_load_more_button_hover_border_color";s:0:"";s:37:"masonary_layout_load_more_button_icon";s:2:"14";s:30:"masonary_layout1_no_of_columns";s:10:"apif-col-4";s:33:"masonary_layout1_background_color";s:0:"";s:27:"masonary_layout1_text_color";s:0:"";s:29:"masonary_layout1_border_color";s:0:"";s:29:"masonary_layout1_border_width";s:0:"";s:35:"masonary_layout1_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout1_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout1_load_more_button_text";s:9:"Load More";s:44:"masonary_layout1_load_more_button_text_color";s:0:"";s:50:"masonary_layout1_load_more_button_background_color";s:0:"";s:46:"masonary_layout1_load_more_button_border_color";s:0:"";s:50:"masonary_layout1_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout1_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout1_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout1_load_more_button_icon";s:2:"14";s:30:"masonary_layout2_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout2_background_color";s:0:"";s:27:"masonary_layout2_text_color";s:0:"";s:29:"masonary_layout2_border_color";s:0:"";s:29:"masonary_layout2_border_width";s:0:"";s:35:"masonary_layout2_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout2_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout2_load_more_button_text";s:9:"Load More";s:44:"masonary_layout2_load_more_button_text_color";s:0:"";s:50:"masonary_layout2_load_more_button_background_color";s:0:"";s:46:"masonary_layout2_load_more_button_border_color";s:0:"";s:50:"masonary_layout2_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout2_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout2_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout2_load_more_button_icon";s:2:"14";s:30:"masonary_layout3_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout3_background_color";s:0:"";s:27:"masonary_layout3_text_color";s:0:"";s:29:"masonary_layout3_border_color";s:0:"";s:29:"masonary_layout3_border_width";s:0:"";s:35:"masonary_layout3_animate_css_effect";s:6:"fadeIn";s:42:"masonary_layout3_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout3_load_more_button_text";s:9:"Load More";s:44:"masonary_layout3_load_more_button_text_color";s:0:"";s:50:"masonary_layout3_load_more_button_background_color";s:0:"";s:46:"masonary_layout3_load_more_button_border_color";s:0:"";s:50:"masonary_layout3_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout3_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout3_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout3_load_more_button_icon";s:2:"14";s:30:"masonary_layout4_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout4_background_color";s:0:"";s:27:"masonary_layout4_text_color";s:0:"";s:29:"masonary_layout4_border_color";s:0:"";s:29:"masonary_layout4_border_width";s:0:"";s:35:"masonary_layout4_animate_css_effect";s:8:"fadeInUp";s:42:"masonary_layout4_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout4_load_more_button_text";s:9:"Load More";s:44:"masonary_layout4_load_more_button_text_color";s:0:"";s:50:"masonary_layout4_load_more_button_background_color";s:0:"";s:46:"masonary_layout4_load_more_button_border_color";s:0:"";s:50:"masonary_layout4_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout4_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout4_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout4_load_more_button_icon";s:2:"14";s:30:"masonary_layout5_no_of_columns";s:10:"apif-col-3";s:33:"masonary_layout5_background_color";s:0:"";s:27:"masonary_layout5_text_color";s:0:"";s:29:"masonary_layout5_border_color";s:0:"";s:29:"masonary_layout5_border_width";s:0:"";s:35:"masonary_layout5_animate_css_effect";s:6:"fadeIn";s:42:"masonary_layout5_load_more_button_position";s:11:"apif-center";s:38:"masonary_layout5_load_more_button_text";s:9:"Load More";s:44:"masonary_layout5_load_more_button_text_color";s:0:"";s:50:"masonary_layout5_load_more_button_background_color";s:0:"";s:46:"masonary_layout5_load_more_button_border_color";s:0:"";s:50:"masonary_layout5_load_more_button_hover_text_color";s:0:"";s:61:"masonary_layout5_load_more_button_text_hover_background_color";s:0:"";s:52:"masonary_layout5_load_more_button_hover_border_color";s:0:"";s:38:"masonary_layout5_load_more_button_icon";s:2:"14";s:35:"instagram_layout_animate_css_effect";s:6:"fadeIn";s:30:"instagram_layout_show_time_ago";s:1:"1";s:30:"instagram_layout_show_comments";s:1:"1";s:31:"instagram_layout_comments_count";s:1:"8";s:35:"instagram_layout_show_image_caption";s:1:"1";s:42:"instagram_layout_load_more_button_position";s:11:"apif-center";s:38:"instagram_layout_load_more_button_text";s:9:"Load More";s:44:"instagram_layout_load_more_button_text_color";s:0:"";s:50:"instagram_layout_load_more_button_background_color";s:0:"";s:46:"instagram_layout_load_more_button_border_color";s:0:"";s:50:"instagram_layout_load_more_button_hover_text_color";s:0:"";s:61:"instagram_layout_load_more_button_text_hover_background_color";s:0:"";s:52:"instagram_layout_load_more_button_hover_border_color";s:0:"";s:38:"instagram_layout_load_more_button_icon";s:2:"14";s:32:"round_image_layout_no_of_columns";s:10:"apif-col-4";s:35:"round_image_layout_animation_effect";s:16:"circle-rotateyrs";s:37:"round_image_layout_hover_border_width";s:0:"";s:37:"round_image_layout_hover_border_color";s:0:"";s:37:"round_image_layout_animate_css_effect";s:8:"rotateIn";s:44:"round_image_layout_load_more_button_position";s:11:"apif-center";s:40:"round_image_layout_load_more_button_text";s:0:"";s:46:"round_image_layout_load_more_button_text_color";s:0:"";s:52:"round_image_layout_load_more_button_background_color";s:0:"";s:48:"round_image_layout_load_more_button_border_color";s:0:"";s:52:"round_image_layout_load_more_button_hover_text_color";s:0:"";s:63:"round_image_layout_load_more_button_text_hover_background_color";s:0:"";s:54:"round_image_layout_load_more_button_hover_border_color";s:0:"";s:40:"round_image_layout_load_more_button_icon";s:2:"14";s:33:"grid_rotator_layout_no_of_columns";s:1:"4";s:30:"grid_rotator_layout_no_of_rows";s:1:"2";s:38:"slider_layout_show_next_previous_color";s:0:"";s:34:"slider_1_layout_show_image_caption";s:1:"1";s:46:"slider_1_layout_image_caption_background_color";s:0:"";s:40:"slider_1_layout_show_next_previous_color";s:0:"";s:40:"slider_3_layout_show_next_previous_color";s:0:"";s:29:"slider_layout_4_show_time_ago";s:1:"1";s:34:"slider_layout_4_show_image_caption";s:1:"1";s:32:"slider_layout_4_navigation_color";s:0:"";}

But I know which two we’re going to use as template and what pieces we need to alter so we never have to look at that again. Next up is setting a generic function to grab our template data.

function getInstagramTemplate($row){
    global $wpdb;    
    $instaData = $wpdb->get_row( "SELECT `feed_settings` FROM  `wp_instagram_feeds` WHERE `id` = " . $row);
    return $instaData;
}

Now we want to grab one template for the instructor (get data by instagram user name) and one template for the course (get data by instagram hashtag). We’ll grab the variables from the user account, change our array values, serialize them and stick them back in the database table.

    $og_insta_data_faculty = getInstagramTemplate(26);//get template with ID 26 - the faculty one
    $og_insta_data_course = getInstagramTemplate(27);//get template with ID 27 -  the hashtag one
    
    $instaDataArrayFaculty = unserialize($og_insta_data_faculty->feed_settings);   //make them arrays
    $instaDataArrayCourse = unserialize($og_insta_data_course->feed_settings);

    //get data from user
    
    $faculty_ID = $user_id;
    $faculty_name = get_the_author_meta('nickname', $faculty_ID);
    $faculty_insta = get_the_author_meta('faculty_insta', $faculty_ID);//instagram account name
    $course_insta = get_the_author_meta('insta_hash', $faculty_ID);//instagram course hash tag
    $course_insta = str_ireplace('#', '', $course_insta);//remove hashtag if it's in there
    $school = get_the_author_meta('school', $faculty_ID);//school

 //alter instagram elements for faculty
    $instaDataArrayFaculty["feed_name"] = '@' . $faculty_name . ' feed - course is #' . $course_insta ;
    $instaDataArrayFaculty["any_user_username"] = $faculty_insta; 


  //alter instagram elements for course
    $instaDataArrayCourse["feed_name"] = '#' . $course_insta . ' feed - faculty is @' . $faculty_name;
    $instaDataArrayCourse["tag_name"] = $course_insta;

    //reserialize the instagram elements
    $instaDataArrayFaculty = maybe_serialize($instaDataArrayFaculty);
    $instaDataArrayCourse = maybe_serialize($instaDataArrayCourse);
    
    //insert into database
    $faculty_insta_id = insertInstagramData($instaDataArrayFaculty);//id for faculty instagram plugin shortcode
    $insta_hash_id = insertInstagramData($instaDataArrayCourse);//id for course instagram plugin shortcode

Ok, that takes care of making new instagram shortcodes with the right variables. The rest of the code integrates a few other variables.

 //create post
    $course_title = get_the_author_meta('course_name', $faculty_ID);
    $public = get_the_author_meta('visible', $faculty_ID);

    $featured_img = get_the_author_meta('course_img', $faculty_ID);

    $post_content = '[vc_row full_width="stretch_row_content td-stretch-content"][vc_column]<div class="vc_row td_uid_1_5a523c3ba9b10_rand  wpb_row td-pb-row"><div class="vc_column td_uid_2_5a523c3ba9d8b_rand  wpb_column vc_column_container tdc-column td-pb-span12"><div class="wpb_wrapper"><div class="tdm_block td_block_wrap tdm_block_column_title td_uid_3_5a523c3ba9f60_rand tdm-content-horiz-center td-pb-border-top td_block_template_3" data-td-block-uid="td_uid_3_5a523c3ba9f60"><div class="td-block-row"><div class="td-block-span12 tdm-col"><div class="tds-title tds-title3  td-fix-index td_uid_4_5a523c3bab26c"><h2 class="tdm-title tdm-title-bg clan-title">#' . $course_insta .'</h2><div class="tdm-title-line"></div><div class="tdm-title-sub">' . $course_title . '</div></div></div></div></div><div class="wpb_wrapper td_block_empty_space td_block_wrap vc_empty_space td_uid_5_5a523c3bab3dd_rand " style="height: 32px"></div></div></div></div>[vc_empty_space][/vc_column][/vc_row][vc_row full_width="stretch_row"][vc_column]<div class="anth-course">[td_block_2 autors_id="' . $faculty_ID . '"]</div>[/vc_column][/vc_row][vc_row][vc_column][/vc_column][/vc_row][vc_row][vc_column][vc_column_text]<hr /><h3><span class="td_text_highlight_marker_green td_text_highlight_marker">Latest Instagrams from ' . $faculty_insta . '</span></h3>[ap_instagram_feed_pro id="'. $faculty_insta_id .'"]<hr /><h3><span class="td_text_highlight_marker_red td_text_highlight_marker">Latest posts from #' . $course_insta . ' Students</span></h3>[ap_instagram_feed_pro id="' . $insta_hash_id .'"][/vc_column_text][/vc_column][/vc_row]';



// Create the new course page 
$course_post = array(
  'post_title'    => $course_title,
  'post_content'  => $post_content,
  'post_status'   => 'publish',
  'post_author'   => $faculty_ID,
  'post_type'     => 'page',
  'tags_input'    => $public,//I wrote this in two places after deciding to make tags possible for pages because the query loop was more pleasant
   'meta_input'   => array(
        'public' => $public,//visible in public display of courses 
        'school' => $school,//associated school
    ),
);
 
// Insert the post into the database
$new_post_id = wp_insert_post( $course_post );

add_post_meta($new_post_id, 'clan_page', $new_post_id, true);//set the id of the clan page to the custom field in case we need to update it
<a href="https://yapinte5340.blogspot.com/2017/06/case-study-1-anth101com-anthropology.html"></a>
makeFeatured($new_post_id, $featured_img);//make the featured image

Now all of this ends up being trigger by account creation. Which will lead you to believe that you’ll use the user_register action to drive it. That will result in much suffering and your user metadata fields will always come back empty despite propagating when you go to look at the user through the normal WordPress backend or in the database. The action you need to use is gform_user_registered and that makes total sense once you realize it exists.

Now we’re operational and I added some pieces to make user profile updates update the course page in case people make mistakes and want to change elements. That’s not super clean in that it duplicates Instagram entries rather than fixing them but it works.

The result is a page that shows the author posts on this WordPress, the author’s posts on Instagram, and the course hashtag posts on Instagram – all tied to the user profile.

ANTH101 v 3.ish

I think it ended up working out pretty well. There are a number of ways I can/should make the code cleaner and DRY-er but you can check out the whole thing if you want on GitHub.

I do encourage you to go check out the ANTH101 site as it just keeps improving and Mike has continued to expand the resources available. It’s also one of those course sites where the student work inevitable draws me in. I always click on at least one.

As part of preparing to write this post, I also wandered around the Internet to see what other people had written. There are some pretty interesting reviews from different people- a few of which I include below.

I think Professor Iderweis is a bit harsh on themselves but it’s a beautiful description of the ANTH101 community and the tolerance for ongoing development.

anth101.com models for me what a living, breathing course can look like. When I look back to when I taught Drugs and Behavior and Intro Psych, I see the classes I taught as boring and lifeless. We as a class didn’t create our own stories together, and we lost the opportunities to make connections. I really like how well-integrated all the various media for each lesson and challenge are on the website. And the more I dig into this site, the more I am personally challenged? to design my classes to have more of an open pedagogy behind them and being okay with my classes being in the “beta” stage every week.

Another teacher wrote

Thinking about my own teaching and learning, this is something that I would like to get better at. As a learner, I was drawn into the videos and probably spent too much time clicking around, rewatching, and thinking about the themes of the week. It was welcoming, creative, and interesting. In many ways, it is like my current class on digital storytelling. There are weekly themes, challenges, and input from our teacher via podcasts, videos, and social media. My students would enjoy this format as well. They love to do real world, authentic, project based learning and when framed properly, they dive right in. That said, as a teacher, I would be interested in seeing that backend of this type of educational structure. I worry that it might be somewhat unmanageable to create, track, give feedback, and grade the class. What would the pace be if everyone was moving along at different rates and exploring different projects? It is one thing to have a class of 20-30 college students, but having 130 high school freshman is a different animal. I think that I could take aspects of this class and incorporate them slowly over time and build towards something larger – step by step…

There’s lots to unpack in that comment especially in light of the scale of the ANTH101 course.

Megan Pendergast notes-

This open source class is an example of challenging the traditional classroom, and I love it! The teacher is providing the students with the resources to learn new lessons, and then challenging them to apply these lesson to their lives. Talk about complete engagement! I believe this class is a WIN for education. Additionally, if a teacher wanted to mimic this class or parts of it, the resources are available to do so. I think participating in the challenges one day (when I’m not in the middle of a graduate degree) would be very rewarding.

Elsa Pranger had more to say about the ease of interaction.

I was really impressed with how easy the layout of the course seems. It also seems very manageable. On a very basic level, there were a lot of parallels with format for this course we are currently in. I think after taking this course, I would be better equipped to participate in the project-based, online model. The information also looks very interesting to me. As someone who is not always into the newest digital, techy advances, I definitely do appreciate when technology is used to connect people. Now, I know we like to think we are way more connected than truly are, but courses like this offer those authentic experiences that go cross-culturally as well. This also reminds me of our video about campfire storytelling in this weeks playlist as they tried to get stories across cultures. In a similar fashion, ANTH101 is trying to really look at what makes us human, how we interact, and then have the students implement these authentic interactions through a free, online course.

And finally there’s an interview with Mike about this course that’s well worth reading here.


1 The earliest email I see is Aug. 7, 2016. It looks like around 357 emails.

2 This has long been a goal of mine but I think I’m actually in a place now where I can make it happen. It’s also a textbook example of something that I have to offload to technology as I always never get the kind of documentation I want.

3 When I first saw them I had no idea what they were and asked Jeff.

Re-Thinking My Portfolio

Re-Thinking My Portfolio

Below is the assignment I gave. I’m going to give it a shot starting with my own portfolio and then looking for aspirational goals in a subsequent post.

Take a look around at portfolios other people are using. You’ll want to check out people in the field like Dr. Tressie McMillan Cottom but you’ll also want to look outside sociology for inspiration. Cast a wide net. Find sites that are doing things you like. Take targeted screenshots and explain what you like. Link back to the sites. Keep thinking about the data being presented, the user interactions that get you to the data, and the visual elements that appeal to you.

Spend an hour or so looking around and getting ideas. If a particular search isn’t fruitful, try something else or jump ahead 5 or 10 pages in the search results. Don’t get bogged down.

Once you’ve got a good chunk of ideas, use them to create a post outlining what you’d like your portfolio to be. Some people call this wireframing. If you like to sketch, sketch out things on paper or whiteboards and photograph (I do this often) them. You can also use a variety of digital tools to do something similar.

Your portfolio is an interactive argument that provides a skeptic audience with a limited attention span that you’re the person who can do the work you want to do.

The goal here is to create a roadmap for the kind of portfolio you’d like to have.

What’s on page one? What are the major elements of the menu? How do you entice someone to click for more details? Think about things like this as you sketch out what you want the site to look like.

Step one here was to get decent screenshot of the whole screen rather than just the portion visible within the viewport. I used this full page screenshot Chrome plugin to do it. Probably the fact that I had to do this is indicative of a larger problem. Anything “below the fold” is far less likely to be seen so I need to rethink what I’m prioritizing.

The intent here was to give people a quick overview of my educational philosophy while also giving a glimpse of the fact that I have actions visible to back that up. I end up explaining things too much. The three columns at the bottom bring together blog posts, bookmarks, and tweets. It’s ok but I’m not sure it really captures what I want. Tweets tend to be fairly inconsequential on average. I’m also missing out on GitHub activity which is likely far more indicative of things I want to do than anything I’m going to have in a tweet. Github has a variety of interesting API options for data that I need to look into.

Blog posts also end up being fairly limited in what they show. It might be more interesting to display those categorically and emphasize total counts. This might be a nice place to really think through some data visualization options. What matters? How do I show it? How do I show long-term dedication to the topic? What do I need to do to automate this?

Maybe the bookmarks move towards something similar. What does the aggregate show? I think that’s far more indicative of what I want to argue than simply showing the most recent. If I’m using data to show I do this often and have committed then showing total numbers and tag totals will do that better.

Re-Thinking My Portfolio
The photography page does a bit of data. It is grabbing my total number of photos and the start date of my account from the API but it doesn’t doing any visual work with it. I’m focusing again on most recent work rather than making the argument I should be making. I need to be more aware of a tendency to do this. Maybe it’s something that happens more often with syndication. If I’m trying to show I’m good at photography, it makes more sense to choose particular photos for a prime location and do something different to show ongoing practice. It may be that photography doesn’t really fit as a major part of my portfolio. It doesn’t if I’m being purely practical but I want to maintain that I both value and bring a diverse set of skills. I feel it’s an argument worth making.

Re-Thinking My Portfolio
The presentations page need serious work. I pretty much dislike all of it. I want to show scale and scope but the individual pieces are too big and navigation is not intuitive. The structure of the individual elements ends up failing for me as well. It leads to inconsistent data entry. I’m also inclined to move it from a Google Sheet to a custom WordPress post type. Once again, I’m showing most recent . . . which does have value here but I’m not showing how things add up. What’s the aggregate result of all this stuff? People aren’t going to browse through the items. I need to condense and display.

Re-Thinking My Portfolio
Now my timeline did seem kind of decent at the time but it’s neither a decent example of web design on my end nor is it really an engagement that’s likely to happen with many people. They aren’t going to page through each piece. I’m better off with a view that can be seen all at once- no interactions required.

Re-Thinking My Portfolio
My resume was more recently reconsidered. It’s written with Skeleton CSS and was meant to move between the web and PDF/print pretty well. There’s no real data displayed and it’s not a living document. There is no automation. That needs to be remedied.

So if I were to encapsulate my portfolio goals in a few sentences . . .

  • show a commitment to and participation in interesting educational technology integration
  • show my technological skills
  • show dedication to public knowledge creation and sharing
  • show the length and focus of my work in a variety of areas

I want to do this through API integrations and with as little extra work as possible.

Why I’m Teaching Data Viz for Sociology

Why I’m Teaching Data Viz for Sociology

I’m teaching a course for VCU’s digital sociology Master’s degree program this semester. One of the things I’ve asked the participants to do is explain why they’re taking this course. I’m hoping that will help me customize what we’re going to do in the course.

Since I asked the participants to document why they were taking this course, I figured the least I could do is create a similar post explaining why I agreed to shepherd this course and why I made some of these initial choices.

I’m approaching the concept of data visualization pretty broadly. The course is broken into two main components. The first portion is looking at their own portfolio and how they might think it through in terms of data visualization1 and then moving on to broader/deeper applications of data visualization (connected to their personal research). Part of the reason I want to start with the portfolio is that the data is personal and it’s easier to get a feel for how real/accurate any visualizations you make are. We’ll also be able to establish a decent foundation of web literacy, design, accessibility, etc. prior to applying them to more sophisticated visualizations. Essentially, it’s about starting out on more familiar ground before venturing out into increasing complexity.

The second section will focus on figuring out a set of tools and concepts that will directly apply to things the student wants to do. A chunk of this will be looking at and trying out things with various tools and then thinking through the results– both the final data visualization and the process of creating it. There’s some balance of tool cost, author time/effort, the final product’s closeness to your dreams, the tool’s lifespan, etc. that ought to be part of one’s pattern of analysis.2

But I wandered a bit,3 why’d I agree to do this? Teaching a class is actually a financial negative for me. It is absolutely not worth the time it takes to do it compared to what I can make building websites or consulting or editing video. At the root of this particular adventure is the same thing that keeps me entertained in my current job. This looks like a chance to take a deeper dive into data visualization but one driven and constrained by sociology. That’s fun stuff. I think that the audience sought by public sociology, and digital sociology in particular, is ripe for things like interactive explanations. I think that the same people who are attracted to a degree in digital sociology will be interested in pushing the envelope in what’s possible with interactive data visualizations. I hope to help show what’s on the edge of possibilities right now while still grounding things in the desire to reach a diverse audience and to create understanding (or maybe come to new understandings).

There’s also the drive to get into a set of tools. I need to take a fairly wide look around on a regular basis as the tools and frameworks4 are changing so fast and getting increasingly powerful. I hadn’t looked at Tableau in a while and then I saw this parakeets example. Crazy stuff. Keshif continues to improve. R and D3 beckon on the programming side of things and I’ve been revisiting explorable explanations, datasketch.es, basic functions of particular data visualization types, minimalism, Feltron Reports, all kinds of good stuff.

It’s also worth running a class every so often. Can I practice what I believe? Can I do it in a way that’s acceptable to people who are probably used to pretty typical courses?5

Outside this course, I think the knowledge I gain will be useful and I’m going to try to use the course to create content that’s applicable to most people looking to pursue this work.


1 . . . and of course workflows.

2 There’s a bit more to this and it might be worth some more effort to sketch it out with some actual articulation. So this is a footnote saying that I should write better blog posts with actual complete thoughts in them.

3 If I were fancier I’d have digressed.

4 Is everything just a tool?

5 And this is all before I started reading The Ignorant Schoolmaster

Why I’m Teaching Data Viz for Sociology

Why I’m Teaching Data Viz for Sociology

I’m teaching a course for VCU’s digital sociology Master’s degree program this semester. One of the things I’ve asked the participants to do is explain why they’re taking this course. I’m hoping that will help me customize what we’re going to do in the course.

Since I asked the participants to document why they were taking this course, I figured the least I could do is create a similar post explaining why I agreed to shepherd this course and why I made some of these initial choices.

I’m approaching the concept of data visualization pretty broadly. The course is broken into two main components. The first portion is looking at their own portfolio and how they might think it through in terms of data visualization1 and then moving on to broader/deeper applications of data visualization (connected to their personal research). Part of the reason I want to start with the portfolio is that the data is personal and it’s easier to get a feel for how real/accurate any visualizations you make are. We’ll also be able to establish a decent foundation of web literacy, design, accessibility, etc. prior to applying them to more sophisticated visualizations. Essentially, it’s about starting out on more familiar ground before venturing out into increasing complexity.

The second section will focus on figuring out a set of tools and concepts that will directly apply to things the student wants to do. A chunk of this will be looking at and trying out things with various tools and then thinking through the results– both the final data visualization and the process of creating it. There’s some balance of tool cost, author time/effort, the final product’s closeness to your dreams, the tool’s lifespan, etc. that ought to be part of one’s pattern of analysis.2

But I wandered a bit,3 why’d I agree to do this? Teaching a class is actually a financial negative for me. It is absolutely not worth the time it takes to do it compared to what I can make building websites or consulting or editing video. At the root of this particular adventure is the same thing that keeps me entertained in my current job. This looks like a chance to take a deeper dive into data visualization but one driven and constrained by sociology. That’s fun stuff. I think that the audience sought by public sociology, and digital sociology in particular, is ripe for things like interactive explanations. I think that the same people who are attracted to a degree in digital sociology will be interested in pushing the envelope in what’s possible with interactive data visualizations. I hope to help show what’s on the edge of possibilities right now while still grounding things in the desire to reach a diverse audience and to create understanding (or maybe come to new understandings).

There’s also the drive to get into a set of tools. I need to take a fairly wide look around on a regular basis as the tools and frameworks4 are changing so fast and getting increasingly powerful. I hadn’t looked at Tableau in a while and then I saw this parakeets example. Crazy stuff. Keshif continues to improve. R and D3 beckon on the programming side of things and I’ve been revisiting explorable explanations, datasketch.es, basic functions of particular data visualization types, minimalism, Feltron Reports, all kinds of good stuff.

It’s also worth running a class every so often. Can I practice what I believe? Can I do it in a way that’s acceptable to people who are probably used to pretty typical courses?5

Outside this course, I think the knowledge I gain will be useful and I’m going to try to use the course to create content that’s applicable to most people looking to pursue this work.


1 . . . and of course workflows.

2 There’s a bit more to this and it might be worth some more effort to sketch it out with some actual articulation. So this is a footnote saying that I should write better blog posts with actual complete thoughts in them.

3 If I were fancier I’d have digressed.

4 Is everything just a tool?

5 And this is all before I started reading The Ignorant Schoolmaster

Weekly Web Harvest for 2018-01-07

Photography – #161

Photography – #161

Photography – #161

Photography – #161

Photography – #161

Photography – #161

Photography – #161

Photography – #161

A post shared by @twwoodward on

Once again, it’s been a good while between photography updates. I’ve fallen in a bit of a slump and that tends to result in low photography production. Here’s to more and better photos in the future.

Weekly Web Harvest for 2017-12-31

Contact us

Academic Learning Transformation Lab - ALT Lab
1000 Floyd Ave, Suite 4102 | Richmond, Virginia 23284
altlab@vcu.edu | 804-827-5181

Last updated: September 26, 2017

Virginia Commonwealth University