def import_contacts(request):
    """
    This view is called in a by the frontend app in oder to import
    contacts.
    
    It receives the following parameters via POST:
      - username: the freshbooks username
      - token: the freshbooks token
      - contacts: the list of id of freshbooks clients to import as a json obj
      - finalize: whether the import process must be finalized
    
    If there is some sort of error, this view returns a json response 
    like the following:: 
      {
        'success': False,
        'error': 'error msg' 
      }
    
    @todo: Checking if clients from a freshbooks account have
        already been imported MUST be done in the import_contacts_setup view
    """
    # Ensure that the username and token are valid
    username = request.POST.get('username')
    token = request.POST.get('token')
    
    if None in (username, token) or '' in (username, token):
        return HttpResponse(json.dumps({
            'success': False,
            'error': 'Invalid Parameters'
        }))
    
    # Ensure that we have not imported contacts from this freshbooks account
    already_imported = (
        ContactmapsImport.all().
        filter('owner =', users.get_current_user()).
        filter('freshbooks_username ='******'success': False,
            'error': 'Clients from the Freshmaps account %s.freshbooks.com have already been imported as contacts' % username
        }))
    
    # Other parameters
    try:
        contacts = json.loads( request.POST.get('contacts', '[]') )
    except ValueError:
        contacts = []
        
    finalize = request.POST.get('finalize')
    if finalize:
        # Mark that the current google user has already imported
        # contacts from the input freshbooks account.
        ContactmapsImport(
            owner=users.get_current_user(),
            freshbooks_username=username
        ).put()
        
        contacts = [] # Prevent processing contacts. There should be none, though
    
    
    # Set up the Freshmaps account to import from
    freshbooks.setup('%s.freshbooks.com' % username, token)
    
    for contact_id in contacts:
        _contact = freshbooks.Client.get(contact_id)
        
        _name = '%s %s' % (_contact.first_name, _contact.last_name)
        if not _name.strip():
            _name = 'Unknown Name'
                
        # Save the contact in the datastore
        contact = Contact(
            owner=users.get_current_user(),
            name=_name,
            company=_contact.organization,
            address=_contact.p_street1,
            city=_contact.p_city,
            state=_contact.p_state,
            country=_contact.p_country,
            zip_code=_contact.p_code,
        )
        contact.put()
        
        # Add a task in order to calculate the contact's geoinfo
        # This uses AppEngine Task Queues.
        try:
            taskqueue.add(url='/contact-geo-info/', params={'key': contact.key()})
        except TransientError:
            # Sometimes a weird TransientError is thrown by the API, so retrying
            # might recover from that.
            taskqueue.add(url='/contact-geo-info/', params={'key': contact.key()})
    
    return HttpResponse(
        json.dumps({
            'success': True,
        })
    )
def import_contacts_setup(request, page):
    """
    This view is in charge of fetching freshbooks ids of clients 
    from a freshbooks account.
    
    It returns those ids to the frontend app, which in turn initiates 
    a series of ajax calls to import each and every id that this
    view has returned.
    
    This view is also called in a 'paging' way to overcome the
    30-seconds-max-per-request AppEngine limitation.
    
    It receives the following parameters via POST:
      - username: the freshbooks username
      - token: the freshbooks token
    
    In case of failure, this view returns a json response like
    the following::
      {
        'success': False,
        'error': 'error msg',
        'retry': True| False (Whether the frontend app should retry)    
      }
    
    In case of success, this view returns the following json response::
      {
        'success': True,
        'contacts': [1,2,3,n],
        'keepProcessing': True|False
      }
    """    
    # Ensure that the username and token are valid
    username = request.POST.get('username')
    token = request.POST.get('token')
    
    if None in (username, token) or '' in (username, token):
        return HttpResponse(json.dumps({
            'success': False,
            'error': 'Invalid Parameters'
        }))
    
    # Set up the Freshmaps account to import from
    freshbooks.setup('%s.freshbooks.com' % username, token)
    
    # Get the client_ids of the Contacts to import. This is done using paging
    # to overcome AppEngine restrictions
    try:
        page = int(page)
    except ValueError:
        page = 1
    
    options = {
        'per_page': settings.IMPORT_SETUP_CONTACTS_PER_PAGE,
        'page': int(page)
    }
    
    try:
        contacts = [c.client_id for c in freshbooks.Client.list(options)]
    except urlfetch.DownloadError:
        return HttpResponse(
            json.dumps({
                'success': False,
                'error': 'Connection timed out',
                'retry': True
            })
        )
    except urllib2.HTTPError, error:
        if error.code == 401:
            error_msg = 'Invalid username/token'
        else:
            error_msg = 'Connection to Freshbooks failed'
        
        return HttpResponse(
            json.dumps({
                'success': False,
                'error': error_msg,
                'retry': False
            })
        )