Sunday, March 30, 2014

Globe Customer Service - Suggestions for Improvements

I had a chat with a Globe agent today and it did not feel right so I had to share it somehow hoping to enlighten folks in customer service fields. This is not that BIG but I think there are still lessons which can be taken from this experience.

I was seeking their support because I tested my internet's speed and it seemed a bit off.  I had 2.02, 1.02 and 2.24 Mbps download stream results in a series of speedtests for a 3Mbps subscription plan. During the conversation, I kept retesting and looking at the modem page and the speed improved so I cancelled my complaints.

However, I noticed three (3) things that needed to be improved:
  1. Use of words. Whatever happened to "please" and "kindly"? Instead, the agent used "I want you to..." as if he was my commanding officer. It might be hard to really express yourself through a chat conversation but these basic things can help a lot.
  2. Avoid asking repeatedly as if you are a bot. The agent repeatedly asked me to disconnect my router (acting as a signal booster) and directly connect my computer to the Globe provided router. I think that if an agent thinks that the customer did not understand the first time they ask, at least rephrase and explain nicely. Using the same exact words (copied/pasted) in this situation is offending. Moreover, he broke down the sentences and it insinuated that the customer cannot comprehend what was said the first time. It might be because they are using pre-formed texts  that can be easily sent to the customer in one click. But still, it did not look nice.
  3. Follow Protocol.  In this conversation, after I told the agent that my connection then seemed fine, he sent me the thank you line and ended the session immediately. I thought it was a protocol to ask the customer if there are other concerns before ending the session? It might be a personal preference but I always wanted to reach the point where I can say my thanks to my agent and apologize if I have been mean at anytime during our conversation. Also, I could have told him he could improve the way he handles customers. But because I was cut off immediately, this blog post had to happen.
Transcript of the conversation with the Globe agent

As for me, I also have something I need to improve - keep calm. I really told myself even before starting the session that no matter what happen, I will remain calm. But what would I do? I expected customer service and it really disappointed me to have not found a nice one.  In terms of sales, I am the kind who can be sold out by the way the customer service is executed. If I walk-in a shop and given a good customer service experience, 50% of my decision to buy the item is secured - I tend to walk-out, otherwise.

I firmly believe that if you are a person working in customer service field, you have to mean it - provide customer service. Treat your customers nicely.
Wednesday, September 11, 2013

What Graphics Card Fit In Shuttle XPC SZ77R5

Earlier this year (2013) I bought a mini pc that will be dedicated for serving plex media server. I got the ShuttleXPC SZ77R5 because it was small enough for me and it works with the Intel Core i7-3770K since this need to handle multiple 1080p streams on my home network on different devices at the same time. The thing that I did not plan for is the video card. So I can make it a dedicated gaming pc for the most recent games and run it flawlessly. But these kind of cards are huge and usually won't fit this case without modifications.

So after some research I got this card,  Sapphire Radeon HD 7970 OC with Boost 3GB DDR5 DL-DVI-I/SL-DVI-D/HDMI/DP PCI-Express Graphics Card 11197-03-40G which barely fits and when you close the case it would probably overheat. Also you will have to remove the hard drive/cd rom rack to fit this card. I moved my hard drive on top of the case, there is a bunch of holes there where you can just attach it in. Then the hard part was cutting the case to have an opening in same place where the fan of the graphics cards are. I never really tried running it without that opening so I can't say if it would overheat.

It doesn't look that good now with the hole but it works fine.


For anyone that is interested on what else is in there here is the complete list specs.

Wednesday, August 14, 2013

Host Static Website on Google App Engine But With Flexibility of Templating System

If you already know how to use google app engine you can skip to the github project and the short summary will be enough for you to get started on creating static websites. I created this because google sites will stop supporting google adsense and I have a bunch of static sites for my projects that is using google adsense.


If you are new to google app engine and would like a way to create static websites with ability to do stuff like server side includes to avoid duplicating your headers/navigation and everything that you can do with jinja2 templating system and starts your hosting for free with daily quota and competitive pricing, by the time of this writing is (1GB/day free and 12cents/GB after).

First you need python 2.7 installed in your system, you can download at:
Choose the one for your system, then install google appengine sdk at:
The last thing you'll need is the github project above or download zip at:

Once you got all the things in place, install python, extract/install app engine sdk then extract master.
On mac and/or maybe windows there is a GUI where you can simply add the project extracted "app-engine-static-master" to on the appengine sdk and run it for testing. You should go to:


change the port on where it is running, and /dev/ is where all your dynamic website is shown, whatever you see here is what will be generated static files under gen folder. Now you can start building your website inside the templates/ directory, however you structure this will be how it will serve on your url.


templates/
    _layout/main.html (mapping is ignored on underscore(_) prefixes)
    docs/tutorial1.html (url maps to /docs/tutorial1.html and if hide_html_ext = True on generate.py it maps to /docs/tutorial1)
    about.html (maps to /about.html)
    index.html (maps to / your homepage)

The "hide_html_ext" will just generate the files under a folder and index.html to emulate the clean url designs but by default its using the .html which should be just as good. You would just usually touch the templates directory and then run:

python generate.py

Then it will create static files under gen folder which should serve directly from your running development server, when everything looks good deploy your app, if you need to modify anything you'll have to do it in templates/* then re-run python generate.py then redeploy your app.

For more info on how to make use of jinja2 check their official site:
http://jinja.pocoo.org/docs/templates/

Other app-engine-static specific features includes:

Jinja2 Filters:
{{ link('/about.html') }} - use this for any linking between pages

generate.py variables:
use_index_paths - hides index.html on generated static files
hide_html_ext - hides all .html and generated files are all under folder/index.html

static/ - should contain all your other non-generated static files such as css/js/images and other files
Friday, July 12, 2013

Cookie-less Domain and Static Files versioning with Google App Engine

Cookieless domains are one of the must have optimization when you are serving a lot of static files. The reason is that if you have a cookie in the same domain it sends those cookies in the request headers if you are serving your static files on the same domain. Those build up and unnecessary. If you have your cookie in domain level and using appspot domain for your main domain this won't work. This will also version your files so in your app.yaml you can leave (default_expiration: "30d") all the time. Each deploy will prefix your static urls with different version. Here is how I did it:


import os
from google.appengine.api import app_identity

VERSION_ID = os.environ.get('CURRENT_VERSION_ID', '1.1').split('.')
VERSION = VERSION_ID[0]
APP_VERSION = int(VERSION_ID[1])
APP_ID = app_identity.get_application_id()

IS_DEV = os.environ.get('SERVER_SOFTWARE', 'Development/%s' % VERSION_ID).startswith('Dev')
IS_BACKEND = backends.get_backend() is not None

def static_url(num=0):

    if not IS_DEV:
        return 'http://%s' % '.'.join([
            str(num),
            str(APP_VERSION),
            VERSION if not IS_BACKEND else 'cdn',
            app_identity.get_default_version_hostname()
        ])

    return ''


Then in all your static js/css etc prefix all with static_url(1) 2, 3 so it loads in parallel. Modern browsers should now load stuff in parallel but still helpful. The reason this works is cause in app engine you can have multiple versions of your app serving in appspot.com like (version).(app-id).appspot.com so what I did here is get the unique version of currently deployed app and prefix it like: (for-parallel-loading-number).(unique-deployed-app-version).(app-version).(app-id).appspot.com, (unique-deployed-app-version) is a version number that only changes when you do an app deployment so this is what we use for versioning the static file, so this will never change until your app is ready and deployed, but I'm not really sure if about it if this is something that can get used while other instance is not ready and request goes through an instance that is not ready yet.

This is also built-in to my app-engine-starter.
Tuesday, June 18, 2013

HTC One + I-Blason PowerGlider + Moga Pro Review

I bought the I-Blason PowerGlider battery pack thinking that it would fit in a moga pro for extended gameplay use, because there was a review somewhere in xda that the mophie doesn't fit in the moga pro. So here is a quick video to demo it altogether.


It does fit, but it can slip its heavy and hard to play while laying head up because the moga pro grip don't have a lock it might be the same issue with htc one alone. Although in the video it doesn't drop it does feel that it might, but it still fits which is the most important.

Here are more photos:



I used the moga tablet stand cause the HTC One + PowerGlider is too heavy to stay upright. For battery life, I've never ran out anymore, although sometimes I remove it and charge it by itself so I can still use the phone and never had to charge it directly. Beware though it only has half the mAh output of the charger, people say its fine but I really don't know how it would affect its battery.

Connecting the moga pro to my phone seems to disconnect the wifi, not sure if this is a common issue. It is having hard time to find the controller with wifi on. Other than that everything seems smooth, the moga is very responsive.


Friday, June 14, 2013

Google App Engine VirtualEnv Tool that is not virtualenv for Python

When I build app engine projects and I want to include python package, what I do is download the source and symlink/include it in the project. I have tried the virtualenv approach but it didn't feel clean and I couldn't find anything that will suit my needs in a simple way. So I created this new tool called gaenv which will automate installation/linking of your installed packages to your gae project, it doesn't really need to be a gae project since what it does is just create a folder of symlinks following your requirements.txt so this can be useful to any container base deployment packages that follows symbolic links.

Installation & usage:
    # Note that if you install on a system with multiple python, 
    # you need to call the correct binary on which it looks up packages
    $ sudo pip install gaenv
    $ cd to/your/gae-project
    # Create your requirements.txt & run this to install it
    $ pip install -r requirements.txt
    $ gaenv

That's it, this should create a folder named gaenv_lib on the same path you execute it on and if you said "y" on the detect insert import it will look into your app.yaml and try to add import gaenv_lib before any other import happens. You should just do it manually if you have complex  setup on your project, what it basically does is simply add gaenv_lib in sys.path. To overwrite those names you can check with gaenv -h

To people that haven't experienced using requirements.txt it's basically a list of your python packages  per line. Then using pip install -r requirements.txt just install them all. gaenv follows the same format. Here is a sample:

boto>=2.8.5
peewee
flask==0.9


You can download and install manually at
https://pypi.python.org/pypi/gaenv
it's also on github at
https://github.com/faisalraja/gaenv

Hope someone finds it useful as I did. Enjoy.

Also note that this shouldn't stop you from using virtualenv with all your gae compatible packages then just run install gaenv on that virtualenv and run the binary from it's bin folder, this way you just maintain one virtualenv.
Tuesday, June 11, 2013

Windows Process Monitor Script in Python

I have a windows desktop that is always tunneled to some server for reason that is not important. I use kitty.exe an improved putty with auto re-login and more, but only needed that feature that's why I'm using that. But for some reason it stops responding if a lot of traffic has passed to the tunnel and it has now happened too frequent that I just had to auto restart it, there might be softwares that does this already but here is what I did using python. This should be reusable with any process just change the variable names you want to monitor.


import os
import subprocess
import time

__author__ = 'faisal'

# Change this if you want to monitor a different process
process_name = 'kitty.exe'
# Separate your parameters
start_command = ['C:\Applications\Putty\kitty.exe', '-load', 'MyTunnelSession']
# Sleep time in seconds before checking again
sleep_time = 60


while True:
    # Filter a list of windows process that has stopped responding
    r = os.popen('tasklist /FI "STATUS eq Not Responding"').read().strip().split('\n')
    for p in r:
        if p.startswith(process_name):
            # It's a quick script so just go get the
            # first # valid info which is the PID
            d = p.split(' ')
            d.pop(0)
            i = d.pop(0)
            while not i:
                i = d.pop(0)
            # Kill by PID
            k = os.popen('taskkill /F /T /PID %s' % i).read()
            print k

    # Now we check if kitty.exe is running then run it if its not loaded
    r = os.popen('tasklist /FI "STATUS eq Running"').read().strip().split('\n')
    found = False
    for p in r:
        if p.startswith(process_name):
            found = True

    if not found:
        with open(os.devnull, 'wb') as devnull:
            subprocess.Popen(start_command, stdout=devnull, stderr=subprocess.STDOUT)

    print 'Running time: %s' % time.time()
    time.sleep(sleep_time)

Also let this start your process instead of having it already started.