I just completed a new project at the TV station creating a database for Packing Slips to send back equipment for repair. We wanted to not only be able to print out these slips, but also keep a record of them to easily see the history of repairs different devices needed. Our Mediawiki on site already had a lot of information about all of the devices in the station so I wanted to use that data as well as make an app that would scale well over time. In my research I found that not only did Mediawiki have an API that I could tap into, but it’s been shipped with it for years.
Mediawiki API
Connecting to your Mediawiki API is very straightforward.
http://path/to/wiki/api.php?
From here you add a format, actions and parameters to this end point to collect your data. I was only interested in making GET requests since I only needed to pull some data out of our device database so I didn’t have to worry about logging in or other more complicated API actions. You can read more about the Mediawiki API in their documentation.
Here is what my api request looked like for a random device in our database.
http://###.###.###.###/mediawiki/api.php?format=json&action=parse&page=927-000245
{"parse":{"title":"927-000245","revid":3269,"text":{"*":"<div class=\"assetPage\">\n<p><span class=\"assetTitle\" style=\"font-size:2em; font-weight:bold;\">QC 6 Vector Scope<\/span>\n<\/p>\n<table>\n\n<tr>\n<td>\n<table class=\"wikitable\" style=\"float:left;margin:0;\">\n<caption> Device Data\n<\/caption>\n<tr>\n<th> Manufacturer\n<\/th>\n<td> <a href=\"\/mediawiki\/index.php\/Tektronix\" title=\"Tektronix\">Tektronix<\/a>\n<\/td><\/tr>\n<tr>\n<th> Device Name\n<\/th>\n<td> QC 6 Vector Scope\n<\/td><\/tr>\n<tr>\n<th> Serial Number\n<\/th>\n<td> ######\n<\/td><\/tr>\n<tr>\n<th> Model Number\n<\/th>\n<td> ####\n<\/td><\/tr>\n<tr>\n<th> Software Version\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Location\n<\/th>\n<td> <a href=\"\/mediawiki\/index.php\/Equipment_Rack_76\" title=\"Equipment Rack 76\">Equipment Rack 76<\/a>\n<\/td><\/tr>\n<tr>\n<th> IP Address\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Router Source\n<\/th>\n<td>\n<\/td><\/tr><\/table>\n<\/td>\n<td>\n<table class=\"wikitable\" style=\"float:left;margin:0;\">\n<caption> Address for returns\n<\/caption>\n<tr>\n<th> Ship To Contact:\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Attn:\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Street Address\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> City State Zip\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> RMA Number\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Customer Contact Name\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Customer Contact Number\n<\/th>\n<td>\n<\/td><\/tr>\n<tr>\n<th> Reason for Return\n<\/th>\n<td>\n<\/td><\/tr><\/table>\n<\/td><\/tr><\/table>\n<\/div>\n<div class=\"packingSlip\">\n<div class=\"printHeader\">\n<p><a href=\"\/mediawiki\/index.php\/File:Working.PNG\" class=\"image\"><img alt=\"Working.PNG\" src=\"\/mediawiki\/images\/thumb\/c\/c0\/Working.PNG\/400px-Working.PNG\" width=\"400\" height=\"90\" \/><\/a>\n<span style=\"font-size:2em; font-weight:bold;\">Packing Slip<\/span>\n<\/p><p>May 24 2015\n<\/p>\n<\/div>\n<div class=\"printRow\">\n<div class=\"printAddressCell\">\n<table>\n\n<tr>\n<td style=\"width:120px;\">Address:\n<\/td>\n<td>#######\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>########\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>Phone: #######\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>Fax: #######\n<\/td><\/tr><\/table>\n<\/div>\n<div class=\"printAddressCell\">\n<table>\n\n<tr>\n<td style=\"width:120px;\">Ship To:\n<\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>Attn:\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td>\n<\/td>\n<td>\n<\/td><\/tr><\/table>\n<\/div>\n<\/div>\n<div class=\"printRow\">\n<div class=\"printOrderCell\">\n<table>\n\n<tr>\n<td style=\"font-weight:bold;width:200px;\">RMA Number:\n<\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td style=\"font-weight:bold\">Customer Contact:\n<\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td style=\"font-weight:bold\">Customer Phone #:\n<\/td>\n<td>\n<\/td><\/tr><\/table>\n<\/div>\n<\/div>\n<div class=\"printRow\">\n<table class=\"wikitable\" style=\"width:100%\">\n<tr>\n<th> Product\n<\/th>\n<th> Description\n<\/th>\n<th> Model\n<\/th>\n<th> Serial Number\n<\/th>\n<th> Ship Quantity\n<\/th><\/tr>\n<tr>\n<td> <a href=\"\/mediawiki\/index.php\/Tektronix\" title=\"Tektronix\">Tektronix<\/a>\n<\/td>\n<td> QC 6 Vector Scope\n<\/td>\n<td> 1720\n<\/td>\n<td> #######\n<\/td>\n<td> 1\n<\/td><\/tr>\n<tr>\n<td style=\"height:1em;\"> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td style=\"height:1em;\"> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td style=\"height:1em;\"> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td>\n<\/td><\/tr>\n<tr>\n<td style=\"height:1em;\"> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td> <\/td>\n<td>\n<\/td><\/tr><\/table>\n<\/div>\n<div class=\"printRow\">\n<table>\n<tr>\n<th> Reason For Return\n<\/th><\/tr>\n<tr>\n<td>\n<\/td><\/tr><\/table>\n<\/div>\n<\/div><a href=\"\/mediawiki\/index.php\/File:1720.jpg\" class=\"image\"><img alt=\"1720.jpg\" src=\"\/mediawiki\/images\/3\/34\/1720.jpg\" width=\"600\" height=\"449\" \/><\/a>\n\n<!-- \nNewPP limit report\nPreprocessor node count: 64\/1000000\nPost-expand include size: 2272\/2097152 bytes\nTemplate argument size: 91\/2097152 bytes\nExpensive parser function count: 0\/100\n-->\n"},"langlinks":[],"categories":[{"sortkey":"","*":"Asset_Tags"}],"links":[{"ns":0,"*":"Equipment Rack 76","exists":""},{"ns":0,"*":"Tektronix","exists":""}],"templates":[{"ns":10,"*":"Template:Asset Tag","exists":""}],"images":["Working.PNG","1720.jpg"],"externallinks":[],"sections":[],"displaytitle":"927-000245"}}
As you can see its not as easy to pull specific information you want when the api returns a page like this. All of the content in the page is wrapped in the text object with a key of *.
I needed to get into all of that text and just pull out the data. I used Semantic Mediawiki Forms to create this database so I didn’t have access to give the table cells I needed a class or an id that I could access through javascript. I came up with a solution that might not be the cleanest, but it worked.
My Javascript Solution
I decided I would use Javascript to connect to the API, then spit out the returned html into a hidden div on my page. Then with jQuery I would parse through the html and grab the information I needed. Since every device page would be the same I only needed to work out the jQuery the one time. Here is my final working solution:
var assetData = {};
function loadData() {
var $body = $('body');
var $returned = $('#returned');
var asset = $('#wiki-asset').val();
var wikiRequestTimeout = setTimeout(function() {
$('body').append("Failed to get wikipedia resources");
}, 8000);
var wikiUrl = "http://###.###.###.###/mediawiki/api.php?format=json&action=parse&page=" + asset;
$.ajax({
url: wikiUrl,
dataType: "jsonp",
success: function( response ) {
var page = response.parse;
var title = page.displaytitle;
var text = page.text["*"];
$returned.html(text);
var $table = $('.wikitable tbody').first();
$table.children('tr').each(function(e) {
var key = $(this).find('th').text().trim();
var value = $(this).find('td').text().trim();
assetData[key] = value;
});
insertData(assetData);
clearTimeout(wikiRequestTimeout);
}
});
return false;
}
$('#wiki-submit-btn').on('click', function(e) {
e.preventDefault();
loadData();
var asset = $('#wiki-asset').val();
$('#asset-id-form').hide();
$('#slip_assetTag').val(asset);
});
function insertData(assetData) {
$('#slip_manufacturer').val(assetData['Manufacturer']);
$('#slip_deviceName').val(assetData['Device Name']);
$('#slip_modelNumber').val(assetData['Model Number']);
$('#slip_serialNumber').val(assetData['Serial Number']);
}
First I assign a couple jQuery objects for the body as well as the returned div that is hidden from view. I also created a global variable that I would append my data to.
Next I grab the page name that was entered into the form and append that onto the wikiUrl variable and use this to make the ajax request.
After a successful connection to the mediawiki server I create a variable page that is the full response from the connection. That’s the json object that is above. Next I assign a new variable to the text objects * key and then insert that block of html into my returned div. Next I create a jQuery object of the first table that was in that block of html. That first table is the only one that I care about so I’m working my way down to the data that I need. Next I go through each of the children table rows of that table and assign the th to the key, and the td to the value of my assetData variable. After that I run a function that assigns those values from my assetData variable to the values of my form.
As I said it might not be the cleanest way of doing things since I am injecting a bunch of html in a hidden div but I found it works and returns the data very quick. After I setup the program on our internal web server it only takes a second to get the data and insert them into the form.
This is only one part of the application I built for creating a packing list database. I created it with Codeigniter v3 but I’ll save that write up for another post.
Leave a Reply