<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- generator="Chisimba" -->
<rss version="2.0">
    <channel>
        <title>Chisimba JabberBlog</title>
        <description>Jabber based microblogging</description>
        <link>http://www.paulscott.za.net/index.php?module=jabberblog</link>
        <lastBuildDate>Thu, 20 Jun 2013 04:22:27 +0200</lastBuildDate>
        <generator>Chisimba</generator>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_27029_1370259714</link>
            <description><![CDATA[Mahout MongoDBDataModel setup in Spring-3.2.2

Mahout (http://mahout.apache.org) is a set of machine learning libraries licensed under the Apache Software License for doing a bunch of fun stuff with machine learning and recommendations (making use of the Mahout Taste libraries). This opens up a lot of very nice possibilities for data mining of various data sets within your application(s)

The setup of Mahout to work with spring-data-mongodb is a little tricky sometimes (especially when you are using Spring-3.2.2 or later) as the Mahout MongoDB driver is somewhat outdated. Luckily this is pretty easily remedied with a little bit of extra tweaking.

I will assume that your spring-data-mongodb is up and running in your project already, as the crux of this post is the integration bit...

That being said, you will need to import the Mahout libraries into your pom.xml file, starting with mahout-core and at least mahout-integration. You will also need uncommons-math and some other libs, depending on what you would like to achieve anyway.

The tricky part comes in on the dependency heirarchy of your POM file. If you take a look at the dependency heirarchy for the Mongo driver, you will notice that Mahout will depend on a slighly (OK, very) outdated version of the MongoDB driver. This needs to be removed, so that the [b]only[/b] MongoDB driver is the one that you get with the latest version of Spring-data-mongodb. This should be around 2.20 or so IIRC.

It is probably also a good idea to grab the latest mahout-integration code from github as well, to make sure that you are all up to date.

Once that is done, you should be able to follow any of the (admittedly sparse) Mahout tutorials on using the MongoDBDataModel. One huge caveat is that if you do not follow the schema defined by Mahout in your collection that you would like Mahout to connect to, you will need to override the default constructor with your configs.

example:

[code]
MongoDBDataModel dbm = new MongoDBDataModel(
                    String mongoHost,
                    int mongoPort,
                    String mongoDBName,
                    String collection, 
                    boolean manage, 
                    boolean finalRemove,
                    dateFormat, 
                    String useridentifier, 
                    String itemIdentifier, 
                    String preferenceField,
                    String mappingCollection
);
[/code]

Note that the data model construction will fail horribly without a specified mappingCollection. This is a dynamically created collection that will hold mapping results, so it can be called just about anything, but [b]it must be declared![/b]

So, taking the above connection constructor, we can provide an example constructor that looks something like:
[code]
private MongoDBDataModel getModel() throws UnknownHostException,
            MongoException {
        MongoDBDataModel dbm = new MongoDBDataModel(
                "localhost",
                27017,
                mydb,
                mydatacollection, 
                false, // leave this as false unless you want Mahout to manage i.e. delete your entries in your collection! 
                false,
                dateFormat, 
                "customerId", 
                "item", 
                "rating",
                "likemap"
        );
        return dbm;
    } 
[/code]

You can then go ahead and use your MongoDBDataModel in the rest of your code. The rest of the API is pretty simple to understand, so if you would like some more details on the various bits and pieces of Mahout, please leave a comment and I will attempt to find time to get it done too!
]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_71854_1367822975</link>
            <description><![CDATA[YouTube has not won anything...

This is a response to Eric Schmidt's claim that YouTube has won the battle against TV before it started.

See article at http://www.thedenverchannel.com/news/u-s-world/youtube-has-already-won-battle-with-television-executive-claims

Firstly, what battle? Secondly, YouTube has won nothing, especially in Africa. Where it counts by the way. Why does that count? I'll give you a billion reasons.
Let us disambiguate some of the statements.

1. YouTube cannot even be compared to linear TV, so that whole argument is marketing troll, or stupidity. We'll call it trolling for now.
2. What I [b]think[/b] Schmidt was thinking when he uttered this nonsense, is that the new way of doing TV is Video On Demand (VOD)
3. YouTube as a service provider, with paid for VOD, [b]could[/b] be a player, sometime in the near future.
4. YouTube has a very long way to go before they are a serious contender in the space.
5. Anyone paying USD for TV services in Africa is going to have a bad time.

Let me explain a bit more. People are used to using YouTube for 10 minute clips. There will have to be a major mindset change in user interaction to change that behaviour. 
Most folks view YouTube clips on PC or small screen devices and have pretty much no idea how to consume YouTube video in any other way. People in Africa, still are familiar with opening browser tabs, loading 3 videos overnight (buffering) and then playing them hours or even a day later. This may not be the case everywhere, of course, but I have seen this recently in my travels. People that consume YouTube occasionally via HTPC systems like XBMC are few and far between, certainly not the majority, and certainly want to see that in 1080i/p at least resolution, requiring a faster than 4Mb/s ADSL line (very expensve).

As an ADSL subscriber with a 4Mb/s line, I can tell you that (with discounts), it is more expensive than a Premium DStv package (includes Transactional VOD service).

On the other hand, we have TV. Let's forget linear TV, because that is last season, for sure. People want the VOD style, "I want to watch this now". What needs to happen is that we focus on getting cheaper devices into every home and make VOD services available to more people. This is currently happening (check DStv's website for new packages) and with my own daily work, there are many more very good things in the pipeline...

If YouTube think that this is an easy task, they have a lot of surprises coming.

]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_16677_1356016481</link>
            <description><![CDATA[Speed tests on MTN Wireless "broadband" 

IP_ADDRESS	TEST_DATE	TIME_ZONE	DOWNLOAD_MEGABITS	UPLOAD_MEGABITS	LATENCY_MS	SERVER_NAME	DISTANCE_MILES
41.121.61.59	9/20/2012 11:13 AM	GMT	1.28	1.73	80	Randburg	0
41.121.124.103	11/25/2012 8:43 AM	GMT	0.2	0.35	70	Johannesburg	0
41.121.124.103	11/25/2012 8:57 AM	GMT	0.05	0.28	1389	Medford, OR	10500
41.121.238.126	12/1/2012 5:16 AM	GMT	0.35	0.35	56	Johannesburg	0
41.121.238.126	12/1/2012 5:24 AM	GMT	0.37	0.31	413	Berkeley	10550
41.121.238.126	12/1/2012 11:18 AM	GMT	0.14	0.03	66	Johannesburg	0
41.121.238.126	12/2/2012 5:43 AM	GMT	0.21	0.35	75	Johannesburg	0
41.121.207.164	12/20/2012 3:11 PM	GMT	0.25	0.33	153	Johannesburg	0
]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_42641_1356005365</link>
            <description><![CDATA[Transcript of Facebook messages between MTN and I since December 3. I started this conversation on or before November 24th 2012 with them on twitter and client chat/email.

Conversation started December 3
12:46pm
Paul Scott

Any response to my post?

12:55pm
Paul Scott

My 3g connection is useless. Signal strength is best (reports full upstream and downstream speeds) but bandwidth is very low and latency very high. The signal strength is high due to the fact that I had to buy an additional antenna as well. What I suspect the issue is is that buildings in our particular area block line of sight with the tower (which is pretty close by) and therefore affect the signal. I would like the contract cleanly cancelled and I will return the b106 device too, in order to get a fixed line solution in place.
	
	

December 5
10:43am
Paul Scott

Well? What is happening now? A little info can go a long way. I am getting VERY tired of this as it has now severely affected my ability to complete an online course.

10:50am
MTN South Africa

Hi Paul,

Thank you for your post, apologies for the inconvenience caused. Please note that you need to log a call with coverage on 0839001212 / coverage@mtn.co.za they will send out a technician to address the issues raised, if the issue cannot be resolved they will issue a letter for a cancellation.

Regards
Nonz.
MTN SM Team

12:12pm
Paul Scott

WHY DID YOU NOT TELL ME THAT 2 DAYS AGO? You are STILL WASTING my time and testing my patience. GRRRR!

12:19pm
Paul Scott

Also, I have sent the form to at LEAST 2 DIFFERENT people regarding coverage and reported it numerous times via chats. PLEASE just get this done, my wife (a professional photographer ) has now lost clients due to bad uploads, I have missed a week of my online course and now will probably fail, AND STILL NOBODY AT MTN GIVES A SHIT!

2:56pm
MTN South Africa

HI Paul.

Once again apologies for the inconvenience caused. Please note that you can follow up with the ref number from coverage.

Regards
Nonz.
MTN SM Team

December 6
8:02am
Paul Scott

What reference number? I now have at least 3 different ones. This is as frustrating as pulling my own teeth with plastic pliers

8:31am
MTN South Africa

Hi Paul,

I understand your frustration, the number I am talking about is the one coverage issues for a logged query.

Regards
Nonz.
MTN SM Team

December 7
11:50am
Paul Scott

Have now sent ANOTHER email. Will reply with that outcome as soon as I get an answer (probably never)

12:05pm
Paul Scott

6038602 is the reference. Make this happen finally please.

12:29pm
MTN South Africa

Hi Paul.

The follow needs to be done with coverage.

Regards
Nonz.
MTN SM Team

12:31pm
Paul Scott

So now I am not your problem anymore. Great. Another successful client ticket closed for you. Congratulations.

I am so sick and tired of this crap.

Give me someone to call and resolve this today.

Give me a contact for head office.

Give me a contact for coverage.

Give me a break.

1:18pm
MTN South Africa

Hi Paul.

I have forwarded your reference to coverage as a follow up to your logged query.

Regards
Nonz.
MTN SM Team

December 10
11:49am
Paul Scott

Any news? I need to get a proper internet connection now. I have lost far too much productivity to MTN

December 11
3:41pm
Paul Scott

Well?

December 12
7:01am
Paul Scott

Anything? No? Small claims court maybe?

8:58am
MTN South Africa

Hi Pau.

There are no records on the ref number you sent, I have received feedback from coverage on this. Please the correct ref number and your cell phone number.

Regards
Nonz.
MTN SM Team

10:21am
Paul Scott

Coverage ref: #6038602
Kagiso ref: REQ:1473135
Sbongile ref: REQ:1473135 [#6007074]

Most of the others look to be the same ticket(s).

My phone number is 083 436 0955 (again)

What is the feedback from coverage? I want this contract cancelled so that I can get a decent service now.

Friday
6:13am
Paul Scott

Well? Anything? Come on! Why do I have to beg for an answer?

7:31am
MTN South Africa

Hi Paul.

I have logged a call for you and the ref is #0048807 , please follow up with coverage.

Regards
Nonz.
MTN SM Team

8:56am
Paul Scott

COVERAGE HAS RESPONDED ALREADY WITH THE PREVIOUS REF. They said that the REEA tower was offline. WHAT THE ACTUAL FUCK DO I HAVE TO DO NOW?

PLEASE, just fix this. I cannot go on like this. I have failed my final exam because I could not log in.

11:03am
Paul Scott

Copied here too then:

Y'ello!

Please be advised the is a tower off in the area tower at REEA Nursery site#1602 ref#SMC/00003397665 support is working on resolving the tower we apologize on the inconvenience caused

Kind regards,
Sizakele Masina
National Quality and Coverage Help Desk

(case # 6038602 )

11:33am
MTN South Africa

Hi Paul.

You have received communication from coverage on your query, please advise on how you would like me to assist.

Regards
Nonz.
MTN SM Team

Friday
2:38pm
Paul Scott

I WOULD LIKE MY CONTRACT CANCELLED AS YOU DO NOT PROVIDE THE SERVICE THAT I AM CURRENTLY PAYING FOR.

I thought we had been through this already. Are you TRYING to piss me off?

Tuesday
12:39pm
Paul Scott

WELL? What is the bullshit excuse going to be THIS week? Are we cancelling my useless contract or are you stringing me along another month? Hmmm?

Today
1:46pm
Paul Scott

Still no call. Sigh]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_29655_1353824970</link>
            <description><![CDATA[MongoDB Index creation trick that will save you time and frustration in production

Right, so you have a huge dataset in MongoDB in production and your replicaSet is purring away serving all your data. What fun, when suddenly, someone decides that we need to add another index to the database. Quickly, you think, we have 2 options:

1. Lock up the database and do a fast foreground index create and hope we don't lose too much money on failed/queued transactions OR
2. Do a really slow background index on the data and hope for the best with respect to new documents coming in.

Either of these scenarios is not ideal in any mans language, so there is, of course, a trick...

Remember, you are running a replicaSet? Yeah, so what does a replicaSet do? Replicates, yeah! OK, so we can use that to our advantage!

Take a single node [b]out[/b] of the replicaSet and then build a nice foreground index on it. Then insert the node back into the replicaSet and let it replicate across all the nodes. Simple, yet effective. No chewing up all your resources, waiting for hours or locking any databases! 

WIN!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_79732_1349851055</link>
            <description><![CDATA[Nikon HEX maker codes for EXIF data parsing:

'01 58 50 50 14 14 02 00' => 'AF Nikkor 50mm f/1.8',
    '01 00 00 00 00 00 02 00' => 'AF Teleconverter TC-16A 1.6x',
    '01 00 00 00 00 00 08 00' => 'AF Teleconverter TC-16A 1.6x',
    '02 42 44 5C 2A 34 02 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5',
    '02 42 44 5C 2A 34 08 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5',
    '03 48 5C 81 30 30 02 00' => 'AF Zoom-Nikkor 70-210mm f/4',
    '04 48 3C 3C 24 24 03 00' => 'AF Nikkor 28mm f/2.8',
    '05 54 50 50 0C 0C 04 00' => 'AF Nikkor 50mm f/1.4',
    '06 54 53 53 24 24 06 00' => 'AF Micro-Nikkor 55mm f/2.8',
    '07 40 3C 62 2C 34 03 00' => 'AF Zoom-Nikkor 28-85mm f/3.5-4.5',
    '08 40 44 6A 2C 34 04 00' => 'AF Zoom-Nikkor 35-105mm f/3.5-4.5',
    '09 48 37 37 24 24 04 00' => 'AF Nikkor 24mm f/2.8',
    '0A 48 8E 8E 24 24 03 00' => 'AF Nikkor 300mm f/2.8 IF-ED',
    '0B 48 7C 7C 24 24 05 00' => 'AF Nikkor 180mm f/2.8 IF-ED',
    '0D 40 44 72 2C 34 07 00' => 'AF Zoom-Nikkor 35-135mm f/3.5-4.5',
    '0E 48 5C 81 30 30 05 00' => 'AF Zoom-Nikkor 70-210mm f/4',
    '0F 58 50 50 14 14 05 00' => 'AF Nikkor 50mm f/1.8 N',
    '10 48 8E 8E 30 30 08 00' => 'AF Nikkor 300mm f/4 IF-ED',
    '11 48 44 5C 24 24 08 00' => 'AF Zoom-Nikkor 35-70mm f/2.8',
    '12 48 5C 81 30 3C 09 00' => 'AF Nikkor 70-210mm f/4-5.6',
    '13 42 37 50 2A 34 0B 00' => 'AF Zoom-Nikkor 24-50mm f/3.3-4.5',
    '14 48 60 80 24 24 0B 00' => 'AF Zoom-Nikkor 80-200mm f/2.8 ED',
    '15 4C 62 62 14 14 0C 00' => 'AF Nikkor 85mm f/1.8',
    '17 3C A0 A0 30 30 11 00' => 'Nikkor 500mm f/4 P',
    '18 40 44 72 2C 34 0E 00' => 'AF Zoom-Nikkor 35-135mm f/3.5-4.5 N',
    '1A 54 44 44 18 18 11 00' => 'AF Nikkor 35mm f/2',
    '1B 44 5E 8E 34 3C 10 00' => 'AF Zoom-Nikkor 75-300mm f/4.5-5.6',
    '1C 48 30 30 24 24 12 00' => 'AF Nikkor 20mm f/2.8',
    '1D 42 44 5C 2A 34 12 00' => 'AF Zoom-Nikkor 35-70mm f/3.3-4.5 N',
    '1E 54 56 56 24 24 13 00' => 'AF Micro-Nikkor 60mm f/2.8',
    '1F 54 6A 6A 24 24 14 00' => 'AF Micro-Nikkor 105mm f/2.8',
    '20 48 60 80 24 24 15 00' => 'AF Zoom-Nikkor ED 80-200mm f/2.8',
    '21 40 3C 5C 2C 34 16 00' => 'AF Zoom-Nikkor 28-70mm f/3.5-4.5',
    '22 48 72 72 18 18 16 00' => 'AF DC-Nikkor 135mm f/2',
    '24 48 60 80 24 24 1A 02' => 'AF Zoom-Nikkor ED 80-200mm f/2.8D',
    '25 48 44 5C 24 24 1B 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D',
    '25 48 44 5C 24 24 52 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D',
    '27 48 8E 8E 24 24 1D 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED',
    '27 48 8E 8E 24 24 F1 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED + TC-14E',
    '27 48 8E 8E 24 24 E1 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED + TC-17E',
    '27 48 8E 8E 24 24 F2 02' => 'AF-I Nikkor 300mm f/2.8D IF-ED + TC-20E',
    '28 3C A6 A6 30 30 1D 02' => 'AF-I Nikkor 600mm f/4D IF-ED',
    '2A 54 3C 3C 0C 0C 26 02' => 'AF Nikkor 28mm f/1.4D',
    '2C 48 6A 6A 18 18 27 02' => 'AF DC-Nikkor 105mm f/2D',
    '2D 48 80 80 30 30 21 02' => 'AF Micro-Nikkor 200mm f/4D IF-ED',
    '2E 48 5C 82 30 3C 28 02' => 'AF Nikkor 70-210mm f/4-5.6D',
    '2F 48 30 44 24 24 29 02' => 'AF Zoom-Nikkor 20-35mm f/2.8D IF',
    '31 54 56 56 24 24 25 02' => 'AF Micro-Nikkor 60mm f/2.8D',
    '32 54 6A 6A 24 24 35 02' => 'AF Micro-Nikkor 105mm f/2.8D' .
                                 ' or Sigma 105mm f/2.8 EX DG MACRO', #JD
    '33 48 2D 2D 24 24 31 02' => 'AF Nikkor 18mm f/2.8D',
    '34 48 29 29 24 24 32 02' => 'AF Fisheye Nikkor 16mm f/2.8D',
    '35 3C A0 A0 30 30 33 02' => 'AF-I Nikkor 500mm f/4D IF-ED',
    '36 48 37 37 24 24 34 02' => 'AF Nikkor 24mm f/2.8D',
    '37 48 30 30 24 24 36 02' => 'AF Nikkor 20mm f/2.8D',
    '38 4C 62 62 14 14 37 02' => 'AF Nikkor 85mm f/1.8D',
    '3A 40 3C 5C 2C 34 39 02' => 'AF Zoom-Nikkor 28-70mm f/3.5-4.5D',
    '3B 48 44 5C 24 24 3A 02' => 'AF Zoom-Nikkor 35-70mm f/2.8D N',
    '3C 48 60 80 24 24 3B 02' => 'AF Nikkor 80-200mm f/2.8 ED', #25
    '3D 3C 44 60 30 3C 3E 02' => 'AF Zoom-Nikkor 35-80mm f/4-5.6D',
    '3E 48 3C 3C 24 24 3D 02' => 'AF Nikkor 28mm f/2.8D',
    '3F 40 44 6A 2C 34 45 02' => 'AF Zoom-Nikkor 35-105mm f/3.5-4.5D',
    '41 48 7C 7C 24 24 43 02' => 'AF Nikkor 180mm f/2.8D IF-ED',
    '42 54 44 44 18 18 44 02' => 'AF Nikkor 35mm f/2D',
    '43 54 50 50 0C 0C 46 02' => 'AF Nikkor 50mm f/1.4D',
    '44 44 60 80 34 3C 47 02' => 'AF Nikkor 80-200mm f/4.5-5.6D',
    '45 40 3C 60 2C 3C 48 02' => 'AF Zoom-Nikkor 28-80mm F/3.5-5.6D',
    '46 3C 44 60 30 3C 49 02' => 'AF Zoom-Nikkor 35-80mm f/4-5.6D N',
    '47 42 37 50 2A 34 4A 02' => 'AF Zoom-Nikkor 24-50mm f/3.3-4.5D',
    '48 48 8E 8E 24 24 4B 02' => 'AF-S Nikkor 300mm f/2.8D IF-ED',
    '49 3C A6 A6 30 30 4C 02' => 'AF-S Nikkor 600mm f/4D IF-ED',
    '49 3C A6 A6 30 30 F1 02' => 'AF-S Nikkor 600mm f/4D IF-ED + TC-14E',
    '49 3C A6 A6 30 30 F2 02' => 'AF-S Nikkor 600mm f/4D IF-ED + TC-20E',
    '4A 54 62 62 0C 0C 4D 02' => 'AF Nikkor 85mm f/1.4D IF',
    '4B 3C A0 A0 30 30 4E 02' => 'AF-S Nikkor 500mm f/4D IF-ED',
    '4B 3C A0 A0 30 30 F1 02' => 'AF-S Nikkor 500mm f/4D IF-ED + TC-14E',
    '4B 3C A0 A0 30 30 F2 02' => 'AF-S Nikkor 500mm f/4D IF-ED + TC-20E',
    '4C 40 37 6E 2C 3C 4F 02' => 'AF Zoom-Nikkor 24-120mm f/3.5-5.6D IF',
    '4D 40 3C 80 2C 3C 62 02' => 'AF Zoom-Nikkor 28-200mm f/3.5-5.6D IF',
    '4E 48 72 72 18 18 51 02' => 'AF DC-Nikkor 135mm f/2D',
    '4F 40 37 5C 2C 3C 53 06' => 'IX-Nikkor 24-70mm f/3.5-5.6',
    '53 48 60 80 24 24 60 02' => 'AF Zoom-Nikkor 80-200mm f/2.8D ED',
    '54 44 5C 7C 34 3C 58 02' => 'AF Zoom-Micro Nikkor 70-180mm f/4.5-5.6D ED',
    '56 48 5C 8E 30 3C 5A 02' => 'AF Zoom-Nikkor 70-300mm f/4-5.6D ED',
    '59 48 98 98 24 24 5D 02' => 'AF-S Nikkor 400mm f/2.8D IF-ED',
    '5A 3C 3E 56 30 3C 5E 06' => 'IX-Nikkor 30-60mm f/4-5.6',
    '5D 48 3C 5C 24 24 63 02' => 'AF-S Zoom-Nikkor 28-70mm f/2.8D IF-ED',
    '5E 48 60 80 24 24 64 02' => 'AF-S Zoom-Nikkor 80-200mm f/2.8D IF-ED',
    '5F 40 3C 6A 2C 34 65 02' => 'AF Zoom-Nikkor 28-105mm f/3.5-4.5D IF',
    '60 40 3C 60 2C 3C 66 02' => 'AF Zoom-Nikkor 28-80mm f/3.5-5.6D', #(http://www.exif.org/forum/topic.asp?TOPIC_ID=16)
    '61 44 5E 86 34 3C 67 02' => 'AF Zoom-Nikkor 75-240mm f/4.5-5.6D',
    '63 48 2B 44 24 24 68 02' => 'AF-S Nikkor 17-35mm f/2.8D IF-ED',
    '64 00 62 62 24 24 6A 02' => 'PC Micro-Nikkor 85mm f/2.8D',
    '65 44 60 98 34 3C 6B 0A' => 'AF VR Zoom-Nikkor 80-400mm f/4.5-5.6D ED',
    '66 40 2D 44 2C 34 6C 02' => 'AF Zoom-Nikkor 18-35mm f/3.5-4.5D IF-ED',
    '67 48 37 62 24 30 6D 02' => 'AF Zoom-Nikkor 24-85mm f/2.8-4D IF',
    '68 42 3C 60 2A 3C 6E 06' => 'AF Zoom-Nikkor 28-80mm f/3.3-5.6G',
    '69 48 5C 8E 30 3C 6F 06' => 'AF Zoom-Nikkor 70-300mm f/4-5.6G',
    '6A 48 8E 8E 30 30 70 02' => 'AF-S Nikkor 300mm f/4D IF-ED',
    '6B 48 24 24 24 24 71 02' => 'AF Nikkor ED 14mm f/2.8D',
    '6D 48 8E 8E 24 24 73 02' => 'AF-S Nikkor 300mm f/2.8D IF-ED II',
    '6E 48 98 98 24 24 74 02' => 'AF-S Nikkor 400mm f/2.8D IF-ED II',
    '6F 3C A0 A0 30 30 75 02' => 'AF-S Nikkor 500mm f/4D IF-ED II',
    '70 3C A6 A6 30 30 76 02' => 'AF-S Nikkor 600mm f/4D IF-ED II',
    '72 48 4C 4C 24 24 77 00' => 'Nikkor 45mm f/2.8 P',
    '74 40 37 62 2C 34 78 06' => 'AF-S Zoom-Nikkor 24-85mm f/3.5-4.5G IF-ED',
    '75 40 3C 68 2C 3C 79 06' => 'AF Zoom-Nikkor 28-100mm f/3.5-5.6G',
    '76 58 50 50 14 14 7A 02' => 'AF Nikkor 50mm f/1.8D',
    '77 48 5C 80 24 24 7B 0E' => 'AF-S VR Zoom-Nikkor 70-200mm f/2.8G IF-ED',
    '78 40 37 6E 2C 3C 7C 0E' => 'AF-S VR Zoom-Nikkor 24-120mm f/3.5-5.6G IF-ED',
    '79 40 3C 80 2C 3C 7F 06' => 'AF Zoom-Nikkor 28-200mm f/3.5-5.6G IF-ED',
    '7A 3C 1F 37 30 30 7E 06' => 'AF-S DX Zoom-Nikkor 12-24mm f/4G IF-ED',
    '7B 48 80 98 30 30 80 0E' => 'AF-S VR Zoom-Nikkor 200-400mm f/4G IF-ED',
    '7D 48 2B 53 24 24 82 06' => 'AF-S DX Zoom-Nikkor 17-55mm f/2.8G IF-ED',
    '7F 40 2D 5C 2C 34 84 06' => 'AF-S DX Zoom-Nikkor 18-70mm f/3.5-4.5G IF-ED',
    '80 48 1A 1A 24 24 85 06' => 'AF DX Fisheye-Nikkor 10.5mm f/2.8G ED',
    '81 54 80 80 18 18 86 0E' => 'AF-S VR Nikkor 200mm f/2G IF-ED',
    '82 48 8E 8E 24 24 87 0E' => 'AF-S VR Nikkor 300mm f/2.8G IF-ED',
    '89 3C 53 80 30 3C 8B 06' => 'AF-S DX Zoom-Nikkor 55-200mm f/4-5.6G ED',
    '8A 54 6A 6A 24 24 8C 0E' => 'AF-S VR Micro-Nikkor 105mm f/2.8G IF-ED', #10
    '8B 40 2D 80 2C 3C FD 0E' => 'AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED', #20
    '8B 40 2D 80 2C 3C 8D 0E' => 'AF-S DX VR Zoom-Nikkor 18-200mm f/3.5-5.6G IF-ED',
    '8C 40 2D 53 2C 3C 8E 06' => 'AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED',
    '8D 44 5C 8E 34 3C 8F 0E' => 'AF-S VR Zoom-Nikkor 70-300mm f/4.5-5.6G IF-ED', #10
    '8F 40 2D 72 2C 3C 91 06' => 'AF-S DX Zoom-Nikkor 18-135mm f/3.5-5.6G IF-ED',
    '90 3B 53 80 30 3C 92 0E' => 'AF-S DX VR Zoom-Nikkor 55-200mm f/4-5.6G IF-ED',
    '92 48 24 37 24 24 94 06' => 'AF-S Zoom-Nikkor 14-24mm f/2.8G ED',
    '93 48 37 5C 24 24 95 06' => 'AF-S Zoom-Nikkor 24-70mm f/2.8G ED',
    '94 40 2D 53 2C 3C 96 06' => 'AF-S DX Zoom-Nikkor 18-55mm f/3.5-5.6G ED II', #10 (D40)
    '95 4C 37 37 2C 2C 97 02' => 'PC-E Nikkor 24mm f/3.5D ED',
    '95 00 37 37 2C 2C 97 06' => 'PC-E Nikkor 24mm f/3.5D ED', #JD
    '96 48 98 98 24 24 98 0E' => 'AF-S VR Nikkor 400mm f/2.8G ED',
    '97 3C A0 A0 30 30 99 0E' => 'AF-S VR Nikkor 500mm f/4G ED',
    '98 3C A6 A6 30 30 9A 0E' => 'AF-S VR Nikkor 600mm f/4G ED',
    '99 40 29 62 2C 3C 9B 0E' => 'AF-S DX VR Zoom-Nikkor 16-85mm f/3.5-5.6G ED',
    '9A 40 2D 53 2C 3C 9C 0E' => 'AF-S DX VR Zoom-Nikkor 18-55mm f/3.5-5.6G',
    '9C 54 56 56 24 24 9E 06' => 'AF-S Micro Nikkor 60mm f/2.8G ED',
    '9E 40 2D 6A 2C 3C A0 0E' => 'AF-S DX VR Zoom-Nikkor 18-105mm f/3.5-5.6G ED', #PH/10
#
    'FE 47 00 00 24 24 4B 06' => 'Sigma 4.5mm F2.8 EX DC Circular Fisheye HSM', #JD
    '26 48 11 11 30 30 1C 02' => 'Sigma 8mm F4 EX Circular Fisheye',
    '79 40 11 11 2C 2C 1C 06' => 'Sigma 8mm F3.5 EX', #JD
    '02 3F 24 24 2C 2C 02 00' => 'Sigma 14mm F3.5',
    '48 48 24 24 24 24 4B 02' => 'Sigma 14mm F2.8 EX Aspherical HSM',
    '26 48 27 27 24 24 1C 02' => 'Sigma 15mm F2.8 EX Diagonal Fish-Eye',
    '7F 48 2D 50 24 24 1C 06' => 'Sigma 18-50mm F2.8 EX DC MACRO', #25
    '26 58 31 31 14 14 1C 02' => 'Sigma 20mm F1.8 EX Aspherical DG DF RF',
    '26 58 37 37 14 14 1C 02' => 'Sigma 24mm F1.8 EX Aspherical DG DF MACRO',
    '02 46 37 37 25 25 02 00' => 'Sigma 24mm F2.8 Macro',
    '26 58 3C 3C 14 14 1C 02' => 'Sigma 28mm F1.8 EX DG DF',
    '48 54 3E 3E 0C 0C 4B 06' => 'Sigma 30mm F1.4 EX DC HSM',
    'F8 54 3E 3E 0C 0C 4B 06' => 'Sigma 30mm F1.4 EX DC HSM', #JD
    '32 54 50 50 24 24 35 02' => 'Sigma 50mm F2.8 EX DG Macro',
    '79 48 5C 5C 24 24 1C 06' => 'Sigma 70mm F2.8 EX DG Macro', #JD
    '02 48 65 65 24 24 02 00' => 'Sigma 90mm F2.8 Macro',
    '48 48 76 76 24 24 4B 06' => 'Sigma 150mm F2.8 EX DG APO Macro HSM',
    'F5 48 76 76 24 24 4B 06' => 'Sigma 150mm F2.8 EX DG APO Macro HSM', #24
    '48 4C 7C 7C 2C 2C 4B 02' => 'Sigma 180mm F3.5 EX DG Macro',
    '48 4C 7D 7D 2C 2C 4B 02' => 'Sigma APO MACRO 180mm F3.5 EX DG HSM',
    '48 54 8E 8E 24 24 4B 02' => 'Sigma APO 300mm F2.8 EX DG HSM',
    '26 48 8E 8E 30 30 1C 02' => 'Sigma APO TELE MACRO 300mm F4',
    '02 2F 98 98 3D 3D 02 00' => 'Sigma 400mm F5.6 APO',
    '02 37 A0 A0 34 34 02 00' => 'Sigma APO 500mm F4.5', #19
    '48 44 A0 A0 34 34 4B 02' => 'Sigma APO 500mm F4.5 EX HSM',
    '48 3C 19 31 30 3C 4B 06' => 'Sigma 10-20mm F4-5.6 EX DC HSM',
    'F9 3C 19 31 30 3C 4B 06' => 'Sigma 10-20mm F4-5.6 EX DC HSM', #JD
    '48 38 1F 37 34 3C 4B 06' => 'Sigma 12-24mm F4.5-5.6 EX Aspherical DG HSM',
    '26 40 27 3F 2C 34 1C 02' => 'Sigma 15-30mm F3.5-4.5 EX Aspherical DG DF',
    '48 48 2B 44 24 30 4B 06' => 'Sigma 17-35mm F2.8-4 EX DG  Aspherical HSM',
    '26 54 2B 44 24 30 1C 02' => 'Sigma 17-35mm F2.8-4 EX Aspherical',
    '7A 47 2B 5C 24 34 4B 06' => 'Sigma 17-70mm F2.8-4.5 DC Macro Asp. IF HSM',
    '7F 48 2B 5C 24 34 1C 06' => 'Sigma 17-70mm F2.8-4.5 DC Macro Asp. IF',
    '26 40 2D 44 2B 34 1C 02' => 'Sigma 18-35 F3.5-4.5 Aspherical',
    '26 48 2D 50 24 24 1C 06' => 'Sigma 18-50mm F2.8 EX DC',
    '7A 48 2D 50 24 24 4B 06' => 'Sigma 18-50mm F2.8 EX DC HSM',
    '26 40 2D 50 2C 3C 1C 06' => 'Sigma 18-50mm F3.5-5.6 DC',
    '7A 40 2D 50 2C 3C 4B 06' => 'Sigma 18-50mm F3.5-5.6 DC HSM',
    '26 40 2D 70 2B 3C 1C 06' => 'Sigma 18-125mm F3.5-5.6 DC',
    '26 40 2D 80 2C 40 1C 06' => 'Sigma 18-200mm F3.5-6.3 DC',
    '26 48 31 49 24 24 1C 02' => 'Sigma 20-40mm F2.8',
    '26 48 37 56 24 24 1C 02' => 'Sigma 24-60mm F2.8 EX DG',
    'B6 48 37 56 24 24 1C 02' => 'Sigma 24-60mm F2.8 EX DG',
    '26 54 37 5C 24 24 1C 02' => 'Sigma 24-70mm F2.8 EX DG Macro',
    '67 54 37 5C 24 24 1C 02' => 'Sigma 24-70mm F2.8 EX DG Macro',
    '26 40 37 5C 2C 3C 1C 02' => 'Sigma 24-70mm F3.5-5.6 Aspherical HF',
    '26 54 37 73 24 34 1C 02' => 'Sigma 24-135mm F2.8-4.5',
    '02 46 3C 5C 25 25 02 00' => 'Sigma 28-70mm F2.8',
    '26 54 3C 5C 24 24 1C 02' => 'Sigma 28-70mm F2.8 EX',
    '26 48 3C 5C 24 24 1C 06' => 'Sigma 28-70mm F2.8 EX DG',
    '26 48 3C 5C 24 30 1C 02' => 'Sigma 28-70mm F2.8-4 High Speed Zoom',
    '02 3F 3C 5C 2D 35 02 00' => 'Sigma 28-70mm F3.5-4.5 UC',
    '26 40 3C 60 2C 3C 1C 02' => 'Sigma 28-80mm F3.5-5.6 Mini Zoom Macro II Aspherical',
    '26 40 3C 65 2C 3C 1C 02' => 'Sigma 28-90mm F3.5-5.6 Macro',
    '26 48 3C 6A 24 30 1C 02' => 'Sigma 28-105mm F2.8-4 Aspherical',
    '26 3E 3C 6A 2E 3C 1C 02' => 'Sigma 28-105mm F3.8-5.6 UC-III Aspherical IF',
    '26 40 3C 80 2C 3C 1C 02' => 'Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro',
    '26 40 3C 80 2B 3C 1C 02' => 'Sigma 28-200mm F3.5-5.6 Compact Aspherical Hyperzoom Macro',
    '26 41 3C 8E 2C 40 1C 02' => 'Sigma 28-300mm F3.5-6.3 DG Macro',
    '26 40 3C 8E 2C 40 1C 02' => 'Sigma 28-300mm F3.5-6.3 Macro',
    '02 40 44 73 2B 36 02 00' => 'Sigma 35-135mm F3.5-4.5 a',
    '7A 47 50 76 24 24 4B 06' => 'Sigma 50-150mm F2.8 EX APO DC HSM',
    '48 3C 50 A0 30 40 4B 02' => 'Sigma 50-500mm F4-6.3 EX APO RF HSM',
    '26 3C 54 80 30 3C 1C 06' => 'Sigma 55-200mm F4-5.6 DC',
    '7A 3B 53 80 30 3C 4B 06' => 'Sigma 55-200mm F4-5.6 DC HSM',
    '48 54 5C 80 24 24 4B 02' => 'Sigma 70-200mm F2.8 EX APO IF HSM',
    'EE 48 5C 80 24 24 4B 06' => 'Sigma 70-200mm F2.8 EX APO DG Macro HSM II', #JD
    '02 46 5C 82 25 25 02 00' => 'Sigma 70-210mm F2.8 APO', #JD
    '26 3C 5C 82 30 3C 1C 02' => 'Sigma 70-210mm F4-5.6 UC-II',
    '26 3C 5C 8E 30 3C 1C 02' => 'Sigma 70-300mm F4-5.6 DG Macro',
    '56 3C 5C 8E 30 3C 1C 02' => 'Sigma 70-300mm F4-5.6 APO Macro Super II',
    'E0 3C 5C 8E 30 3C 4B 06' => 'Sigma 70-300mm F4-5.6 APO DG Macro HSM', #22
    '02 37 5E 8E 35 3D 02 00' => 'Sigma 75-300mm F4.5-5.6 APO',
    '02 3A 5E 8E 32 3D 02 00' => 'Sigma 75-300mm F4.0-5.6',
    '77 44 61 98 34 3C 7B 0E' => 'Sigma 80-400mm F4.5-5.6 EX OS',
    '48 48 68 8E 30 30 4B 02' => 'Sigma 100-300mm F4 EX IF HSM',
    'CE 34 76 A0 38 40 4B 0E' => 'Sigma 150-500mm F5-6.3 DG OS APO HSM', #JD
    '48 54 6F 8E 24 24 4B 02' => 'Sigma APO 120-300mm F2.8 EX DG HSM',
    '26 44 73 98 34 3C 1C 02' => 'Sigma 135-400mm F4.5-5.6 APO Aspherical',
    '26 40 7B A0 34 40 1C 02' => 'Sigma APO 170-500mm F5-6.3 Aspherical RF',
    '48 3C 8E B0 3C 3C 4B 02' => 'Sigma APO 300-800 F5.6 EX DG HSM',
#
    '32 53 64 64 24 24 35 02' => 'Tamron SP AF90mm f/2.8 Di Macro 1:1 (272E)',
    '00 4C 7C 7C 2C 2C 00 02' => 'Tamron SP AF180mm f/3.5 Di Model B01',
    '00 36 1C 2D 34 3C 00 06' => 'Tamron SP AF11-18mm f/4.5-5.6 Di II LD Aspherical (IF)',
    '07 46 2B 44 24 30 03 02' => 'Tamron SP AF17-35mm f/2.8-4 Di LD Aspherical (IF)',
    '00 53 2B 50 24 24 00 06' => 'Tamron SP AF17-50mm f/2.8 (A16)', #PH
    '00 3F 2D 80 2B 40 00 06' => 'Tamron AF18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF)',
    '00 3F 2D 80 2C 40 00 06' => 'Tamron AF18-200mm f/3.5-6.3 XR Di II LD Aspherical (IF) Macro',
    '00 40 2D 88 2C 40 62 06' => 'Tamron AF18-250mm F/3.5-6.3 Di II LD Aspherical (IF) Macro', # A18N?
    '00 40 2D 88 2C 40 00 06' => 'Tamron AF18-250mm f/3.5-6.3 Di II LD Aspherical (IF) Macro (A18NII)', #JD
    '07 40 2F 44 2C 34 03 02' => 'Tamron AF19-35mm f/3.5-4.5 N',
    '07 40 30 45 2D 35 03 02' => 'Tamron AF19-35mm f/3.5-4.5',
    '00 49 30 48 22 2B 00 02' => 'Tamron SP AF20-40mm f/2.7-3.5',
    '0E 4A 31 48 23 2D 0E 02' => 'Tamron SP AF20-40mm f/2.7-3.5',
    '45 41 37 72 2C 3C 48 02' => 'Tamron SP AF24-135mm f/3.5-5.6 AD Aspherical (IF) Macro',
    '33 54 3C 5E 24 24 62 02' => 'Tamron SP AF28-75mm f/2.8 XR Di LD Aspherical (IF) Macro',
    '10 3D 3C 60 2C 3C D2 02' => 'Tamron AF28-80mm f/3.5-5.6 Aspherical',
    '45 3D 3C 60 2C 3C 48 02' => 'Tamron AF28-80mm f/3.5-5.6 Aspherical',
    '00 48 3C 6A 24 24 00 02' => 'Tamron SP AF28-105mm f/2.8',
    '0B 3E 3D 7F 2F 3D 0E 02' => 'Tamron AF28-200mm f/3.8-5.6D',
    '0B 3E 3D 7F 2F 3D 0E 00' => 'Tamron AF28-200mm f/3.8-5.6',
    '4D 41 3C 8E 2B 40 62 02' => 'Tamron AF28-300mm f/3.5-6.3 XR Di LD Aspherical (IF)',
    '4D 41 3C 8E 2C 40 62 02' => 'Tamron AF28-300mm f/3.5-6.3 XR LD Aspherical (IF)',
    '00 47 53 80 30 3C 00 06' => 'Tamron AF55-200mm f/4-5.6 Di II LD',
    '69 48 5C 8E 30 3C 6F 02' => 'Tamron AF70-300mm f/4-5.6 LD Macro 1:2',
    '20 3C 80 98 3D 3D 1E 02' => 'Tamron AF200-400mm f/5.6 LD IF',
    '00 3E 80 A0 38 3F 00 02' => 'Tamron SP AF200-500mm f/5-6.3 Di LD (IF)',
    '00 3F 80 A0 38 3F 00 02' => 'Tamron SP AF200-500mm f/5-6.3 Di',
#
    '00 40 2B 2B 2C 2C 00 02' => 'Tokina AT-X 17 AF PRO - AF 17mm f/3.5',
    '00 47 44 44 24 24 00 06' => 'Tokina AT-X M35 Pro DX - 35mm f/2.8',
    '00 54 68 68 24 24 00 02' => 'Tokina AT-X M100 PRO D - 100mm f/2.8',
    '00 54 8E 8E 24 24 00 02' => 'Tokina AT-X 300 AF PRO 300mm f/2.8',
    '00 40 18 2B 2C 34 00 06' => 'Tokina AT-X 107 DX Fish-Eye -AF 10-17mm f/3.5-4.5',
    '00 48 1C 29 24 24 00 06' => 'Tokina AT-X 116 PRO DX AF 11-16mm f/2.8',
    '00 3C 1F 37 30 30 00 06' => 'Tokina AT-X 124 AF PRO DX - AF 12-24mm f/4',
    '00 48 29 50 24 24 00 06' => 'Tokina AT-X 165 PRO DX - AF 16-50mm f/2.8',
    '25 48 3C 5C 24 24 1B 02' => 'Tokina AT-X 287 AF PRO SV 28-70mm f/2.8',
    '07 48 3C 5C 24 24 03 00' => 'Tokina AT-X AF 28-70mm f/2.8', #JD
    '00 48 3C 60 24 24 00 02' => 'Tokina AT-X 280 AF PRO 28-80mm f/2.8 Aspherical',
    '00 48 50 72 24 24 00 06' => 'Tokina AT-X 535 PRO DX - AF 50-135mm f/2.8',
    '14 54 60 80 24 24 0B 00' => 'Tokina AT-X 828 AF 80-200mm f/2.8',
    '24 44 60 98 34 3C 1A 02' => 'Tokina AT-X 840 AF II 80-400mm f/4.5-5.6',
    '00 44 60 98 34 3C 00 02' => 'Tokina AT-X 840D 80-400mm f/4.5-5.6', #PH
    '14 48 68 8E 30 30 0B 00' => 'Tokina AT-X 340 AF II 100-300mm f/4',
#
    '00 54 56 56 30 30 00 00' => 'Coastal Optical Systems 60mm 1:4 UV-VIS-IR Macro Apo',
#
    '00 54 48 48 18 18 00 00' => 'Voigtlander Ultron SL2 40mm f/2 SL II Aspherical',
    '00 54 55 55 0C 0C 00 00' => 'Voigtlander Nokton SL2 58mm f/1.4 SL II',
#
    '07 3E 30 43 2D 35 03 00' => 'Soligor AF Zoom 19-35mm 1:3.5-4.5',
    '03 43 5C 81 35 35 02 00' => 'Soligor AF C/D Zoom UMCS 70-210mm 1:4.5',
#
    '12 36 5C 81 35 3D 09 00' => 'Cosina AF Zoom 70-210mm f/4.5-5.6 MC Macro',
    '06 3F 68 68 2C 2C 06 00' => 'Cosina 100mm f/3.5 Macro',
#
    '2F 40 30 44 2C 34 29 02' => 'Unknown 20-35mm f/3.5-4.5D',
    '1E 5D 64 64 20 20 13 00' => 'Unknown 90mm f/2.5',
    '12 3B 68 8D 3D 43 09 02' => 'Unknown 100-290mm f/5.6-6.7',
    '00 00 00 00 00 00 00 01' => 'Manual Lens No CPU',]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_21261_1346397827</link>
            <description><![CDATA[Low level MongoDB in Java Spring Framework

I recently discovered that doing lower level functions like distinct() and some of the $near (i.e. spatial) functions in MongoDB using the Spring Framework is not all that easy.

After a little poking around, I have come up with the following solution, which is OK and works, but can probably be improved upon. I am still pretty new to Spring, so if you have a better idea, then please do let me know in comments.

Anyway, here it is:

First off, we use the @Autowired annotation to bring in the base MongoTemplate from spring-data-mongodb

[code]
@Autowired
MongoTemplate mongoTemplate;
[/code]

Once we have that, we can use it to make some queries. Note that this is the slightly smelly part because you have to tell Spring what the return type is and it doesn't really like that...
[code]
// Get the distinct stuff from MongoDB
List<String> coll = mongoTemplate.getCollection("mycollection").distinct("myfield");
[/code]

In the above code you will notice that I have defined a List type variable called coll that uses the @Autowired MongoTemplate variable to get a collection and then a field using distinct. This is analogous to db.whatever.distinct("term) on the Mongo shell.

Hope this saves someone else a few minutes at least, and as I said, if you have a better idea, please do share!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_27215_1342836600</link>
            <description><![CDATA[I wanted to encode some footage that I had shot from .avi to h.264 to play on an Android tablet device, but it seems that Android cannot play avi natively without additional app installations.
After some research, I found out that avconv, which is the replacement for good old ffmpeg in Ubuntu these days, does not work with x.264 either. The solution was to download and compile in x.264 support from source. Steps taken below.

In retrospect, I am not sure this is a great way to approach this, and probably grabbing an app is way better. That being said, this may be useful elsewhere too though.

mkdir avconv-source

2.) Download and install the x264 library

Grab yasm from Ubuntu
[code]
apt-get install yasm
cd ~/avconv-source
sudo git clone git://git.videolan.org/x264.git x264/
sudo ./configure --enable-static
sudo make
sudo make install
[/code]

3.) Download the avconv source
[code]
cd ~/avconv-source
sudo git clone git://git.libav.org/libav.git avconv/
sudo ./configure --enable-gpl --enable-libx264
sudo make
sudo make install
[/code]

and you can now execute
[code]
avconv -i test.mov -c:v libx264 -c:a copy test.mp4
[/code]]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_45051_1334831888</link>
            <description><![CDATA[How to set up a MongoDB replicaset

This tutorial will go through setting up a 3 node MongoDB replicaset with an Arbiter

On all nodes:
[code]
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10
add deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen to /etc/apt/sources.list
apt-get update
apt-get install mongodb-10gen
[/code]

Start mongodb with upstart [code]start mongodb[/code]

[code]vi /etc/mongodb.conf[/code]

add the following parameters to the configuration file
[code]
# Enable REST interface
rest=true

# in replica set configuration, specify the name of the replica set
replSet = setname
[/code]

where setname is the overall name of your replicaset.

Restart mongodb. It will give you errors as it cannot find any other replicaset members

On the "primary" node:

1. Initiate the replicaSet with rs.initiate() in the mongo shell. This will create and empty config.

2. Check the replicaset status with rs.status() after each node addition. Status at this point will simply reflect the primary node.

3. Check that this node is master with rs.isMaster(). You should get some information back about which node is currently master and which slaves are up and running.

4. Next add your second node. rs.add("hostname or ip")

5. rs.add() all of the nodes that you like, but leave one node as the arbiter

6. Add the arbiter node: rs.add( { _id:2, host:"ip or hostname", arbiterOnly:true } )

7. Check with rs.status() what is happening. You should now have a couple of nodes as well as an arbiter running and healthy

Notes:

Additional information about the replicaset can ge gained with

rs.help()	  
rs.status()	
rs.conf()	
db.isMaster()	
You have enabled the REST interface, which means that you can browse to http://yourhostname:28017 for more information. Don't miss the ReplSet link!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_18321_1334828109</link>
            <description><![CDATA[How to set up and deploy a GlusterFS distributed, replicating file system.

OK so you want a replicated, distributed file system. Sure thing. Enter Gluster. Take a look at http://www.gluster.org/ for more information.

For the purposes of this article, we will look at a 2 node replicated store and then mount the filesystem on a third node for use as a regular old filesystem. 

1. Make sure that your 2 servers can talk to each other. If needs be, modify your hosts file so that they do. I personally do not like using FQDN for this as it slows things down, so normally would use an IP address.

2. On each of your Ubuntu based nodes, do an apt-get update && apt-get install glusterfs-server

3. On server 1, do a gluster peer probe server2 where server2 is either a FQDN or an ip address, depending on how you roll.

4. Check everything is cool with a gluster peer status. You should now be seeing some information on the peer(s) like hostnames and UUID's, states and stuff like that.

5. Now we need to create a gluster volume, which is essentially a big disc that you are going to store your junk on. This is also pretty simple. Do a gluster volume create MyVolume replica 2 transport tcp server1:/data server2:/data

Some notes:

replica 2 means replicate across 2 machines. This is for your failover integrity. It also needs to be at least 2.
MyVolume is the name of your virtual volume.
transport tcp means use tcp to talk to the servers in the cluster
serverx:/data is the part on the physical disc that the data will be stored to.

6. Great! Ready for bigger and better things! Let's start the volume: gluster volume start MyVolume

7. Check the status of the volume with gluster volume info. You should get some output telling you about your nodes as "Bricks"

8. Now is a good time to think about security and authentication. If you want to lock it down to a certain IP or host, you need to do a gluster volume set MyVolume auth.allow 192.168.1.5 or whatever your IP address is. Remember the thing is open until you set the auth.allow so if you need to allow a bunch of clients, then send it a comma separated list of IP's

9. About now, we have a working server cluster. Now to configure the client. On your client machine, apt-get install glusterfs-client

10. mkdir /mnt/myshared

11. mount -t glusterfs server1:/MyVolume /mnt/myshared

12. You are done. Seriously, that is it. Add your new mount to fstab if you like, copy some stuff to it, watch it replicate etc.

Notes:

1. You can install the client on the server(s) too. If you want to mount the glusterfs share locally (i.e. on the server) use localhost

2. Replicas should be considered when creating larger clusters. Remember when you have more than one node failure, you want enough replicas to keep all the data integrity.

3. Leave other tips in the comments if you think of anything.


]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_45959_1334218871</link>
            <description><![CDATA[How to monitor your MongoDB server(s) and ReplicaSets with Nagios on Ubuntu

I needed to use Nagios to monitor a mongodb server (OK, my localhost as an experiment) and after a little poking around, managed this with relative ease.

These are the steps (I used nagios3 out of the Ubuntu repository)

1. Install Nagios3

2. Fix the SSH and disk error issues according to http://ubuntuforums.org/showthread.php?t=1749248

3. git clone https://github.com/mzupan/nagios-plugin-mongodb to /usr/lib/nagios/plugins/nagios-plugin-mongodb

4. Add the following commands to /etc/nagios3/commands.cfg

[code]
define command {
    command_name    check_mongodb
    command_line    /usr/lib/nagios/plugins/nagios-plugin-mongodb/check_mongodb.py -H $HOSTADDRESS$ -A $ARG1$ -P $ARG2$ -W $ARG3$ -C $ARG4$
}

define command {
    command_name    check_mongodb_database
    command_line    /usr/lib/nagios/plugins/nagios-plugin-mongodb/check_mongodb.py -H $HOSTADDRESS$ -A $ARG1$ -P $ARG2$ -W $ARG3$ -C $ARG4$ -d $ARG5$
}
[/code]

5. touch /etc/nagios3/conf.d/mongodb_nagios2.cfg

6. add the following service definitions to the above file. Use your brain here a bit, so for example, if you do not run a replicaSet configuration on MongoDB, leave that service definition out, else it will produce CRITICAL warns the whole time and annoy you.

[code]
define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Connect Check
    check_command           check_mongodb!connect!27017!2!4
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Free Connections
    check_command           check_mongodb!connections!27017!70!80
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Replication Lag
    check_command           check_mongodb!replication_lag!27017!15!30
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Memory Usage
    check_command           check_mongodb!memory!27017!20!28
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Lock Percentage
    check_command           check_mongodb!lock!27017!5!10
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Flush Average
    check_command           check_mongodb!flushing!27017!100!200
}

define service {
    use                 generic-service
    hostgroup_name          mongodb-servers
    service_description     Mongo Last Flush Time
    check_command           check_mongodb!last_flush_time!27017!200!400
}

define service {
      use                     generic-service
      hostgroup_name          mongodb-servers
      service_description     MongoDB state
      check_command           check_mongodb!replset_state!27017!0!0
}

define service {
      use                     generic-service
      hostgroup_name          mongodb-servers
      service_description     MongoDB state
      check_command           check_mongodb!index_miss_ratio!27017!.005!.01
}

define service {
      use                     generic-service
      hostgroup_name          mongodb-servers
      service_description     MongoDB Number of databases
      check_command           check_mongodb!databases!27017!300!500
}

define service {
      use                     generic-service
      hostgroup_name          mongodb-servers
      service_description     MongoDB Number of collections
      check_command           check_mongodb!collections!27017!300!500
}

define service {
      use                     generic-service
      hostgroup_name          mongodb-servers
      service_description     MongoDB Database size YOUR-dbname
      check_command           check_mongodb_database!database_size!27017!300!500!YOUR-dbname
}
[/code]

see the bit that says YOUR-dbname in the final service? Yeah, you need to change that to an actual db name...

7. Do a quick sanity check on your configs
[code]
nagios3 -v /etc/nagios3/nagios.cfg
[/code]

if you get something like: 
[code]
Total Warnings: 0
Total Errors:   0

Things look okay - No serious problems were detected during the pre-flight check
[/code]
you should be good to go.

8. Restart nagios3 with /etc/init.d/nagios3 restart

9. Check the web interface for nagios3 to see information about your MongoDB services!

10. Have some milk and a cookie, you deserve it!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_22347_1330693632</link>
            <description><![CDATA[How to install Puppet (puppetmaster) server on Debian Squeeze in 2 minutes or less

echo "deb http://debian.mirror.iweb.ca/debian-backports squeeze-backports main" >> /etc/apt/sources.list.d/backports.list
echo "deb-src http://debian.mirror.iweb.ca/debian-backports squeeze-backports main" >> /etc/apt/sources.list.d/backports.list
apt-get update
apt-get update
apt-get install -y -t squeeze-backports puppet puppet-common puppet-el puppet-testsuite puppetmaster puppetmaster-common vim-puppet vim-nox
kill -9 $(ps aux | grep puppet | grep -v grep | awk '{print $2}')
sed -i "s/START=yes/START=no/g" /etc/default/puppetmaster
cat /etc/default/puppetmaster 
apt-get install -y puppetmaster-passenger
mkdir -p /etc/puppet/files
echo "ServerName puppet" >> /etc/apache2/apache2.conf
service apache2 restart
puppet --version
netstat -a | grep 8140

Done.]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_36164_1329481328</link>
            <description><![CDATA[Why PSR-0 may be one of the most significant things to happen to PHP ever.

What is PSR-0?

Very basically, PSR-0 is a recently defined standard for autoloading classes in PHP-5.3.x and above. 
If you would like to have a much closer look, a copy of the final document can be found at https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md

I am not going to spend much time on explaining the document or autoloading in general, as that is an exercise best left to the reader, but as a quick introduction to those that have never worked with this kind of thing before, I will explain the very basics and leave it at that.

Basically, in the old days, you would have a class with name, say, User.php to do something with users. Great. Now to use that code you needed to go through certain steps in order to include() it and then instantiate and use it, like so:

<?php
require "User.php";
$user = new User("Paul");

So far, so good, right? Right! OK, so skip forward a few years and suddenly we see that there is a magic function called __autoload() in PHP5. Awesome! Let's look at how that works then:

<?php
public function __autoload($className) {
    $filename = $className . ".php";
    if (is_readable($filename)) {
        require $filename;
    }
}

OK, nice. Now we can load up files with a specific file naming structure with relative ease. One very big drawback here is that you could only ever register a single autoloader, which, in many cases was OK, but not the best situation. Skip forward to PHP-5.1.x and we see spl_autoload() being introduced. What this allowed was multiple autoloaders with the spl_autoload_register() function. Cool, now we could have a function to autoload the different components of our frameworks (such as controllers, views and models) with slightly different autoload functions, which tends to increase the security slightly as well as making tracking down bugs a lot easier.

This was all great stuff, and made our lives as large project developers much easier, but we soon started to see some cracks in the glossy sheen of autoloading. What people started to notice very quickly is that everyone was doing it their way. Look at a few examples of class names from projects that I have worked on:

1. Chisimba - user_class_inc.php
2. PEAR - PEAR_User which translates to PEAR/User.php
3. Zend - Zend_Cache which translates to Zend/Cache.php
4. etc. (see class.name.php, class_name.php, class.name.inc.php, classname.inc.php, etc...)

This is part of the reason that PSR-0 was conceived; to try and standardise some of this mess. 

What PSR-0 tries to do is to make sure that all classes and reusable objects are kept as such, reusable, across many different frameworks and codebases with minimal effort. These standards are also exploring other aspects of large project codebases such as cache interfaces as well, but those are still in active discussion.

By now, I guess you have read through the PSR-0 standard document as above. Let us take a look at it quickly.

Basically, the way that it all works is that you have to stick to a certain naming convention. That naming convention should implement a certain directory and code file layout according to a fully qualified namespace in your code. PSR-0 compliant autoloaders will then automagically be able to find the files by traversing the directory structure and include them for use in your code. Magic! An example can be found at https://gist.github.com/221634 with a SplClassLoader implementation that sticks to the standard.

In my own project(s), I have adopted the Symfony autoloader component though. You can find a bunch of information, as well as a guide and download link at http://symfony.com/doc/2.0/components/class_loader.html Please go and take a look there for additional information.


So why is this significant?

The actual subject of this post is the significance of PSR-0. Now that we have a pretty good idea of how it works, we can now start thinking of the possibilities associated with it!

Imagine starting a new project which requires a forum, a wiki and a file upload area. OK, not a huge ask, but will take time. These are also not uncommen web applications, so there will be a lot of reusable code lying around in other projects that we can use, but each class may need to be manually edited to work with the overall project, and many other finicky little subtleties may need to be addressed, which could end up taking as long as if you were to have coded everything almost from scratch. Not good.

If, however, we used a bunch of high quality components from any number of fine projects and frameworks out there already, we could simply start adding some, or all, of these components to our own project and simply writing a bit of glue code to hold them all together, which would significantly reduce the dev time on any project. Bug tracking and fixing becomes easier due to the use of namespacing and the overall velocity of the project increases. All good things.

The more projects and people that stick to these standards, (as they become available) the better. The more simple, reusable components that are created and made available via systems like packagist http://packagist.org/ the better. Many projects can now make use of these components by simply adding a line of JSON or two to a file and using a package manager like Composer (http://getcomposer.org/composer.phar) to work with them! Side note: If you do use Composer, it will automatically generate an autoload.php file for you, making this even easier!

As a final note, from a performance perspective, we all (should) know that require() and include() etc are pretty heavy operations, but by using SPL, we should even get a bit of a bonus performance boost (on top of the performance boost you get from using PHP-5.3.x and above anyway), as well as making your code more readable and maintainable. What reason could you have for not using it? :)

In conclusion, PSR-0 autoloading is the first of many standards. If you would like to participate in creating these standards, the working group is an open one and your feedback is welcomed.

The fact that these things are being collaboratively created makes for a very exciting time to be a PHP developer! This is one of the most significant things to happen to PHP since PHP5!

Keep an eye out and most importantly, have fun!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_42457_1326457955</link>
            <description><![CDATA[VoIP only phones

Not sure if this has been done or not, but this may be a pretty cool idea to steal.

Imagine an ultra cheap mobile device, in a phone handset form factor, that only does data based VoIP. Would be very cheap as you don't really need to put much into it feature or component wise and could very well replace devices like walky talkies and things like that in rural areas (farms etc) and many other places.

Is it cheap enough to manufacture something like that? Would people rather have the features of an entry level phone?
]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_91577_1324376671</link>
            <description><![CDATA[DSTV decoders and set top boxes in general

DSTV has an opportunity to do something awesome with their decoders. Many people have the things in their homes already, and with the next iteration of undersea cables coming along, more people will be able to make more use of broadband connections. Put these things togather and we have some ideas that may or may not be worth exploring...

Imagine the following: A set top box with a programmable API. Sure, keep the proprietary signal decoder bit proprietary, but open up an API on device for 3rd party apps. Create an appstore concept for developers to lodge apps in and make them downloadable via the satellite tv connection. This could open up a whole new world for people to explore and have more fun with their tv, which, after all, will translate into more folks signing up for the services and more folks spending more time using the services.

The tech could be simple. Android possibly, or a simple python interpreter would make the barrier to entry really low and the appstore ecosystem would flourish quickly. Paid for apps that would add in value added services could be deployed fast and easily, as well as be maintained from a central repository.

With something like this, there would be an app to tweet minute by minute football results! No need to watch your phone all the time, just set it to go and relax! 

What do you think?]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_31397_1322129489</link>
            <description><![CDATA[How to write a dead simple REST API using Python Bottle

REST API's should be simple. Following from that statement, implementation of REST API's should also be. Enter Bottle Python web framework http://bottlepy.org/docs/dev/

Bottle is a simple WSGI micro framework written in Python. I experimented with both Bottle and Web.py and found that although web.py is faster, Bottle is way easier to get going with on a simple app.

In order to quickly prototype a REST API with a MongoDB backend, you only need a few lines of code:

[code]
import json
import bottle
from bottle import route, run, request, abort
from pymongo import Connection

connection = Connection('localhost', 27017)
db = connection.mydatabase

@route('/documents', method='PUT')
def put_document():
	data = request.body.readline()
	if not data:
		abort(400, 'No data received')
	entity = json.loads(data)
	if not entity.has_key('_id'):
		abort(400, 'No _id specified')
	try:
		db['documents'].save(entity)
	except ValidationError as ve:
		abort(400, str(ve))
	
@route('/documents/:id', method='GET')
def get_document(id):
	entity = db['documents'].find_one({'_id':id})
	if not entity:
		abort(404, 'No document with id %s' % id)
	return entity

run(host='localhost', port=8080)
[/code]

Which will just about do it. Requirement is that you have the excellent pymongo package installed (get it at http://pypi.python.org/pypi/pymongo/ or just grab the Ubuntu package) and the Bottle packages (check docs for install instructions).

The code above is pretty self explanatory, but if you would like some additional notes, please do leave a comment and I will get back to you. Note the use of the route decorator, this is the bit that makes your life easy!

Happy RESTing... ;)]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_99799_1319787538</link>
            <description><![CDATA[When you copy someone else’s ideas and roll them as your own, claiming innovation, you are doing it wrong. When it is someone’s ideas or even implementation that you are copying, it is plagiarism, not innovation. Taking ideas from things and creating something new on top of those ideas that will benefit people, that is innovation.

Regarding idea theft. If I help you, it would be nice to at least have a mention. Sometimes. A little attribution goes a long way. If you ask me for help, I will probably help you. I don't want your money, I don't want anything like that from you, I would appreciate a thank you or an acknowledgement sometimes though. Your crappy little "startup" is probably going to fail anyway, especially if you treat people that have a lot to give like that. Sorry for the honesty, but you probably need a reality check anyway.

I give away all of my code as Free Software, acknowledge that it is mine at least. Don't claim it as your own, it isn't. Should I stop giving away my "Intellectual Property"? I would hate to do that, as I believe that anyone has the ability to think up the same things, but sometimes you may need a seed idea to kick those thoughts off. To me, IP is bullshit, but if I keep on finding people claiming my ideas and text as their own, is it?

I don't know. Perhaps I just need a break.]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_2884_1319786527</link>
            <description><![CDATA[Innovation

The term innovation derives from the Latin word innovatus, which is the noun form of innovare "to renew or change," stemming from in—"into" + novus—"new". Although the term is broadly used, innovation generally refers to the creation of better or more effective products, processes, technologies, or ideas that are accepted by markets, governments, and society. Innovation differs from invention or renovation in that innovation generally signifies a substantial positive change compared to incremental changes.

With that out the way, let us explore the way in which some folks are (ab)using this word.

The part that I would like to point out to the folks that are using the word "innovation" for every stupid project that they crank out would be the "substantial positive change" part. See the word [b]substantial[/b] there? Yeah, didn't think so. OK, so what is substantial? Substantial is [b]not[/b] doing the same stuff you would do anyway, by means of a mobile app, or a shiny new web site. It means a lot more than that. It means to do something in a new (novus) way in order to really have an impact on how most people would accomplish a task. Banks, you have been doing the same thing for hundreds of years. You are not innovative. Deal with it. A mobile app to do banking is still doing the same boring thing, just on a mobile device. This, to me, is not innovative. Evolutionary, maybe, but not innovative.

When your marketing team gets hold of the word "innovation" please stop them and think before you start spewing nonsense. It annoys people who are trying to actually make the world a better place.]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_35582_1318665416</link>
            <description><![CDATA[Using a framework to reduce technical debt

Ward Cunningham coined the phrase technical debt, and it seems to be one of those magical phrases that truly encompasses a number of software development issues that companies face every day.

Take a small software startup as an example. 

The Minimum Viable Product (MVP) is the piece of software that the business is based upon. This will form the basis of most of the future operations of the company and will normally make or break the company in terms of additional funding or future business prospects. Writing a complete MVP from scratch is normally not a good way to proceed. Very little of any new startups business is all new, but rather a new idea or model based on existing ideas. This is the key piece of information and should be the focus of the startup.

Technical debt is introduced when architectural decisions are not taken wisely from the beginning of the project and large amounts of refactoring, or indeed, rewrites of the software are needed.

This is somewhat avoidable in the short and medium terms, giving the technical team more time to get out a MVP and start with business processes, as well as buy more time for innovation in the niche space in which they are attempting to carve or operate in.

How can this technical debt be avoided? Quite easily in fact. Use a framework. Take Chisimba for example (or any other big framework that you may be familiar with) and see what I mean. If I know that I can install Chisimba and it will take care of things like my collaboration environment, a wiki, a company blog and numerous othehr bits and pieces needed to get going, I can start work almost immediately on the things that I am passionate about (my niche). This also brings in another very important point about minimising technical debt - focus. The more diluted your focus is, the chances of failure increases exponentially. Focus all of your products based on a single framework and give your development team time to become expert at that framework. In the long run, technical decisions like "what framework/code base to use to start" are done for you and your team is allowed to get on with it from the start. This allows for an increasingly rapid (and more agile) response to the evolving business environment. This is especially important in software development environments, as the landscape is extremely fickle, changes very rapidly, and often, by the time that you have a MVP out, someone else has done it already, thereby losing all competitive advantage that you may have gained previously. By utilising a framework that you are expert at, you can rapidly prototype, make quick changes and have an MVP out the door for market testing/suitability in days instead of weeks or months.This also gives you the advantage of being able to retract a product from the market and essentially start over numerous times, as not much time (and therefore money) has been spent on any single project.

In the short term, you will feel pain. You need to be aware of this. A framework is not everything to everyone and it will take time initially for your dev team to get up to speed, but in the long term, you will easily be able to leverage that expertise for additional productivity. The mere fact that devs will be under less pressure to write additional base code for every other project will also make your dev team happier. I don't think I know any developer that actually likes doing donkey work (same old base code to get an app together) and your technical architect can then focus on radical improvements to existing base architecture (innovation) as well as making things faster, more efficient, more friendly and generally increasing the competitive gap that you have over any competitors (present and future).

I guess the ramblings above boil down to one core aspect - focus. Have it. Use it. Profit!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_60261_1316888290</link>
            <description><![CDATA[Getting the SD Card reader to work on a Dell Latitude and Ubuntu Natty

If you have a Dell Latitude series laptop (widescreen version) you probably have experienced the fact that the SD card reader refuses to work. 
This is due to the fact that the SD card reader and the Firewire port share space in the BIOS.
Thankfully, it is pretty easy to fix.

[code]
vi /etc/modprobe.d/sdhci-pci.conf
[/code]

add the following lines:

[code]
options firewire-ohci quirks=0x10
options sdhci debug_quirks=0x40
[/code]

Then update initramfs

[code]
update-initramfs -u
[/code]

reboot and Voila! Go read some SD cards!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_85074_1316690235</link>
            <description><![CDATA[How to write a quick Favourite Tweet backup script.

Someone asked me to do a quick @twitter "favorites" backup script for them, and the result is that it is very easy to do. I wrote the code in Python, but I guess anything else would probably do as well.

I chose to go the full OAuth route, but I think (I may be mistaken though) that you can do the following with only basic auth involved.

Firts up, you will need a copy of tweepy, which is a python module to work with Twitter. Grab it at http://code.google.com/p/tweepy/ It has good support for the latest twitter API, so you should be good there.

Next up, you will need to create an app on twitter for the OAuth dance to interact with. Again, if you are using basic auth, you won't need to do this, but I did anyway (besides, it gives you a good base to build a bunch of other twitter stuff with)
You need to make your app give up a user secret and key so that you can sign off the requests. You can do that with the following code:

[code type="python"]
#!/usr/bin/env python

import tweepy

CONSUMER_KEY = 'consumer_key'
CONSUMER_SECRET = 'consumer_secret'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth_url = auth.get_authorization_url()
print 'Please authorize: ' + auth_url
verifier = raw_input('PIN: ').strip()
auth.get_access_token(verifier)
print "ACCESS_KEY = '%s'" % auth.access_token.key
print "ACCESS_SECRET = '%s'" % auth.access_token.secret
[/code]
Remember to substitute the consumer_key and consumer_secret with the values that twitter gives you when you register your app.

What the above code will spew out is an access key and access secret which you will need to copy and paste to the next bit of code:

[code]
#!/usr/bin/env python
import time
import sys
import tweepy
import csv

CONSUMER_KEY = 'consumer_key (same as above)'
CONSUMER_SECRET = 'consumer_secret (same as above)'
ACCESS_KEY = 'access key generated from the above code'
ACCESS_SECRET = 'access secret generated from the above code'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_KEY, ACCESS_SECRET)
api = tweepy.API(auth)

# substitute the username here with your own username!
userid = api.get_user(username).id
 
# Counter is used for reporting purposes only
i = 1
 
'''
Empty lists are false in Python, so we keep on going in an infinite loop
until the list is empty. Hack? Probably. Works? Yes!
'''
 
# Initiate an infinite loop, to be broken when we run out of favorites.
while True: 
 
    results = api.favorites(userid)
    
    if results:
        favWriter = csv.writer(open('twitterfavs.csv', 'wb'), delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL)
        print "Page %d exists. Unfavoriting %d tweets." % (i, len(results))
        for r in results:
            tweetdate = r.created_at
            tweet = r.text
            if hasattr(r.user, 'screen_name'):
                name = r.user.screen_name
            else :
                name = "Unknown"
            line = [tweetdate.isoformat(), tweet.encode('utf8'), name.encode('utf8')]
            favWriter.writerow(line)
            api.destroy_favorite(r.id) # This will unfavourite the tweet too so you are left with 0 favs. Use at your own discretion.
 
        # Rest for a while. Stupid rate limits...
        print "Script is sleeping for 15 minutes to accommodate Twitter rate limits."
        print
        i += 1        
        time.sleep(900)       # 900 seconds = 15 minutes
    else:
        print "No more favorites. Start again!"
        break
[/code]

As you can see, the code will go through all of your favourites and write them to a CSV file. Remember that I am only grabbing a small subset of the information from a tweet to write, so if you need more, look at the tweepy API docs.
We also need to watch out for rate limits. Twitter limits at 150 an hour (IIRC) so if you have loads of favourites, this could run all night. If you only have a few, go have a bere or some milk and you will be done!

Have fun!

(Again, please excuse the formatting, I will work it out sometime)]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_92772_1316507039</link>
            <description><![CDATA[HOWTO create a massively scalable and very fast Geo database using MongoDB

First things first, I will assume that you have MongoDb set up and running on your server. If you do not, on Ubuntu it is really easy, other distros, not sure. RTFM.

OK, so we need to create a geographic database for geocoding, reverse geocoding and some simple spatial queries. MongoDB is fast and easy to do this with, so we shall proceed from there.

NOTE: If you are using a 32 bit operating system, your dataset will be limited to about 2GB. Keep that in mind as we proceed. If you are going to try and import the entire CC-BY licensed dataset in this HOWTO, you will need a 64 bit OS.

Now to the fun part:

1. Make sure that Mongodb is up and running on your server (there is an upstart script to do so - start mongodb)

2. Make sure that you can connect to your MongoDB server with mongo (client). If you can, you are ready to proceed.

3. Open up a terminal (if you use Windows or something, you have bigger problems) and grab some of the geonames.org data (or all of it) with:
    [code]
    wget -r -l2 -nd -Nc -A.zip http://download.geonames.org/export/dump/
    unzip \*.zip
    mkdir zip
    mv *.zip zip/
    [/code]
    You may also want to remove the cities*.txt and other lowercase file names, as well as the README file for the next step.

4. Make sure that all of the .txt files have an uppercase 2 letter ISO country code as the filename. Any other files will more than likely break the next step, as I really didn't take too much time to write good code. The next bit is used once only.

5. Write a quick Python script to insert the data to MongoDB
   [code type="python"]
   import glob
   import csv
   csv.field_size_limit(1000000000)
   from pymongo import Connection, GEO2D

   db = Connection().geopoints
   db.places.create_index([("loc", GEO2D)])

   def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs):
       csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs)
       for row in csv_reader:
           yield [unicode(cell, 'utf-8') for cell in row]

   li = glob.glob('*.txt')
   for filename in li:
       print "Doing "+filename
       reader = unicode_csv_reader(open(filename), delimiter="\t", quoting=csv.QUOTE_MINIMAL)
       for row in reader:
           insdict = {"loc": [float(row[5]), float(row[4])], "geonameid": [row[0]], "name": [row[1]], "asciiname": [row[2]], "alternatenames": [row[3]], "latitude": [float(row[4])], "longitude": [float(row[5])], "featureclass": [row[6]], "featurecode": [row[7]], "countrycode": [row[8]], "cc2": [row[9]], "admin1code": [row[10]], "admin2code": [row[11]], "admin3code": [row[12]], "admin4code": [row[13]], "population": [row[14]], "elevation": [row[15]], "gtopo30": [row[16]], "timezone": [row[17]], "modificationdate": [row[18]]}
           db.places.insert(insdict)
    print filename + "Done"
    [/code]
    
6. You will notice that the code above (excuse me if the indentation is messed up...) makes use of the python-mongo code, which you will also need. Again on Ubuntu, it is as simple as: apt-get install python-pymongo

7. Execute the script. 

8. Next, download a copy of Chisimba and the geo module (WIP) and install that. Configure your MongoDB connevtion in the sysconfig and Voila! You have a geocoding and reverse geocoding API. Easy!

9. Profit!

NOTES: You will notice that the pymongo script creates a 2D geospatial index for your data. I would also recommend that you create some additional indexes according to your app.
       The database and collection names can be changed to anything you like. Just keep the whole db.collection string less than 128 chars long.
       The CSV field size limit is huge. I doubt it needs to be THAT big, but it is safe and rather be safe than sorry right?
       If your dataset is to grow more, or you need even more performance, look at sharding with mongos too.
]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_42446_1315406046</link>
            <description><![CDATA[Laptop tracking system (cheap)

Following on from a discussion on a work forum, I decided to have a stab at writing a super simple laptop tracking system. The main aim of the system will be to track stolen laptops and (hopefully) recover them somehow.

After a little bit of consideration, I decided to have a look at doing this in a client/server way with a MongoDB back end. The following is a simple, but workable system that uses the python bindings to the DBUS messaging system on Linux (tested on Ubuntu 11.04), so I am doubtful that this could be used for anything other than that. That being said, however, the client bit simply needs to send through wifi access points and MAC addresses to get a triangulation on the position of the laptop, so I am pretty sure that this can be achieved with relative ease on other platforms.

Triangulation is done via a Google API and the coordinates as well as the accuracy level is then inserted to the MongoDB back end for mapping or whatever else needs to be done with the data. Features that the client should also probably support include bricking the machine remotely or something to that effect, as well as possibly sending messages or SOS tweets or something to let people know that it is not in its rightful owners posession.

Enough rambling! To the code!

Client:

As I said, the code uses python-dbus and the standard json and urllib modules. You can install python-dbus with an apt-get install python-dbus.

[code]
import dbus
import json
import urllib

NM = 'org.freedesktop.NetworkManager'
NMP = '/org/freedesktop/NetworkManager'
NMI = NM + '.Device'
PI = 'org.freedesktop.DBus.Properties'

def list_ssids(): 
    bus = dbus.SystemBus()
    nm = bus.get_object(NM,NMP)
    nmi = dbus.Interface(nm,NM)
    # Iterate over the devices queried via the interface
    for dev in nmi.GetDevices():
        # get each and bind a property interface to it
        devo = bus.get_object(NM,dev)
        devpi = dbus.Interface(devo,PI)
        if devpi.Get(NM+'.Device','DeviceType') == 2:
            wdevi = dbus.Interface(devo,NMI + '.Wireless')
            wifi = []
            for ap in wdevi.GetAccessPoints():
                apo = bus.get_object(NM,ap)
                api = dbus.Interface(apo,PI)
                wifi.append({'ssid':''.join(["%c" % b for b in api.Get("org.freedesktop.NetworkManager.AccessPoint", "Ssid", byte_arrays=True)]), 'mac':''.join(["%c" % b for b in api.Get("org.freedesktop.NetworkManager.AccessPoint", "HwAddress", byte_arrays=True)])})
                
    return wifi

if __name__ == '__main__':
  ap = list_ssids()
  data = json.dumps(ap)
  params = urllib.urlencode({'wifi': data})
  f = urllib.urlopen("http://127.0.0.1/junk/geo/geopost2.php", params)
[/code]

You will need to modify the post URL at the end to somewhere meaningful for yourself of course.

Initially, I did the client using the PHP ext/dbus, but decided against that due to the fact that it is hard to install and nobody really uses it...

The server side is just as simple. You will need a Mongodb instance running on the server and then you need a simple script to catch the POSTs. NOTE: This script is just a concept, so if you are actually going to do something like this, clean it up!

[code]
<?php
$wifi = $_POST['wifi'];
$wifi = json_decode($wifi);
$request = array( 'version' => '1.1.0', 'host' => 'myurl.com', 'wifi_towers' => $wifi );
$c = curl_init();
curl_setopt( $c, CURLOPT_URL, 'https://www.google.com/loc/json' );
curl_setopt( $c, CURLOPT_POST, 1 );
curl_setopt( $c, CURLOPT_POSTFIELDS, json_encode( $request ) );
curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
$result = json_decode( curl_exec( $c ) )->location;

$fields = array(
            'lat'=>urlencode($result->latitude),
            'lon'=>urlencode($result->longitude),
            'accuracy'=>urlencode($result->accuracy),
            'laptopid' => urlencode('16cptl-pscott'),
            'wifi'=>urlencode(json_encode($wifi)),
        );


// connect
$m = new Mongo();
// select a database
$db = $m->laptoptrack;
$collection = $db->lappoints;

// add a record
$obj = array( "loc" => array("lon" => floatval($fields['lon']), "lat" => floatval($fields['lat'])), "lon" => floatval($fields['lon']), "lat" => floatval($fields['lat']), "accuracy" => intval($fields['accuracy']), "laptopid" => $fields['laptopid'], "wifi" => json_encode($wifi));
$collection->ensureIndex(array('loc' => "2d"));
$collection->insert($obj, array('safe'=>true)); 
[/code]

So from the above code, you will see that we create a 2d geospatial index on the Mongodb instance as well. Not sure if this is useful, but it will probably help in speeding up queries like "Gimme all the laptops that Company X owns in area Y" or something - if that is something that you would like to add to your query interface of course...

Also, I am not 100% sure of the legality of storing SSID's with a location, so check that one first too!

Dead simple, works well. 

I would say that the client bit should be on a cron job or something that pings the service every hour or something.

Remember: Mongodb works best on 64bit OS. If you are using a 32bit arch, then you will only be able to store around 2GB data at a time. Depending on the need for historical records etc, keep that in mind...

Most importantly, HAVE FUN!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_68627_1314947641</link>
            <description><![CDATA[The first iteration of a location database is now available for use. All data is Creative Commons BY licensed, which means that you can use it for ANY purpose whatsoever (including commercial use). That being said, if you think that your app using the database may create a lot of traffic, please notify me so that I can make adequate provisions.

The API will return JSON for use in mobile, web or even desktop applications. In the very near future, the semantically linked data will also be returned in JSON-LD.

The API has a number of endpoints, with more becoming available soon. For the time being, it is a read only API, but that will change over the coming weeks (as I get time to implement more). The basic usage is something like:

1. getbylatlon - Gets a point(s) and nearby points by passing a longitude and latitude pair (WGS84) to the function. You may also specify a radius in metres (m) to get points in a sphere around that point too. Example:

http://geo.chisimba.com/index.php?module=geo&action=getbylonlat&lon=18.56&lat=-33.59&radius=100

This will get the points around the point specified within a radius of 100m and return them as a JSON string.

 

2. getbyplacename - The get by place name function takes a single urlencoded string as a place name and an optional radius to search around as well. If the place name is not found at all, an empty JSON string will be returned. Example:

http://geo.chisimba.com/index.php?module=geo&action=getbyplacename&placename=Milnerton&radius=100

where the placename is "Milnerton" and the optional radius parameter is 100m

 

3. getwikipedia - will get the wikipedia content from a specific latitude and longitude pair. This is dependant on wikipedia location tagging:

http://geo.chisimba.com/index.php?module=geo&action=getwikipedia&lon=18.513600833955&lat=-33.810299107327005

 

4. getflickr - will return flickr photos from the location:

http://geo.chisimba.com/index.php?module=geo&action=getflickr&lon=18.513600833955&lat=-33.810299107327005

 

NOTE: Both the flickr and wikipedia functions are a work in progress, so they may spuriously be down over the next few days.

 

All functions can be called without authentication by using cURL or a similar library. Please remember to cache! Bandwidth is expensive and this is a personal project!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_36170_1314085894</link>
            <description><![CDATA[Is infrastructure really a barrier to education?

A few folks over the years have asked me what the biggest barrier to (online) education is and the pretty stock standard answer has normally been an infrastructure related one.
This does not need to be the case. Over the past few years, I have been thinking about an infrastructure deployment that would work in most areas of the developing world, as well as those in the more developed regions.
The answer is mesh networks. Yes, a few people are experimenting with mesh connected devices for edu already, but I do not believe that they are seeing the true potential here. Most are small scale, underfunded and rely on donor grants. That should not really be the case, as there is an alternative approach that can work with the cooperation of banks/loan vendors or some corporate backing in the same way. Let us have a look at what I am on about shall we?

Small, wireless thin client devices. I have done some research into these things and it seems that the shopping list below is not a huge ask, it just needs for someone to actually do it!

1. small to very small form factor. The device should be small enough to be stuck to the back of a screen.
2. 12v Power supply. This means that the device can also be powered by solar panels, small wind turbines or a car battery.
3. Resistant to dust.
4. Operates in an ambient temperature range of around 5-40 degrees Celcius
5. Has multiple USB ports (at least 2 but preferebly 6)
6. Has wifi mesh capability. This is the most important one. Many mesh networks can operate in proximity at greater than 1 Gigabit per second.
7. Outputs for (wireless) keyboard and mouse as well as a (HD/HDMI) monitor.
8. NO moving parts (possibly a fan, but limited to that). Easy to maintain and replace parts.
9. Open hardware and BIOS/ROM if possible. Will allow folks to maintain the devices easily with minimal training.
10. Audio in and out.

With these basics, we can set up a mesh of devices in proximity based urban conditions (like informal settlements and tower apartments) - both vertically and horizontally. Devices should be given to people at very low cost or free of charge in order to minimise theft and essentially flood the market. All devices within a proximity area should connect to each other in multiple ways, and then utilise a wired backhaul to the internet on a high speed connection. Server set up, maintenance and connection can be run as a business by a single person and all connected devices can subscribe optionally to an internet connection at a small monthly charge to offset costs.

What these kinds of networks will provide is:

1. a Community based network where skills can be shared and learned on an eLearning system hosted on the server.
2. IP phones within the network, and optionally the internet via the backhaul, and IP TV streamed directly from the server. IP TV could be a paid service as well.
3. Access to educational resources and Creative Commons licensed courseware to learn new skills and possibly get a better job
4. Government services via an eGov initiative (especially social services, HIV education, etc)
5. Community bulletin board style communication to post notices, services on offer and possible job openings.
6. A bunch of other good things.

The next steps really involve getting together a proof of concept. This shouldn't be too challenging. Other things that should be considered are creating a standard business plan that people could take to banks etc to fund the server/backhaul setup as a SMME and then just doing it!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_77206_1312184622</link>
            <description><![CDATA[The start of a new era

Today I start a new job. Something that I have not done in over 8 years at UWC. 

I would like to thank UWC for all the good times, and I am really looking forward to my time at MIH SWAT. I will probably post some stuff a little later on OSCON too, so if you are interested in that, keep an eye out!

Thanks UWC and hello MIH! Lets do it!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_68827_1308153769</link>
            <description><![CDATA[Disruptive Tech

I have been seeing many instances of the term "disruptive tech" being used all over the web recently and would like to make a few comments as to what I understand from this. I asked a few people on twitter as to what they understood by the term (especially some folks that were actually using the term) and the overwhelming answer was "A technology like mobile phones that changes the way we do things". OK that is fine, but I don't buy that as a "disruptive" technology. Disruption to me would be something that [b]fundamentally[/b] changed the way that humans do things that allow a certain evolutionary pathway to be opened up. To me, disruptive tech would be something like controlled fire making, the wheel, electricity and building on the communications example above, the telephone itself. Those things changed the way we do things. Mobile phones, to me at least, are "evolutionary tech". That is, they are technologies that have undergone a natural evolution (mobility) to enable us to do more of the same thing (phone communication) in an easier way.

I am seeing this term also being adopted by marketing people to describe certain web apps. How is a single web app disruptive? The web itself may be disruptive tech, but a single app? Never in my humble opinion! Some semantic web applications (not in the single app sense of the word of course) may become somewhat disruptive, as IPv6 based devices and folksonomy start feeding into a wider network of things, situations, contexts and locations, but until that is commonplace, it is difficult to justify calling just about anything on the web right now "disruptive".

I can also see the day that someone starts calling themselves a "Disruptive Technologist". Much the same way that some call themselves "Social Media Gurus". I do hope that the ghosts of Leonardo Da Vinci, Nikola Tesla and others haunt them!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_81544_1307010076</link>
            <description><![CDATA[Please don't steal this idea

I have been tossing around the idea of an "anti-curation" application. Now, we all see that curation is really hot right now and everybody and their grandmother uses some sort of curation system to build up little newsletters or pages to share with their friends. Usually stuff that is interesting to you and your in group.

To me, these kind of curation systems are a little flat. The problem of contextual realtime (search for that on this site for more detail) becomes more and more of an issue as people curate other people's links and pages. There is also very little social mindedness in these types of applications. I, for one, would like to take a slightly different approach to curation itself.

My idea here would be to get your friends to curate stuff for you. Things that a group of trusted people you know will curate decent stuff that you will be interested in. That is, anti-curation. This would work OK, except how do you ever find people with similar interests to you? Ah, well therein lies the tricksy bit. I think that a Hadoop cluster would be a good thing here. Think about a billion tweets a week going into a Hadoop cluster. You then run almost user defined Map/Reduce jobs against that store and get some simple metrics from it. Much the same way that I presume "Trending Topics" on twitter works. OK, nothing too spectacular yet. That should give you all top things that your group are interested in across a time scale (e.g. weekly, daily, hourly, whatever). You also get the advantage that you see stuff once. A Retweeted URL that did the rounds in your group could get annoying, but a system like this would reduce that to a single occurrance of each "thought". So this now sounds like a realtime social proxy/filter system. Good. We can make it better.

At some point, your trusted group of friends may become a little stale and your interests may change slightly. We then have the opportunity to run Euclidean Distance algorithms across the entire data store to look for closest relatives to your interests. These can also be almost user defined, so you could search for a hash tag, a keyword, a user, a URL, almost anything. A simple interface to refine your interests would be easy to put together as well, so that filtering and anti-curation can be done in just as near as damnit realtime. Cool!

Some of the cool things about this would be that it is not restricted to a single web presence like twitter OR Facebook OR whatever, but anyone can curate anything for anyone anywhere. A simple browser extension would suffice for that.
Another very cool thing about this would be that the distance algorithm need not only work on folksonomy (tags etc), but also around images, videos, audio etc.

The same system will easily work as a curation system, a social bookmark service and an anti-curation service. What a win!

Data can be sent back to users in almost any way too. Web, XMPP feeds, RSS feeds, tweets, facebook status updates, and more.

Sound exciting? Thoughts?]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_52714_1306994156</link>
            <description><![CDATA[How to add a Google +1 button to your Chisimba site

There is nothing much to adding a +1 button to your Chisimba site. If you would like to add the button to individual articles on, say, your Chisimba blog,
then please read the docs at http://code.google.com/apis/+1button/ especially taking note of the URL section. Again, nothing too strenuous.

Basically you need two things. A piece of Javascript in either the head section of your page template, or else the same JS just before the closing body tag. I prefer the closing body tag as then the js loads from Google last and doesn't interfere too much with your content loading.

Anyway, the line to add is:

[code]< script type="text/javascript" src="http://apis.google.com/js/plusone.js" >< /script >[/code] (Skipping the spaces between the angle braces)

Then you simply need to add: [code]< g:plusone >< /g:plusone >[/code] somewhere strategic that you want the button to render.

Very easy. ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_96406_1304061173</link>
            <description><![CDATA[Upgrading Ubuntu to Natty Narwhal

So @cazpi decided to upgrade to Natty last night. Everything went smoothly until the restart where the whole system crashed on startup. This seemed to me like a graphics driver issue, so it was relatively simple to fix.

1. Restart the machine, holding down the shift key to get the GRUB menu.
2. Choose the failsafe kernel and select "Low graphics mode"
3. Boot into Ubuntu with no graphics acceleration.
4. Go to the "System" menu, administration -> additional drivers and select the NVidia proprietary driver. It will say that it is not in use.
5. Remove the driver, then add it again. It will download and install a newer version.
6. Reboot into a prettier, faster and easier to use Ubuntu Unity desktop!

No other issues so far. Graphics card is a NVidia 4GB card.

Hope this helps someone else!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_21890_1303017655</link>
            <description><![CDATA[The evolution of the Chisimba framework

[YOUTUBE]http://www.youtube.com/watch?v=2T1P1mABabI[/YOUTUBE]

This is a gource visualisation of the development of the Chisimba framework only. This does NOT include any modules, skins or 3rd party code, just the framework stuff. Enjoy!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_94178_1302237631</link>
            <description><![CDATA[Linux is 20 years old (via the Linux Foundation on YouTube!)

[YOUTUBE]http://www.youtube.com/watch?v=5ocq6_3-nEw&feature=BF&list=WL8383840D3F56E5B8&index=1[/YOUTUBE]]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_20533_1301915070</link>
            <description><![CDATA[Collaborative Photo gallery

Let's make a photo gallery shall we? 

The challenge is to create a collaborative photo gallery of the coolest photos on Flickr. Yes, you can add your own if you think they make the grade!The process is very simple:

1. If you have an account on Flickr already, go to http://www.flickr.com/blogs_add_metaweblogapi.gne
2. Fill in the form, using http://www.paulscott.za.net/index.php?module=simplegal&action=metaweblog as the API endpoint, flickr as the username and flickr as the password
3. click Next
4. Ensure that the remember password checkbox is checked (else you will have to remember the password) and give it a meaningful label.
5. Click "All Done"
6. Find cool photos
7. Click the share link to share it to the collaborative gallery.
8. Go check out http://www.paulscott.za.net/index.php?module=simplegal to see what you and others have done!

Cool! Have fun! Cannot wait to see what you all post!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_26058_1301505083</link>
            <description><![CDATA[How to add a Facebook "like" button to any content item in Chisimba

Adding a Facebook like button to any content item in Chisimba is relatively easy. There is a little code to help you on your way, plus you will need to register an application for your URL.
I chose to use the FBML method as it did not require an additional iFrame which I always try and stay away from.

Warning! This [b]will[/b] slow down your site a bit due to the requests to Facebook, so those on slow connections, please do take care!

OK so with that out the way, the process is pretty simple. We first need to make sure that you are using a version 3 skin (any of the canvas style skins work) and that your _common directory in the skins directory is up to date as of today.

You then need to add in the OpenSocialGraph meta tags to your document head. I did this by writing a quick function to add it to the header params in my ops class:

[code]
$fbadmin = $this->objSysConfig->getValue('fbadminsid', 'facebookapps');
        $fbapid = $this->objSysConfig->getValue('apid', 'facebookapps');
        $oghead = '< meta property="fb:admins" content="'.$fbadmin.'"/ >
                   < meta property="fb:app_id" content="'.$fbapid.'" / >
	           < meta property="og:type" content="blog" />		
                   < meta property="og:title" content="'.$this->objConfig->getSiteName().'" / >    	
                   < meta property="og:url" content="'.$this->uri('').'" / >';
        // add the lot to the headerparams...
        $this->appendArrayVar('headerParams', $oghead);
[/code]

(please note that I have left a space between the < and > to avoid this thing parsing them!)

You are free to change the og:type tag to blog or article, depending on your content type.
You will notice that the Facebook API and admin ID's are grabbed from the facebookapps config settings, so obviously you need that to be installed.

You then need to generate an unique URL for your like button to refer to. I did this with:

[code]
$url = $this->uri(array('action' => 'viewsingle', 'id' => $s['id']));
$fburl = htmlspecialchars($this->objConfig->getsiteRoot().$url);
[/code]

This where the id I got from the database record of the content item that I am building the like button for.
Once that is done, you simply need to build the like button and plonk it somewhere.

[code]
$fb = "< fb:like href=\"$url\" layout=\"button_count\" show_faces=\"true\" action=\"like\" colorscheme=\"light\" >< /fb:like >";
[/code]

There are a few things that you still need to do, so be patient!

First up, go and register your app on the Facebook developer portal at http://www.facebook.com/developers/apps.php Make sure that the URL is correct to get back to your app!
Then go and find your Facebook ID using the social graph API. This is the fbadminid config parameter. Mine is at http://graph.facebook.com/paulscott56 but you would obviously change the end bit to your own username. The "FB admin ID" is the "id": "666600991" bit. Copy just the number and set it in your facebookapps config setting. Once your app is registered, copy and paste the API Key, secret and App ID to your facebookapps config setting.

Fire up your module. You will now see a facebook like button to click and enjoy. Great! OK now go read the developer docs on customizing the like button to your hearts content!

Have fun!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_9630_1301291371</link>
            <description><![CDATA[VLIR Mozambique mission

I have recently returned from Mozambique where I participated in a VLIR sponsored workshop about eLearning and Chisimba. The workshop was a culmination of the VLIR NSS project (North-South-South) between VUB (Brussels), UoN (Kenya) and UWC (South Africa). The NSS project was a 2 year project started to create a repository of resources for educators to use in eLearning. There are some specific docs available in the repository centred around Chisimba, but mostly they are purpose built documents on educational theory and best practice in eLearning.

The repository itself can be used as any other subversion repository at http://trac.uwc.ac.za/trac/VLIR

The first days of the workshop were spent on planning and finalising the budgets of the project. That left us with an intense 3 days of actual workshop with approximately 20 delegates from various faculties from the University of Eduardo Mondlane (UEM) including ditance learning, education and computer science.

The delegates were happy with the course materials and repository and even managed to produce some updated versions of some of the repoitory documents during the course of the three days!

A big thank you to Francisco Mabila and UEM for being a most gracious host!

Photographs are available at http://www.flickr.com/photos/paulscott56/sets/72157602346136749/]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_49789_1299772507</link>
            <description><![CDATA[Dear SAA Voyager programme,

Despite having flown international flights already this year, as well as numerous international flights last year (all of which were on SAA) you seem to have revoked my Gold Status to Blue Status again. I would like to take this opportunity to say thank you for absolutely nothing.

I have never had the opportunity to use any Voyager miles (besides an emergency flight 3 years ago to Port Elizabeth), as they are essentially useless. For the past few years, I have made an annual trip to Portland Oregon in the USA, including flying internal flights on Star Alliance partners, which alone should be sufficient mileage to move up from Blue status. I work in a job that demands numerous trips all over Africa, which I insist on flying SAA (or should I say used to insist - as I will no longer put myself through that any more) in order to make use of the SAA lounges on those routes (which are very seldom worth the effort, besides OR Tambo and at a push Cape Town International).

Please could you actually let me know what the benefits of using your Voyager programme are? I have never had the fortune of being upgraded, having a better seat (despite me being 2.02m tall) or anything else of any use whatsoever. The lounge food is terrible, the service is sub standard and priority check in often takes longer than my non-Voyager colleagues.

Your Voyager web site is a complete mess and I have only been able to log in there once or twice. The login kicks you out at random and most of the time I have had to log in twice in order to just see my account. This is not a good thing, especially seeing as though you punt that abomination on every single domestic flight to the point that I want to tear my remaining hair out. 

The entire Voyager programme and the SAA website at flysaa.com is an embarrassment to the country.

I have now booked an international flight for the end of the month. This will be the last time that I insist on flying SAA. Thanks for nothing. Goodbye. ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_38440_1298284017</link>
            <description><![CDATA[Master/Slave replication on Ubuntu

This is a short HOWTO to set up a master and slave replicating database on Ubuntu using the popular MySQL server.

Steps:

1. On the Master server, you need to edit the my.cnf file located in /etc/mysql/my.cnf and modify the following:
    In the mysqld section find the "logging and replication" section, then uncomment the server-id = 1 bit. This is to identify your master as the first ID. Then add in the following lines:
    [code]
    binlog_do_db=chisimba  # input the database which should be replicated
    binlog_ignore_db=mysql # input the database that should be ignored for replication
    binlog_ignore_db=test
    [/code]
    
2. Still on the master server, you then need to create a user and assign some rights to that user to push the data to the slave(s)
    [code]
    mysql> grant replication slave on *.* to 'replication'@192.168.10.2 identified by 'slave';
    [/code]
    Remember to change the IP address to the IP address of your server!
    
3. Restart your MySQL server with /etc/init.d/mysql restart (or use upstart and do a mysql restart)

4. On the slave server, go to the logging and replication bit again in the my.cnf and add the following:
    [code]
    server-id=2

    master-host = 192.168.10.1
    master-user = replication
    master-password = slave
    master-port = 3306
    [/code]
    Taking care to change any variables (like IP address and passwords) to match your own setup.
    
5. Restart your slave MySQL server.

6. Check that the slave is up and running (as a slave)
    [code]
    mysql> start slave;
    mysql> show slave status\G;
    [/code]
    
    You should get something similar to the following:
    [code]
    *************************** 1. row ***************************
             Slave_IO_State: Waiting for master to send event
                Master_Host: 192.168.10.1
                Master_User: replica
                Master_Port: 3306
              Connect_Retry: 60
            Master_Log_File: MASTERMYSQL01-bin.000009
        Read_Master_Log_Pos: 4
             Relay_Log_File: MASTERMYSQL02-relay-bin.000015
              Relay_Log_Pos: 3630
      Relay_Master_Log_File: MASTERMYSQL01-bin.000009
           Slave_IO_Running: Yes
          Slave_SQL_Running: Yes
            Replicate_Do_DB:
        Replicate_Ignore_DB:
         Replicate_Do_Table:
     Replicate_Ignore_Table:
    Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
                 Last_Errno: 0
                 Last_Error:
               Skip_Counter: 0
        Exec_Master_Log_Pos: 4
            Relay_Log_Space: 3630
            Until_Condition: None
             Until_Log_File:
              Until_Log_Pos: 0
         Master_SSL_Allowed: No
         Master_SSL_CA_File:
         Master_SSL_CA_Path:
            Master_SSL_Cert:
          Master_SSL_Cipher:
             Master_SSL_Key:
      Seconds_Behind_Master: 1519187

    1 row in set (0.00 sec)
    [/code]
    
    On your master server, check that the master is running as a master too:
    [code]
    mysql> show master status;
    [/code]
    You should get something like the following:
    [code]
    +------------------------+----------+--------------+------------------+
    | File                   | Position | Binlog_Do_DB | Binlog_Ignore_DB |
    +------------------------+----------+--------------+------------------+
    |MysqlMYSQL01-bin.000008 |      410 | chisimba     |                  |
    +------------------------+----------+--------------+------------------+
    1 row in set (0.00 sec)
    [/code]
    
7. That is pretty much it! Make sure that your slaves are started after the master and then do some quick updates/deletes/inserts to check that the replication is taking place.

Enjoy!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_49112_1296767434</link>
            <description><![CDATA[Double Gauss lens design and the web

So while I was starting a piece as a guest blogger on a photography blog, I started thinking about how lens technology is so simple, but yet yields such incredible results. What is the secret there? Well, the secret is simple. Simplicity. OK, yes, we have all heard the old adage of KISS principal (Keep It Simple, Stupid), but this takes that to a whole new level. I am not talking here about user interactions with your software, but rather about the entire ecosystem of your site and design.

Here I would like to introduce you to one of my favourite camera lenses. The Nikon AF Nikkor 50 mm f/1.8D. It is a 50mm fast lens that really performs well in low light conditions. What does this have to do with software design you ask? Well, I am glad you asked! The answer is almost everything! Not the overall finished product, or even what it does, but rather the way that it works.

The best thing about this lens is that it is really cheap, and gives amazingly sharp and clear results, even on lower end cameras. The reason it is cheap is that there is almost nothing to it. It works on a very simple lens design called Double Gauss lenns design. Check out the Wikipedia entry at http://en.wikipedia.org/wiki/Double-Gauss_lens

Now, the way that the lens works is that it has a large focal plane and it does very well at reducing optical aberrations over that focal plane. The reason that it can do that is that there are two sets of optical elements in the lens that work together to achieve the result.

Are you starting to see the analogy yet?

OK, so in the web world, we have a large focal plane. Lots and lots of potential users, all over the place, not with much focus, maybe casual browsers. The first task of the lens and your site is to get those users to engage with your app. That would include stuff like clicking on buy buttons or signing up to use a service or something. Great! We have achieved in our app, what the first set of optical elemnts in the lens does.

The next step is to focus the different types of users together, create community and provide interest to retain them. This is the job of the second phase web design. What is going to hold your users' attention? What will keep them loyal? What will get them to keep coming back for more?

If you can figure that out, you have a winning combo. You have a Double Gauss Web Application Design. Sounds much better than "Dude, my site rocks" eh?

Anyway, this is a short summary of a piece that I am thinking about for an upcoming conference. I am not sure if I will use it or not, but wanted to keep track of it somewhere (here).

Let me know what you think!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_39768_1296663797</link>
            <description><![CDATA[I recently reported on Twitter that almost all of the sites that I am running Google Analytics on (about 10 in total) have experienced major increases in traffic and content indexing. This statement piqued the interest of @rafiq and he asked me why the increases, and I promised to have a look.

After a small investigation, it turns out that there is one common factor that all of the sites share, that is a good Semantic Web presence.

From previous experience, I have stated that the major search sites are indexing in the following order:

1. Semantic web machine readable data
2. RSS
3. XML based sitemaps etc
4. Content (HTML etc)

so from the above, it is pretty easy to determine which area to concentrate on as an SEO person or even if you simply want to better your search engine rankings. (A good way to check your site for the index that, say, Google is indexing is to do a search for site:yoursite.com, so for example, I would do a search for site:www.paulscott.za.net

If you see lots of results on different pieces of content, you are OK. If not, read on, carefully.:)

OK, so we can get to some low hanging fruit first. 

1. Make sure that you provide a readable (compliant) RSS feed and ATOM feed. Bots prefer ATOM to RSS for some reason, but do ensure that you provide at least one, if not both. 
2. Make sure that your markup is OK. Check your HTML for errors and warnings and fix as many as possible
3. Do the stuff that Google Webmaster Tools and Analytics tell you to do. They are trying to help. OK they are trying to sell more stuff through their ad networks, and better sites all round help in that, but for the intents of this piece, they are trying to help you. Listen and do it!
4. Once all of that is in place, you will probably be ready for the next step - the semantic web.

Disclaimer: The semantic web is NOT a SEO tool, although it works as one. Please read up about what the Semantic web is [b]really[/b] about sometime soon too

OK so, we will concentrate on 2 different ontologies that I use regularly for sites that I work on. Content sites and sites that exist to sell stuff. Lets start with content sites. Content is just that, content. In most Web2.0+ sites, you should be getting quite a bit of user contributed content, for example user forums, blogs, comments and wikis etc. All that content can be hidden behind logins and other things that would normally render it unreachable to most bots. This is not always a good thing. Obvioulsy if you have content that needs to be protected, then don't expose it, but for the most part, if you have a help forum or something, you want maximum exposure. You also want exposure if you are running ad campaigns...

One of the best ways that I have found so far of exposing this type of content is through the SIOC ontology - http://sioc-project.org/ 
This ontology caters well for comments and community based content, but works equally well for regular "flat" content too (like found in a CMS or something).

Next up are the sites that sell, or want to sell, stuff. The best approach for this is to take a long read through the Goodrelations Ontology. You only really need a small subset of the ontology to succeed, but check out the full spec anyway. Find their stuff at http://purl.org/goodrelations/v1 If the page is busted (as it sometimes is) check out http://www.heppnetz.de/projects/goodrelations/

Basically what this thing can do for you is highlight products and services that you are flogging to search engines. If you get really into it, you can even get ratings for your products displayed as well, which are really useful. This will almost automagically make full use of the summary bits on result pages! W00t!

So, pretty easy eh?

(If you would me to help you out in doing this stuff for your site, I come pretty cheap)]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_31369_1295880467</link>
            <description><![CDATA[Some more high(er) speed photography

Not quite achievable on the sound trigger from the previous post, but pretty awesome nevertheless:

[YOUTUBE]http://www.youtube.com/watch?v=XIayrDbVdJ0&feature=player_embedded[/YOUTUBE]

[YOUTUBE]http://www.youtube.com/watch?v=QfDoQwIAaXg[/YOUTUBE]

[YOUTUBE]http://www.youtube.com/watch?v=AUSo6ehRCHI[/YOUTUBE]

Source: http://www.techeblog.com/index.php/tech-gadget/photographers-capture-incredible-images-of-bullets-being-fired]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_55606_1295782687</link>
            <description><![CDATA[How to make a sound trigger for your camera with Ubuntu

So I was interested in taking some high speed photos, but the available tutorials all called either for a very expensive piece of equipment called a sound trigger, or else to build one from a bunch of tricky to get electronic parts and a soldering iron, none of which particularly appealed to me.

This, of course, got me thinking. Why can't I code a sound trigger? The electronics all already exist in my laptop, and I know that you can do tethered photography on Ubuntu with gPhoto2 (in Ubuntu), so I started poking around.

I started with a bit of Perl, mainly because I knew of a module called Audio::DSP that I thought would do the job. A little bit of code later and I thought that there had to be an easier way, so I started a quick look around on http://www.sourceforge.net and found a little bit of Python (hell yeah!) that would do the job.

The code is called AudioTrigger and they provide a handy .deb even to get things started nice and quickly. Download it at http://sourceforge.net/projects/audiotrigger/

Once you have installed audiotrigger, launch it by going to the Applications menu -> Other -> audiotrigger. Now, audiotrigger launches as a PyGTK app in the taskbar thingy, so I almost thought that it had failed until I spotted the little icon. Much to my joy, right clicking on the icon brings up a context menu with profiles and a start/stop and quit item. I clicked on profiles and in the "Launch" text area, I put in /home/paul/Desktop/photography/strigger.sh I also tweaked the audio settings on my built in laptop mic so that it was nice and sensitive.

In the file /home/paul/Desktop/photography/strigger.sh I simply put a line to fire off gPhoto2 and save a pic to my desktop.

[code]
#!/bin/bash
gphoto2 --set-config capture=on --frames=1 --interval=0 --capture-image-and-download --filename "/home/paul/Desktop/photography/caps/%Y%m%d%H%M%S.jpg"
[/code]

which I then chmodded to read/write/execute and saved.

I then started the audiotrigger "listener" (yes, haha...) and hoped for the best. It worked. Awesome. 

So for a couple minutes of code, I saved a couple thousand bucks worth of equipment. How sweet is that? ;)

P.S gPhoto2 works with most, but not all cameras. I have a Nikon D90, but it should work with most well know DSLR's. Check the gPhoto2 site for configs for your device. ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_14449_1295677418</link>
            <description><![CDATA[Group buying platform(s)

So the new big thing is group buying online. This is not a new idea. Not by a longshot. How does this impact us here in Africa? I am not sure yet, but time, as usual, will tell I suppose.

I would like to say that around 8 years ago, I wrote a group buying system that I canned, simply because of the lack of decent eCommerce in South Africa. The basic idea was to allow people to bid on an item, say sugar, bread or maize meal by pledging to buy a couple bags of it. When enough people had pledged for an item, a palette or a container of the stuff would be bought and the cheaper prices gained from massive bulk buying could be distributed to all. The system was called SwarmBuy. The source code is GPL licensed, but I have no idea where it is right now (hopefully backed up somewhere, but unsure of that too)

I still think that this is a pretty decent idea, and now that mobile payment solutions are in place in a few African countries, it could work. Now that the time is right(er), large internationals like Groupon and Google are also competing in this space, and there is little chance that local consumers will benefit from large scale buying/selling. We must be cogniscent that the profits from these types of schemes [b]leave Africa[/b] which is not a good thing, ever.

What does that say? We in Africa need to think about what we need and when, not always follow the crowd. We can do this stuff easily enough. These kinds of systems are trivial to roll out, but the marketing and consumer education is a perennial issue. How do we start addressing that? ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_18023_1295609390</link>
            <description><![CDATA[Using mobile phones and data mashups for sustainable agriculture

I wrote an article about this a few years back, but would like to just put down some thoughts here too. @andyhadfield asked on twitter about ideas for sustainable development in rural areas in Africa using mobile/web/tech and reminded me of a set of ideas that I had.

1. Use mobile data to get remote sensing reports to subsistence farmers. Remote sensing data coupled with some simple GIS systems could easily be used to help subsistence farmers make the best use of their lands. With accurate data on things like chlorophyll flourescence, farmers can be informed of where to fertilise, irrigate etc without wastage. Chlorophyll flourescence can be used to quickly determine the overall health of a plant, and preventative measures can be quickly taken to rectify any problems. Data can be made available quite easily through simple text messages or in a more realtime scenario by XMPP push to the devices or kiosk internet stations.

2. Agriculture eLearning - eLearning systems (like the Chisimba eLearning system) can be used to provide basic HOWTO's and techniques shared between farmers and experts from around the world. This is especially important for non-farmers like school feeding schemes, that could benefit from learning on their mobile phone how to grow vegetables for their communities etc.

3. eCommerce - I have posted quite a few examples of mobile based eCommerce, and again, it applies. You could quite easily use a phone based system to create marketplaces for goods like fruit and vegetables, as well as crafts and curios made locally. Payment can be made by mobile payment solutions or by a bartering system where goods are assigned virtual currency values for fairness.

4. Use local communities to build infrastructure - This was a result of another suggestion by @dazMSmith, but the mobile phone companies and others could contract locals to build cell towers and other infrastructure to boost local economies. Once connectivity is in place, more can happen (see above)

5. Deploy wireless networks with backhauls - using very cheap hardware, it is easy enough to deploy wireless zones over large areas of agricultural land. The advantage is that most will be line of sight, which makes for cheap and easy to build/maintain networks. IP phones, eLearning and eCommerce would follow quickly.

There you go. 5 simple ideas that could make a huge impact. All mobile, all do-able with minimal resources and effort. So what is stopping you?]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_78024_1293097405</link>
            <description><![CDATA[How to embed a Creative Commons license directly into the EXIF data on a Nikon D90 (and probably others)

I license all of my photographs under a Creative Commons CC BY-SA license (By Attribution ShareAlike) and found a pretty nifty way to ensure that your license is preserved in the EXIF data foreve. What this does is ensure that the license follows your images, even when people forget to put in explicit Attribution (this has happened to me a few times).

So the process is as follows (I have a Nikon D90, but I am pretty sure it should be similar for whatever else you may have):

1. Turn on the camera
2. Press the Menu button on the left to access the cameras menus
3. Go to the "Setup" menu (the one that looks like a little spanner)
4. Select Image Comment
5. Press the OK button
6. Select "Input Comment" and set that comment string to CC BY-SA Your Name
7. Press OK
8. Make sure that the "Attach Comment" checkbox is set to on
9. Move up to "Done" and press OK.

Sorted! Now, in the comments section of the EXIF data, you will have a "CC BY-SA Your Name" on every single shot you take - automatically! You can, of course choose your own license, comment or whatever else, but this is a really nice way to ensure that your name stays with your data!

Enjoy and happy Creative Commons licensed snapping!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_9115_1292686540</link>
            <description><![CDATA[PHP GD and Ubuntu

It seems that the maintainers over at Ubuntu regard the PHP5 bundled GD version a fork of Boutells GD library (I understand why) and that forks = security risks (again, the logic is there). However, I am working on an application that requires a higher level of GD functions than the "older" GD library provides, chief amongst them the imageantialias() function (amongst others).

The only recourse was to recompile PHP, build a php5-gd package from that and then replace the current php5-gd deb with the new one. Luckily if you link against the standard system GD library (not the PHP shared lib) you get the full deal!

Sounds simple enough, and so it is! Here are the steps required:

1. Completely remove your old php5-gd with an: 
[code]apt-get remove --purge php5-gd[/code]

2. You then need a fakeroot and build environment (if you don't already have one) so install that with the following:
[code]# Install build tools, debian helpers and fakeroot
    apt-get install build-essential debhelper fakeroot
    # source code should reside in /usr/src
    cd /usr/src
    # Download PHP source
    apt-get source php5
    # Install all packages required to build PHP5
    apt-get build-dep php5
    cd php5-5.3.2
[/code]

3. Next up we have to modify the debian rules as to how the package is compiled. All that you need to do here is to open up the debian/rules file in your favourite text editor and find the line that looks like:
[code]--with-gd=shared,/usr --enable-gd-native-ttf \[/code]
and replace it with:
[code]--with-gd=shared --enable-gd-native-ttf \[/code]

4. You can then go ahead and rebuild the deb with the following:
[code]# build the php5-* packages
    dpkg-buildpackage -rfakeroot
    cd ..
    # Install the new php5-gd package
    dpkg -i php5-gd_5.3.2-1ubuntu4.5_i386.deb 
[/code]

5. Restart apache with [code]/etc/init.d/apache2 force-reload[/code] and you should be smiling!

That's it. Told you it would be simple!


  ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_21214_1288366442</link>
            <description><![CDATA[The MXit API

So today I attended the launch of the MXit API. I was pretty excited about this one from the hype surrounding it mainly because there was a lot of talk of MXit finally "opening up" and creating an open API for anyone to use. Turns out that this is not really the case at all.

Firstly, as the launch event started, the audience was asked not to tweet anything without running it by the MXit folks first (you know in case they have to deal with more bad press), at which point I should have left, but didn't.

The "API" as it turns out, is really an SDK. A Microsoft .Net C# SDK at that. There is no API (well not counting the standard XMPP "API" which we have been using for years anyway) at all, but a Windows DLL that contains an interface to the MXit currency functions (they call it Moola) and some basic messaging options. They have also introduced a simple markup language that allows some simple graphical elements to be cached client side for performance. The markup would be really useful IMO, but that seems to be secondary to MXit themselves.

Essentially the DLL is only useful to Microsoft Visual Studio coders as it uses the Net.TCP function to do the bidirectional API calls. That basically means that you cannot even hack this thing onto Mono and still use a Free Software stack to run your apps. There is a WCF service that runs on top of the API to handle the requests to handle the HTTP requests from client apps. 

This could all have been achieved in a much easier way by sticking to pure old XMPP plus a basic API to handle some non-standard XMPP stanzas for the transactions etc. I do not know why they approached it in this way at all.

Basically the MXit API is Yet Another Walled Garden (YAWG) that I do not need to think about again.

For those that are interested in doing something with the "API" the approach would be:

1. They are very interested in games, so make a simple text based multiplayer game.
2. Get in real early. I suspect that the apps will quickly become saturated and lose novelty fast, so start coding immediately if you think that you may want to make money from this thing
3. Be aware that your revenue stream is based on MXit Moola, which means a Premium Rated SMS service via MXit. That means that for every Rand you charge your clients, you will get 50c

One vector that I can think of for one person to make an absolute fortune from this would be to buy the infrastructure and software and create an Open API around it for 3rd party devs to use. You will essentially become the middle middleman and create value for everyone in the world that does NOT want to fiddle with Windows DLL's and such. You could then charge a small transaction fee on that service.

Overall, I was underwhelmed, but lunch was good, so thanks to MXit for that! We also got a goody bag(let) full of stickers and a cap, so I am sure my kids will have fun with those.

]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_65953_1288278977</link>
            <description><![CDATA[Afrihost and why they rock as an ISP

OK, so you really will not see this often (read ever) on this site, but the recent activities at Afrihost http://www.afrihost.com warrant this.

I have been hosting sites with Afrihost for a few years, and decided earlier this year to move my ADSL account over to them as well. This was probably the best move I have ever made. There were a couple of issues in the beginning with respect to SEACOM cable connectivity problems, but Afrihost hacked around that by providing a proxy server onto the SAT3 cable. Other than that, I have nothing but good things to say about them.

1. Bandwidth is cheap. Really cheap. Our bandwidth bill for our home ADSL has shrunk to such a degree that I was able to upgrade our line to a 4mbps line. That is awesome.
2. We use a lot of bandwidth, mainly on the upstream and through their web based "Client Zone" we are able to top up our capped account anytime, from anywhere. With at least one photographer in the house at any one time, we tend to use a few GB a month uploading and this really helps. They do take care of the minority "mainly uploader" group too, which very few ISP's do in SA.
3. They have a 2 for 1 top up special at certain times. OK, scratch that, most times. We pay very little for a very good return with Afrihost
4. Service is awesome. The head honcho at @Afrihost on twitter tweets and answers queries too (@gianvisser) which I respect.
5. They just ran a 1GB for life free promo. I love that, because no matter what happens now, I will always be online at least!

Just a quick anecdote of a query that did come up.

I was experiencing slightly lower line speeds than I should have and tweeted about it. Afrihost picked up that tweet (without me @messaging them or even following them at the time) and had a technician PHONE me on my mobile phone to sort out the issue. The dude was knowledgeable and had respect for the fact that I knew what I was talking about instead of trying to shove a pre-recorded call centre script down my throat (you know the old "OK click on the Start button"... "I use linux"...."Ja, OK, now click on the start button..." routine) and helped me out in a few minutes. In a country where very few companies really take service seriously, this needs mentioning!

To end, I would like to say a big thank you to Afrihost for caring enough that my ISP is an important relationship to me. 

If you do use twitter, please follow:

@gianvisser
@Afrihost

to get a really excellent service and great prices.]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_8330_1287759459</link>
            <description><![CDATA[D-Link Wireless USB device on Ubuntu 10.10 Maverick

I needed a wireless NIC for a desktop and decided to buy the D-Link DWA-125 USB wireless adaptor. I chose it because it was really cheap and a USB device meant that I didn't need to go poking around the guts of the computer, which at this stage of my life I appreciate.

This thing does NOT just work out of the box, but a few simple commands had me up and running in minutes.

First thing to do is plug it in. It comes with a goofy little docking station, so I used that too. If you go and take a look at the output from lsusb you will see a line like:

Bus 001 Device 003: ID 07d1:3c16 D-Link System

The ID bit is important, so note that down.

Next you need to get the thing working with a driver. The generic RealTek (rtxxx) drivers generally work, so I tried that first.

sudo modprobe -rf ndiswrapper
echo 'install rt2870sta modprobe --ignore-install rt2870sta ; /bin/echo "07D1 3C16" > /sys/bus/usb/drivers/rt2870/new_id' | sudo tee /etc/modprobe.d/rt2870sta.conf
sudo modprobe rt2870sta
dmesg | egrep 'rt28|usb|Phy'

The dmesg output should tell you that it is installed and ready to rock 'n roll

Next check that it is alive:

iwconfig
sudo iwlist scan

It should find some friendly neighbourhood wifi networks with that.

Now, that is all fine and well, but you need to do that every time you reboot, which is a bit of a pain, so we add in a Udev rule to start it every time:

sudo gedit /etc/udev/rules.d/10-wlan.rules

Insert the following code and save the file

# UDEV-Rule for D-Link DWA-125 ID 07D1:3C16
SUBSYSTEM=="usb", SYSFS{idVendor}=="07d1", SYSFS{idProduct}=="3c16", RUN+="/sbin/modprobe rt2870sta"

At this point I restarted, but you can also do a sudo service udev reload

Have fun!]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_8736_1287139172</link>
            <description><![CDATA[Exclusives and the DRM Windows Fail

So @cazpi ordered a book from @exclus1ves (Exclusive Books) online through their web site. She happened to choose the PDF version of the book due to the easier (it seemed at the time) way to get reading as soon as possible, as well as the convenience of just being able to download the book.

That was a mistake.

She eventually managed to download the "book" which turned out to be an .acsm document (which on opening, turns out to be a bit of metadata including some DC information about the actual book). This was after paying for the book via an EFT (she doesn't use a credit card). We managed to download the actual PDF by grabbing the URL referred to in the metadata file, but on attempting to open the file, were confronted with a password request. This I understand to cut down on piracy etc, but that is where the real fun started.

She then phoned their support folks, who were extremely helpful in telling us that we need Microsoft Windows and Adobe Digital Edition reader. We gently tried to explain that we use Ubuntu GNU/Linux and do not use any proprietary software. This was not very well understood. We then asked if there was just a password that we could use to unlock the document. Answer was no, that is how they get them from the supplier, and an additional request was made for us to buy Microsoft Windows. We then (now slightly irritated) explained that we do not have R6000 odd to spend on Windows, and that we use Ubuntu.

At the end of a somewhat tedious conversation and me having to explain that they need to put a giant banner on their web site that says that they require their customers to use Windows and that their books actually cost the price of the book PLUS the cost of Windows, they said that they were in the process of putting up such a banner.

That is just so broken and wrong, I am actually dumbfounded, flabbergasted and completely stymied. 

Maybe it is just me? Does this seem right to you? ]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
        <item>
            <title>pscott209@gmail.com</title>
            <link>http://www.paulscott.za.net/index.php?module=jabberblog&amp;amp;action=viewsingle&amp;amp;postid=ps123_14937_1287118521</link>
            <description><![CDATA[Follow Friday post

I don't often do a Follow Friday post, and when I do, I prefer to use the built in Chisimba @anywhere functionality to give myself a little more breathing room than the standard 140 chars, so here it is:

@hennokruger - good chats, good geek
@charlvn - one of the biggest geeks I know
@diekloon - WordPress and blogging geek
@dkeats - Chisimba geek and one of the most intelligent people I know
@cazpi - Photo geek and mom
@byronrode - photo and design geek. Also a genuinely awesome dude
@thebrianmanley - Semweb geek, thinker, great fun
@sznq - SEM and SEO, becoming geekier
@rubygold - all round geek, news geek.
@wesleylynch - head geek at RealmDigital
@weblearning - eLearning geek
@corrie206 - super geek
@HilzFuld - marketing geek, fanboi
@sbestbier - beer drinking geek and undergeek to @wesleylynch
@exmi - Blogging geek that helped test MXitPress on WordPress
@jdtangney - Old school uber geek (greybeard and legend)
@arcainus - BOFH geek
@ramereth - BOFH uber geek
@sheltren - OSUOSL geek
@gchaix - eLearning geek]]></description>
            <author>admin</author>
            <pubDate>Thu, 01 Jan 1970 00:00:00 +0200</pubDate>
        </item>
    </channel>
</rss>
