Wednesday, September 28, 2011

Freelance software development

This article discusses what you should do if you want to work as a freelance software developer. In my view it is becoming more and more easy to get freelance work specially in the field of software, online marketing and content writing. I am working as a freelance developer for more then 4 years. I am working full time from home and I am earning as much as a full time employee of the same experience level will earn from a reputable organization. My advantage is that I have flexible schedule. I work from 8 a.m to 2 p.m, and leave my desk when the kids arrive home after school, I spend time with them and, take rest in the afternoon and then work for 2 hours in the evening when they are playing, finally I start my work at 8 p.m and keep working till my husband comes home. This is why I love my job, I can have good time with my family.


Now I would like to discuss how one should start bidding, winning and completing the project.

1) Find and create a good freelancing site. I have worked at elance.com, vWorker.com and oDesk.com. I have found all of them to be really good. I started as an individual and created a free account. A free account is better as you will not have to invest anything. You will start earning without investing a penny.

2) Find the areas of your expertise. Completing a project is more difficult then winning a project. I have seen many developers who just wanted to grab a project. They had lots of ideas of getting the project done, like forward the project to someone who has used the required technology, find juniors to implement the project etc. But if you are not a champion in the technology that is required by the project them most probably you will fail and you will get a bad rating. A bad rating means no more work. So first of all find your area of expertise, it can be anything related to software development like java, .net, quality assurance, testing, project management, php or requirements gathering etc. You will find more projects for some of the fields like php, jquery and SEO etc.

3) Start searching the project and start making your bids. Before placing the bid make sure that you understand the project completely and you can make an estimate on the time and cost. Here are my tips for making bids

  • Don't bid too small or you will loose interest in the project after winning and start working on the project.
  • Don't put too high amount otherwise you will most probably not be awarded with the project.
  • Multiply your time estimate with 2 or 3. All clients want perfect work, to make them happy you need to invest more time. Mostly our time estimates are not correct as we make estimate on the basis of what we see. But when its about implementing the things we have to be careful about many things which take time.
  • With your bid explain your understanding about the project.
  • Write a small description of how you will implement the project. Don't write everything.
  • Try to ask questions to open a communication channel with the client. Don't ask unnecessary question it will simply make the client annoyed.
  • Inform the client about your availability and about previous experiences of the same type of project.
  • If the client has provided a list of technologies you should know and you don't know some of them, inform him about them too. In case the client chooses you for the project, he would know that there will be learning curve for those technologies.
 3) Don't get dishearten if you placed bids on many project and didn't won even a single project. Try to find out why others are being awarded, try to find out a patterns in your failures, like did the accepted bid had very low/very high amount then your bid, did the accepted bidder was a successful coder at the site etc.

4) After you win a bid make sure that the client had escrow the funds at the site and make sure the terms are good for to start working.

5) I think, I don't need to say that you should work very hard to make the project a success. Deliver exactly what the client wants. Don't try to add extra functionality you think is good, but you are not being paid for it. Think about the time and money the client is investing on the project, keep yourself at the place of client and think what you would the coder to do.

6) Keep in touch with the client and inform him about your progress. Try the understand what type of communication your client wants. If he doesn't want to get bothered about everything, then may be a weekly report will be suitable for him. Try to be consistent, and overcome the difficulties that would arise during your development. Remember "Google" as a best friend of all developers.

7) After finishing the project, ask the client to review and don't mark it as 100% complete until he completes his review or the deadline has arrived. Ideally the project should be marked as 100% complete before or on the deadline.

8) The client will review your work and release payment after full satisfaction. Allow atleast one week to the client to review your work. Don't get impatient if the client takes time in reviewing and releasing payment. Remind him to review your work after 4-5 days.

9) After the client releases your payment you can choose any method to withdraw funds which are supported by your freelancing site. Payoneer is a famous withdrawl method these days which costs very low, other methods are worldpay and bank to bank wire transfer. Request the client to leave a feedback score for your work if he doesn't do so after releasing the payment.

10) Last but not least, remember me in your prayers while enjoying the money!

Tuesday, September 27, 2011

Support GWTCanvas in IE7 and 8

HTML5 is out for quite a long time now. The most popular thing is, I think, Canvas. It is being used for drawing, image editing, charting, games, intros and many more things.

I had created an image editor for a client who wanted me to make it work at internet explorer 7 and 8 also. As internet explorer doesn't support html5 so I had to use excanvas to make the application work in IE7 and 8. It was a tough job and I was annoyed by the client's insist on these version of internet explorer. I was like, who is going to use such old versions of a browser when we have a lots of choices plus chorme and FireFox are getting more and more popular. After I finished the application and made it work on all major browsers (including internet explorer 7 and 8) I got another project in which client wanted to make his GWTCanvas application run at IE7 and 8. I was again shocked why people are so serious about these old browsers.

In those days my computer had become very slow, I had tried many tricks but nothing worked. So I had to restore my operating system factory state. After I restored the initial installation (three years old) of windows vista I had internet explorer running on my computer. I was ready to upgrade it to internet explorer 9. But when I tried to do so I got to know that I had to install many service packs. I started downloading the service packs. There were lots of problems while trying to apply service packs and update the operating system. So I had IE7 on my computer for many days. Now I know why clients insist on supporting these old browsers.

Now lets talk about how we can make GWTCanvas run on the IE7 and 8. Because old browsers don't have Html5 support so GWT guys has not supported it their implementation also. If you try to simply add excanvas.js in your html file, it will not work. This is because that GWT canvas module has following lines in their .gwt.xml file.


Due to the above lines when you will try to run following line you will not get the canvas element in you application.

canvas = Canvas.createIfSupported();
 
GWT knows that the user is running the application in internet explorer 7/8 so the canvas support is not present. To overcome this problem you will have to override the "canvasElementSupport" property by adding following lines in your GWTExCanvas module



Notice that previously the value of canvasElementSupport was "no" and now it is "maybe". Including these lines will make the canvas available in internet explorer 7/8 too. But the canvas will still not get created. This is because the GWT's implementation of DOM doesn't have a way to create html element for Canvas. To add this support we will have to subclass the com.google.gwt.dom.client.DOMImplIE8 so that we can add the method createElement for html canvas element. We will have to put our class in same package as DOMImplIE8. Following is my implementation of this class.

package com.google.gwt.dom.client;

public class MyDOMImplIE8 extends DOMImplIE8 {

     @Override
    public Element createElement(Document doc, String tagName) {

        Element elem = super.createElement(doc, tagName);
        if (tagName.equalsIgnoreCase("canvas")) {
            elem = createExCanvas();
        }
        return elem;
    }

    private native Element createExCanvas()/*-{
        var el = $doc.createElement('canvas');
        el.setAttribute("width",300);
        el.setAttribute("height",150);
        el = G_vmlCanvasManager.initElement(el);
        return el;                                                        
    }-*/;


Here G_vmlCanvasManager.initElement is to initiate excanvas.

Also add following lines in your .gwt.xml file.


Now the last point is where do we add the excanvas.js. You can let the users of our GWTExCanvas module add excanvas.js in their html file or include it in your own java code. I preferred the second option. I created a JSNI method and pasted the excanvas file in this method. The method was called in a static block inside the above class.

 static {
         initExCanvas();
     }


    private native static void initExCanvas()/*-{
// CONTENTS OF EXCANVAS.JS HERE
}-*/;

Now my own GWTExCanvas module was ready. I just have to add it in my application and use the standard GWTCanvas code. If you have already developed your application and now you want to add IE7/8 support, this solution is ideal as all you have to do is to inherit this module. Make sure to inherit it after inheriting GWT in your .gwt.xml file.

Thanks for reading.

Thursday, November 4, 2010

Html5 Canvas Drawing -- Draw dotted or dashed line





This post is for those who want to use html5 canvas for drawing. Canvas have methods to draw lines of different widths but not for different styles. Like if you want to draw dotted lines or dashed line there is no such stroke style. But fortunately there is a way to achieve this. Following is a description about how I achieved this.

You can set the stroke pattern on canvas context. Stroke pattern can be any canvas pattern. So here I created an image of 1 pixel height and 6 pixel width. First three pixels were black and other three were white. Now I created the image to create a repeating pattern.

       var linePattern;
       imageToUsedAsPattern.onload = function() {
               linePattern = context.createPattern(
imageToUsedAsPattern, "repeat");
               context.strokeStyle=linePattern;
        }
        var imageToUsedAsPattern = new Image();

        imageToUsedAsPattern.src = "images/linePatterns.jpg";    

Now all the calls to context.stroke will use the pattern to draw strokes. Like if you create a line from the top left corner of the canvas to the bottom right corner it will be a dashed line.

         context.moveTo(0,0);
         context.lineTo(canvas.width,canvas.height);
         context.stroke();


You can achieve dotted line in similar way by creating an image may be two pixel wide. First pixel of white color and second of black color.


A limitation of this is that you can create lines of only white and black color or only of those colors for which you have already created the images. To provide lines of any color you may create another image on the fly using canvas element and doing pixel mainpulation.

Wednesday, March 31, 2010

GWT-EXT Read nested JSON object

Here is my JSON.

{
 "pk": 9990,
"title": "World Event",
"description": "Description of World Event",
"idea": {
     "ideafk": "9840",
      "ideastring": "World"
           },
 "streetAddress": "World Event's street ad"
 }

You can see it is has nested attributes. The attribute idea has sub-attributes ideafk and ideastring. I wanted to read the value of ideastring in a column and wanted to keep the value of ideafk in the recordDef for further references. Following is the solution.

final RecordDef recordDef = new RecordDef(new FieldDef[] {
new IntegerFieldDef("pk"),
new StringFieldDef("idea", "idea.ideaString"),  // here you name ideaString as idea in recordDef.
new StringFieldDef("title"),
new StringFieldDef("ideapk", "idea.ideapk"),

ColumnConfig ideaStr = new ColumnConfig("Idea", "idea", 45, true);  // In recordDef ideaString was called idea.
ColumnConfig eventTitle = new ColumnConfig("Title", "title", 45, true);

Sunday, January 10, 2010

Handling Simile Timeline in GWT application.

During a project I had to use Simile Timeline with GXT. I handled mouse wheel zoomin, scrolling, event click handling, dynamically loading events as timeline scrolls etc in GWT-EXT application. I was difficult task and took lot of time to handle these things. Here I am pasting my code for some of the solutions.

How did I change the font size
timeline.css

.timeline-container {
position: relative;
overflow: hidden;
font-size:10;
font-color:red;
}

Added these two lines
font-size:10;
font-color:red;

This style is applied to the div element in which timeline is rendered.


How did I cleare the timeline events to re-render the events on prevously rendered dates.
$wnd.Timeline.timelines[0].getBand(0).getEventSource().clear();

If you want to clear band number 2 the it becomes
$wnd.Timeline.timelines[0].getBand(1).getEventSource().clear();

Handling scroll events

public native static void addScrollListener(int index,
CustomTimeLine timeLine)/*-{
timeLine.getBand(index).addOnScrollListener($wnd.bandScroll);
}-*/;

public static void bandScroll(double startDate, double endDate) {

MessageBox.alert("scroll Event recieved");

}



Handling zoom events

import com.google.gwt.core.client.JavaScriptObject;

public class ZoomStepOptions extends JavaScriptObject {
protected ZoomStepOptions() {
super();
}

public static ZoomStepOptions create() {
return ZoomStepOptionsImpl.create();
}

/**
* required, start date
*
* @param value
*/
public final void setPixelsPerInterval(int value) {
JavaScriptObjectHelper.setAttribute(this, "pixelsPerInterval", value);
}

/**
* required, Unit
*
* @param value
*/
// value =
// Timeline.DateTime.HOUR,Timeline.DateTime.DAY,Timeline.DateTime.MONTH
public final void setUnit(int value) {
JavaScriptObjectHelper.setAttribute(this, "unit", value);
}

}


class ZoomStepOptionsImpl {
public static native ZoomStepOptions create()/*-{
return new Object;
}-*/;
}


import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;

class JavaScriptObjectHelper {

private JavaScriptObjectHelper() {

}

public static native String getAttribute(JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : String(ret);
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
String value) /*-{
elem[attr] = value;
}-*/;

public static native JavaScriptObject getAttributeAsJavaScriptObject(
JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native JavaScriptObject[] getAttributeAsJavaScriptObjectArray(
JavaScriptObject elem, String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
JavaScriptObject[] value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
JavaScriptObject value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
int value) /*-{
elem[attr] = value;
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
boolean value) /*-{
elem[attr] = value;
}-*/;

public static native void setDateAttribute(JavaScriptObject elem,
String attr, String value) /*-{
var dateValue=new Date(value);
elem[attr] = dateValue;
alert("dateValue="+dateValue);
alert(elem.id + " " + elem.start);
}-*/;

public static native void setAttribute(JavaScriptObject elem, String attr,
float value) /*-{
elem[attr] = value;
}-*/;

public static native int getAttributeAsInt(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static native float getAttributeAsFloat(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

public static int[] getAttributeAsIntArray(JavaScriptObject elem,
String attr) {
int[] rtn = null;
JavaScriptObject hold = getAttributeAsJavaScriptObject(elem, attr);
if (hold != null) {
rtn = new int[getJavaScriptObjectArraySize(hold)];

for (int i = 0; i < rtn.length; i++) {
rtn[i] = getIntValueFromJavaScriptObjectArray(hold, i);
}
}

return rtn;
}

public static native int getJavaScriptObjectArraySize(JavaScriptObject elem) /*-{
if (elem) return elem.length;
return 0;
}-*/;

public static native int getIntValueFromJavaScriptObjectArray(
JavaScriptObject elem, int i) /*-{
return elem[i];
}-*/;

public static native void setAttributeAsIntArray(JavaScriptObject elem,
String attr, int[] value) /*-{
elem[attr] = value;
}-*/;

public static native boolean getAttributeAsBoolean(JavaScriptObject elem,
String attr) /*-{
var ret = elem[attr];
return (ret === undefined) ? null : ret;
}-*/;

/**
* Helper function to create [] array from List.
*
* @param list
*
* @return array of objects
*/
public static JavaScriptObject[] listToArray(List list) {
JavaScriptObject[] array = new JavaScriptObject[list.size()];

for (int i = 0; i < array.length; i++) {
array[i] = (JavaScriptObject) list.get(i);
}

return array;
}

public static JavaScriptObject arrayConvert(Object[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i < array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}

public static JavaScriptObject arrayConvert(JavaScriptObject[] array) {
JavaScriptObject result = newJSArray(array.length);
for (int i = 0; i < array.length; i++) {
arraySet(result, i, array[i]);
}
return result;
}

private static native JavaScriptObject newJSArray(int length) /*-{
if (length < 0)
{
return new Array();
}
else
{
return new Array(length);
}
}-*/;

public static native int arrayLength(JavaScriptObject array) /*-{
return array.length;
}-*/;

public static native Object arrayGetObject(JavaScriptObject array, int index) /*-{
return array[index];
}-*/;

public static native void arraySet(JavaScriptObject array, int index,
Object value) /*-{
array[index] = value;
}-*/;

public static native void arraySet(JavaScriptObject array, int index,
JavaScriptObject value) /*-{
array[index] = value;
}-*/;
}

Friday, December 25, 2009

How to get freelance work

During 3 years of successful freelance work, I have several times been asked by friends and family members "how do you get work" or "From where do you get work". This blog post is to answer these questions. I am going to update this post soon so stay following me.

Ok so here I am to tell you my secrets of getting work online. First and foremost thing I want to tell you that there are many fields in which you can find freelance work. Here I am listing few fields for which I saw posts on freelancing sites.

1) Teaching
2) Writing
3) Software development
4) Data Entry
5) Website management
6) Software testing and QA.
7) Palmist
8) Psychiatrist
9) Medicine

I am sure you can add many more fields if you search it on the internet. My experiences are related to software development but the key points I am discussing here will apply to the other fields as well.

Now the question is "how to get the work of your interest"? Well there are thousands and thousand of sites offering you to start your online business. Like for software development famous sites are rentacoder.com, elance.com, scriptlance.com and odesk.com. I have worked with rentacoder and elance. Both are robust but I was able to make good profit at elance because of their low fees. For teaching there are tutor.com and liveperson.com.

There are many sites which allow you to create a free account and start bidding for the jobs. Many sites ask you to pay a certain amount to create an account. I would recommend that you consider a free account to start. You may switch to paid service later if you get success in your business.

To create an account was the easiest part of the job. After you create your account you will be able to see the job posting and you will be able to post your comments and pricing to do that job. Reading the post is also easy task but responding to them and making bids is a difficult task. To me following points are important.

1) Read the problem carefully many times.
2) If you think that you can work on the project but you don't understand it fully, write to the buyer immediately. Because it is important to communicate on urgent basis. At rentacoder.com I found that there were messages and bid from other providers within minutes after a new project was posted.

3) Do not bid if you don't fully understand the requirements.

4) If you understand the requirements and can work on the project try to be the first or among the first few who placed the bid. Yes you will have to be very quick. You know the buyer sometimes gets tens of bids, he cannot read each bid and cannot respond to everyone.

5) Tell him the truth. I have seen providers asking you to finish work in two days while the work cannot be done in less then one week. Do tell them that you cannot deliver in 2 days.

6) Respond quickly and promptly. If you receive any query from the buyer respond quickly. Its better to keep checking your emails after other hour to make sure that you will be able to reply him quickly.

7) You may offer him demo of small part of the work he wants. My second project at rentacoder was an online file management system. After reading the requirements I made the bid and within few hours I created a short demo for that. I showed him my work and he was surprised with the quick work and immediately approved my bid.

8) Write a proposal. Its better to write proposal to make bid. What I do is that if the work is small I simply write my proposal in an email message. Like once I was given work to add one field in a form in php. I wrote following points to the client

a) I have to put the html code to add this field.
b) I have to write javascript validation of this field.
c) The is a username it needs to checked for duplicate entries, so I need to put ajax based validation from server.
d) I have to write code to handle and show error.

Above points show that I fully understand the problem and it also lists the tasks that need to be done. By doing this you are making it easy for the buyer to choose you.

If the work is more complicated then write a neat proposal. List all the modules and what you think will be done in each module. Put pricing and hours/days for each modules separately. It ensures that you understand the requirements. It is often better to multiply the estimated time with 2. But never multiply the estimated cost by any number :).

9) If you get the work start immediately. Divide the agreed upon number of days by 2 and take the result as your deadline.

This is all I do. Sometimes my project goes well beyond the deadline but my clients are satisfied at the end. Just because I do all my work with all my honesty and sincerity. If you succeed to get you project and you deliver quality work on time then it is easier to get more work.

If you are also a freelancer, I would love to hear your story. Please share your experiences with us.

Thursday, December 10, 2009

GXT grids - Binding with beans

I had to use many grids in my first GXT project. I got to know that I have to use GXT right after I sent my first release of the front end to the client. I had never used GXT, the deadline was tight. I was puzzled, should I spend time in understanding the library or just start coding with the help of google. I started coding the simpler screens, I was able to show some progress to the client in next couple of days. I turned to the grids after writing all the screens in which there were no grids. I opened their demo and tried to understand the code – I was failed. I started experimenting with the examples and tried to copy it with my classes – I got no success. I searched the internet and I found one description copied to many places by different authors. The description was not enough for me. I read it again and again and tried to implement it in my application. After many trial and errors I was successful. Following is a detailed description of problem and solution.

I had prewritten data access objects which I had to use for grid data binding. I found a good tutorial for this at gxt site at following link

http://www.extjs.com/helpcenter/index.jsp?topic=/com.extjs.gxt.help/html/tutorials/beanmodel.html

After I read the above I did following steps

1) Implement BeanModelTag in all beans. Following is an example of TimeZone bean which contains Timezone record from database.

public class TimeZone extends LightEntity implements Serializable, BeanModelTag
{
private int id;

private String name;
private String defaultValue;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getDefaultValue() {
return defaultValue;
}

public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
}

Note that it is serializable and it is marked as BeanModel by implementing BeanModelTag interface.

Next step is to fetch data from the database. My service implementation returns an ArrayList of above bean and it is as follows.


public class TimeZoneServiceImpl extends PersistentRemoteService implements
TimeZoneService {

public TimeZoneServiceImpl() {
super(HibernateUtil.getBeanManager());
}

@Override
public List getAllTimeZones() throws Exception {
Session currentSession = HibernateUtil.getSessionFactory(false)
.getCurrentSession();

List timeZones = DataCubeUtil.getTimeZoneList(currentSession);

return timeZones;
}

}

You can call this service as a simple RPC call or you can use gxt RPCProxy which handles callback on its own. Using RPCProxy is easy and straight forward it will bind the grid columns with your beans properties using ColumnModel.

final TimeZoneServiceAsync timeZoneService = GWT
.create(DeviceService.class);
RpcProxy proxy = new RpcProxy() {
@Override
public void load(Object loadConfig, AsyncCallback callback) {

timeZoneService.getAllTimeZones(callback);

}
};

Abovel lines make an RPC call and fetch data from TimeZoneService. But we have not loaded this data yet and we have not provide any grid-data binding configuration. To load data into a store we need a loader, we can use ListLoader or PagingLoader etc. I used a ListLoader in this manner

BeanModelReader reader = new BeanModelReader();
ListLoader loader = new BaseListLoader(proxy, reader);

Here reader is used to handle creating new model instances from the beans being returned from the data proxy. So at the stage when you introduce reader into your code, your actual bean is gone and at runtime the gxt libaray creates its own instance of TimeZone class.

Now I have to create a store which can store the loaded data.

ListStore store = new ListStore(loader);

So far what we have done is that we created a bean, created a proxy which brings a list of beans (filled with data), we created a reader which can read the returned beans, then we created a store and a loader, loader loads data into the store. But how it will bind the loaded data into the grid. This is done by ColumnModel.

ColumnConfig columnConfig=getColumnConfig();
ColumnModel cm = new ColumnModel(configs);

Following function creates binding by setting column id same as bean's properties. Here I would like to mention that while binding the grid column to bean's property it follows java's coding convention. Like the id of column name is "name" (column.setId("name"). While creating the binding at runtime that library find the getter method by concatenating get infront of id, in this example it would be getName(). If this getter is not found gxt will not raise any exception. It simply ignores this binding. For example if the name of getter was get_name() then your binding will not be done as expected. So be careful while creating these bindings.

private ColumnConfig getColumnConfig(){
ArrayList config=new ArrayList();

ColumnConfig column = new ColumnConfig();
column.setId("name");
column.setHeader("Name");
column.setAlignment(HorizontalAlignment.CENTER);
column.setWidth(84);
config.add(column);

ColumnConfig column = new ColumnConfig();
column.setId("defaultValue");
column.setHeader("Default Value");
column.setAlignment(HorizontalAlignment.CENTER);
column.setWidth(84);
config.add(column);


}

After you make the binding your grid is ready to go.

To me gxt grids with their store API is robust solution. You can create grids with checkboxes, with multiple headers, with custome styles for each column data etc. They saved me a lot of time and efforts.