MediaWiki Query and/or WikidataQuery to find Wikipedia article - angularjs

This isn't so much a question abut AngularJS as it is about the Wikimedia and Wikidata query API's.
Although I am trying to display the content of a Wikipedia article in AngularJS after doing a certain query that isn't the problem. I already know how to display it... the problem is the search for an article or articles.
I'm trying to query Wikipedia by historical event date as well as by geo-location.
Let's pick a random event, any event. Let's say "1986 Mozambican Tupolev Tu-134 crash". Link: http://en.wikipedia.org/wiki/1986_Mozambican_Tupolev_Tu-134_crash
From the article, I can see the exact date: 19 October 1986
as well as the geo-location of the event: -25.911389, 31.957222
I'm trying to build a search in AngularJS that can use either a date-range and/or geolocation coordinates to find an event.
I am aware that mediawiki has a geolocation API now, and I am able to find the above event by either keyword or coordinates. The result also turns up any other articles that exist within a certain radius around it using this:
http://en.wikipedia.org/w/api.php?action=query&list=geosearch&gsradius=10000&gscoord=-25.911389|31.957222&prop=extracts|info&exintro&titles=1986_Mozambican_Tupolev_Tu-134_crash&format=json&explaintext&redirects&inprop=url&indexpageids&format=json&callback=JSON_CALLBACK
However, there is no way to do a search with mediawiki by a point in time or the date of the event.
Wikidata on the other hand has two methods of searching data... it has a date range as well as geolocation.
However, when a query is run, I have no idea what is being returned.
For example, when I use this query string:
https://wdq.wmflabs.org/api?q=AROUND[625,-25.911389,31.957222,5]%20AND%20BETWEEN[585,1985,1987]
it returns this:
{"status":{"error":"OK","items":1,"querytime":"544ms","parsed_query":"(AROUND[625,-25.9114,31.9572,5]
AND
BETWEEN[585,+00000001985-00-00T00:00:00Z,+00000001987-00-00T00:00:00Z])"},"items":[950273]}
Using wikidata's query tool:
http://tools.wmflabs.org/autolist/autolist1.html?q=AROUND[625,-25.911389,31.957222,5]%20AND%20BETWEEN[585,1985,1987]
I can see that 950273 represents the article in some way. I'm just not sure how to use that to direct me to the actual article in wikipedia.
I don't know what "items": [950273]" represents, or how to use it to get me to the wikipedia article and display the contents of that article in AngularJS.
Is there a way to do both a query by date of the historical event as well as by geolocation. Either by using mediawiki or wikidata or a combination of the two?
EDIT:
This is the solution to my question above. It seems like a bit of a hack... but it works. Good enough for now. Here is my controller.
.controller('WikiQueryCtrl', ['$scope', '$http', function($scope, $http) {
$http({
//these geo coordinates and the date ranges will eventually be dynamic.
url: 'https://wdq.wmflabs.org/api?q=AROUND[625,-25.911389,31.957222,5]%20AND%20BETWEEN[585,1985,1987]&callback=JSON_CALLBACK',
method: 'jsonp'
})
.success(function(response) {
var items = response.items;
$scope.jason = items;
var wikiDataString = 'http://www.wikidata.org/w/api.php?action=wbgetentities&format=json&ids=Q' + items + '&props=sitelinks%7Csitelinks%2Furls&callback=JSON_CALLBACK';
$http({
url: wikiDataString,
method: 'jsonp'
})
.success(function(response2) {
$scope.jason2 = response2;
var url = response2.entities["Q" + items].sitelinks.enwiki.url;
var wikipediaTitle = url.substr(24, url.length);
var wikipediaURL = 'http://en.wikipedia.org/w/api.php?action=query&prop=extracts|info&exintro&titles=' + wikipediaTitle + '&format=json&explaintext&redirects&inprop=url&indexpageids&format=json&callback=JSON_CALLBACK';
$http({
url: wikipediaURL,
method: 'jsonp'
}).success(function(response4) {
var query = response4.query;
var pageID = response4.query.pageids;
var title = response4.query.pages[pageID].title;
var fullurl = response4.query.pages[pageID].fullurl;
var content = response4.query.pages[pageID].extract;
$scope.title = title;
$scope.content = content;
$scope.fullurl = fullurl;
$scope.jason = query;
});
});
});
}]);

950273 is the Wikidata entity ID. You find the entity itself at https://www.wikidata.org/wiki/Q950273
By using that you can query Wikidata for the articles connected to it using the API and the action wbgetentities and ask for sitelinks and sitelinks/url like this: http://wikidata.org/w/api.php?action=wbgetentities&format=json&ids=Q950273&props=sitelinks%7Csitelinks%2Furls
Or try going to the language version of your choice directly using Special:GoToLinkedPage. Eg. https://www.wikidata.org/wiki/Special:GoToLinkedPage/enwiki/Q950273

Related

Tag Manager to two ga properties

I have implemented Google enhanced Ecommerce Via GTM for GA Property (New), keeping the old classic analytics code in the webiste, Now I removed the old classic code and pushing the events data from the same GTM account to (old) GA property (Replicated the Tags with different GA Property, Reference url : http://www.kristaseiden.com/step-by-step-adding-a-second-ga-property-via-google-tag-manager/).
The first GA property transactions are used to track properly, But after adding another GA property the transactions and all other events are not tracking accurately. In both the accounts Transactions are dropped to 50 percent.
Could someone help me. Thanks in advance.
You can create a custom JS variable:
function() {
var newTrackingId = 'UA-XXXXXX-XX'; // Replace here
var globalSendTaskName = '_' + newTrackingId + '_originalSendTask';
return function(customModel) {
window[globalSendTaskName] = window[globalSendTaskName] || customModel.get('sendHitTask');
customModel.set('sendHitTask', function(sendModel) {
var hitPayload = sendModel.get('hitPayload');
var trackingId = new RegExp(sendModel.get('trackingId'), 'gi');
window[globalSendTaskName](sendModel);
sendModel.set('hitPayload', hitPayload.replace(trackingId, newTrackingId), true);
window[globalSendTaskName](sendModel);
});
};
}
And then add this as a custom task on fields to set:
Hope it helps!
PS: Here is a more detail post from Simo Ahava.

Angularjs filtering JSON data based on date range

I have an angular app which will displayed data from database, the app will fetch the data as json from the server.
in the form i have a checkbox and 2 input type=date which is one is start date and the other is end date, the checkbox will enable or disable those input type=date.
So i will fetch all data from database, whenever the user enable the checkbox, they will have select the date and the app will filter based on their selection, if checkbox unchecked then the app will displayed all data.
This is how i populate the data
app.js
$http({
method: 'POST',
url: "api/Enquiry.php",
data: {
matcode:matcode,
location: location,
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
}
}).then(function(response){
$scope.Tablelist = response.data;
console.log(JSON.stringify(response.data));
}, function(response){
console.log("failed");
})
So when the input type date is change, how can i just display the data in Tablelist according to the selected start and end date >=startdate and <=enddate
Is it possible to use the builtin filterFilter? if yes, kinda point me to the right direction.
Here is how you filter between dates, the key is the filter you see in the docs it takes a function.
The filter function I created looks like:
vm.range = function (value, index, array) {
if (value.date < new Date('2016-05-12T12:00:00Z') && value.date > new Date('2016-05-12T09:00:00Z')) {
return true;
}
return false;
}
So the value is the current object to compare, I have chosen two arbitrary dates (these can be gotten from the UI). All I do is just use the logical operators.
If you return true from the function then you are saying keep it. if you return false then you are saying nope!
This can be simplified to:
vm.range = function (value, index, array) {
return (value.date < new Date('2016-05-12T12:00:00Z') && value.date > new Date('2016-05-12T09:00:00Z'));
}
Simple days.
Oh and UI is easy:
<li ng-repeat="f in vm.original | filter: vm.range">
{{f.date}}
</li>
It use the pipe symbol | to defined the next part is going to be a filter of some kind. We use the filter filter (Yes, that is how stupid angular 1 is....) Then we just pass the vm.range function expression to this filter and it will be executed against each object, very similar to a foreach
In relation to your example of if being returned in the $http response, you can still do the UI the same as mine, because of two-way binding as soon as $scope.Tablelist is updated, the filter is re-run over the top. Just remember this isn't very performant for large data sets, so you may want to pull the filter into the javascript:
$scope.Tablelist = $filter('filter')(response.data, $scope.range);
Why is this more performant then in the UI. Because it doesn't cause unnecessary UI reflow till the data is filtered, I am unsure as to whether the filter does this in the UI, but I know this won't.

Query specific sheets of a google spreadsheet

I am attempting to use a google spreadsheet as a temporary database. I have followed the instructions at the folowing tutorial and everything is working fine
http://www.alatechsource.org/blog/2012/05/using-the-google-spreadsheets-data-api-to-build-a-recommended-reading-list-code-words.h
The jquery that gets the data I have copied below for ref
I wondered if it would be possible (using the same or similar code), assuming there are multiple sheets within the spreadsheet to query just one at a time. So for example you could run the below code but only for say sheet 4.
I have tried adding sheet ref # from the web URL (i.e #gid=1) at the end of the numeric code so
"https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE/od6/public/values?alt=json-in-script&callback=?"
becomes
"https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE#gid=1/od6/public/values?alt=json-in-script&callback=?"
but this does not work, the code only seems to loop through the first sheet
Can anyone advise on this?
Any help is much appreciated
<script type="text/javascript">
$(document).ready(function() {
//source file is https://docs.google.com/spreadsheet/ccc? key=0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE
$(function listBooks() {
$.getJSON( "https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE/od6/public/values?alt=json-in-script&callback=?",
function (data) {
$('div#book-list').append('<ul class="items"></ul>');
$.each(data.feed.entry, function(i,entry) {
var item = '<span style="display:none">' + entry.id.$t + '</span>';
item += '<img src="http://covers.openlibrary.org/b/isbn/' + entry.gsx$isbn.$t + '-S.jpg"/>';
item += '<span class="meta">' + entry.title.$t + '';
item += '<br/>Author: ' + entry.gsx$author.$t;
if (entry.gsx$notes.$t) {
item += '<br/>Description: ' + entry.gsx$notes.$t;
}
$('.items').append('<li>' + item + '</span></li>');
});
});
});
});
</script>
Ok, I have a solution for anyone who is interested, although it is not as sensible as I would have hoped
Thanks to the below blogpost
http://damolab.blogspot.co.uk/2011/03/od6-and-finding-other-worksheet-ids.html
It seems that the id for specific sheets is the /od6/ part of the URL and od6 is the default name given to the first, default, sheet
"https://spreadsheets.google.com/feeds/list/0Ak0qDiMLT3XddHlNempadUs1djdkQ0tFLWF6ci1rUUE/od6/public/values?alt=json-in-script&callback=?"
In the above blog post there is an example of how to find out the IDs of specific sheets if you need. There doesn't seem to be a clean logic to it but changing it will spit out the data from specific sheets so it works.
I realize this is an old post, but I have been fighting this for a while. I made my own library to take care of this and I hope this saves someone many hours of research and playing with the google api. The general format is almost verbatim from the google documentation and I removed error checking to stay concise. What I could not find in the google documentation was how to query a tab that isn't the first one. I think this is what we are looking for here.
Based on what you already have, this should answer it.
table_name_here/gviz/tq?sheet=tab_name_here
As an example for all those that come after you:
https://docs.google.com/spreadsheets/d/17mbdvl1jVpqj42E3BSX34q9XUA2PbYubYKpVeypbHLA/gviz/tq?sheet=Sheet1
This could be used as the table_string in the code below.
'SELECT *'
This could be used as the query_string in the code below.
Within context:
function gimmeATable(){
var query = new google.visualization.Query('table_string');
query.setQuery('query_string');
query.send(handleQueryResponse);
}
function handleQueryResponse(response){
var data = response.getDataTable;
drawTable(data);
}
function drawTable(data){
var table = new google.visualization.Table(document.getElementById('div1'));
table.draw(data, {showRowNumber: true});
}
And don't forget to include the google api as well as package type. For this you only need a table.
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
google.load("visualization", '1', {packages:['table']});

Multiple resources in one view

I want to display a list of images and their respective comments. Like:
Image url | Format | Comments
http://example.com/img.jpg | 1280x420 | [Comment 1], [Comment 2] ...show all ...show all
http://example.com/img2.jpg | 630x590 | [Comment 1], [Comment 2] ...show all
I have two resouces: /images and /comments/{image_id}
What is the recommended way to fetch the comments for each image to be able to display them on the same row? Does Marionette have a helper for this?
In my opinion, these look like a good place to use relational models. Backbone doesn't support these out of the box, so you'll need a plugin. Have a look at Backbone-Relational or supermodel.js. These projects provide better forms of model nesting than the default implementation. From there, use nested composite views to render the collections.
From what I know Marionette does not have such helper. I think you can use something simple like:
var ImageComments = Backbone.Collection.extend({
initialize: function(models, options) {
options || (options = {});
this.imageId = options.imageId;
Backbone.Collection.prototype.initialize.apply(this, arguments);
},
urlRoot: function() {
return 'comments/' + this.imageId;
}
});
var id = 1,
image = new Image({ id: id }),
comments = new ImageComments(null, { imageId: id });
$.when(image.fetch(), comments.fetch()).done(function() {
// .. do your things with image & comments
});
This describes simple case, if that's commonly used in your application you might want to implement your own fetch method (e.g. for image, that will also fetch comments) or use plugins like Backbone-relational or Backbone-associations
You can use nested composite views.
http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/
You can also do old fashioned in template loops for the comments
http://www.headspring.com/an-underscore-templates-primer/

Angular JS dynamic JSON building

I want to start with a base JSON, ie [], and provide a tree like structure with options to edit node (change or extend existing keys/values), add sibling (create new entry), and add child (extend the json to become a map, ie add { "field1" : "value1", "field2" : "value2"} to "data".
Seems like the best way to do this is to bind a json scope value to a tree structure. I am about to build one myself, but thought I would check to see if it's been done already....
This sort of feature would allow someone closer to the business to define and refine the data model, as well as make simple edits. Think the php myadmin interface, or the django admin page.
This fiddle will give you a headstart. It was actually referenced from this question. It does not deal with object parameters, just nodes in the tree, but adding these should be pretty straightforward from a controller standpoint. The real challenge is in developing a good-looking view. Here is the controller part (just to make SO happy):
angular.module("myApp", []).controller("TreeController", ['$scope', function($scope) {
$scope.delete = function(data) {
data.nodes = [];
};
$scope.add = function(data) {
var post = data.nodes.length + 1;
var newName = data.name + '-' + post;
data.nodes.push({name: newName,nodes: []});
};
$scope.tree = [{name: "Node", nodes: []}];
}]);
Check it out json-tree angularjs directive.
This looks like a good implementation of what you're looking for.

Resources