Thursday, December 15, 2016

Back to teaching but part time mind you

So I'm just back to teaching part time at USTP which is formerly MUST. I've been asked to teach Object Oriented Programming. And this is what I've noticed.

Starting with the OOP syllabus, I noticed:
  • It's outdated as fuck. For example, it still mentions "Java applets". Yes, that's 1998 for you.
  • The syllabus reads like it's a Java 101 class instead of purely OOP class. 
Aside from the syllabus, the lab setup is wrong.
  • "Standalone Lab" which translates to "Computer Lab WITH NO Internet". Yes, we're back to 1998 again. All Java programming to date requires Internet access. Heck, ALL programming activity requires Internet access.
  • This assumption of using IDEs (Netbeans, in this case) at the start. I disagree with this. IDEs hide or automates the actual process of programming. Not understanding the actual process doesn't make for good devs or programmers.
This complaints are besides the point, what I really found was a wide, gaping disconnect on how OOP is seen in the industry and in academia. This could also be true for other IT related subjects.

We need to find a way to close the gap because if not we are condeming a generation of students, workers to "Hello sir, may I take your order?"

And that is just fucked up.

Thursday, October 27, 2016

SimpleDictDiffer.py when deepdiff is too much

Working with Python for the past 3 months coming from Java has been interesting (grossly understated).

Anyhow, I found myself needing a way to find the stuff (not just difference) between two dictionaries and a quick google led me to deepdiff by Sep Dehpour. It was good but found it a tad overkill for what I was doing. So another round of google and stackoverflow allowed me to cobble together this:

class SimpleDictDiffer(object):
    """
    Calculate the difference between two dictionaries as:
    (1) items added
    (2) items removed
    (3) keys same in both but changed values
    (4) keys same in both and unchanged values
    """

    def __init__(self, first_dict, second_dict):
        self.current_dict, self.past_dict = first_dict, second_dict
        self.set_current, self.set_past = set(first_dict.keys()), set(second_dict.keys())
        self.intersect = self.set_current.intersection(self.set_past)

    def added(self):
        return self.set_current - self.intersect

    def removed(self):
        return self.set_past - self.intersect

    def changed(self):
        return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o])

    def unchanged(self):
        return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o])

A github gist for this is also available.



Thursday, September 22, 2016

Flask on Windows and the case of the Pip install failing

At work I have a Mac and back home I have a Linux (Fedora) and Windows 10 machines. I started a Flask project on the Mac and then put it in a Github repo which I then cloned on my Windows 10 machine. And this is where I encountered the problem of pip failing to install the modules. I tried two variations to the call:
  1. > pip install -r requirements.txt
  2. > python -m pip install -r requirements.txt
This is with Python 3 and both will spit out a "Permission Denied" error relating to your appdata/Temp folder. The contents of my requirements.txt is pretty tame.

Here's a partial.

Flask==0.11.1
Flask-RESTful==0.3.5
Flask-Webpack==0.1.0
get==0.0.0
itsdangerous==0.24

It fails right on the get module. It say something like it can access the temp folder on your Windows machine which put me on a wild goose chase because the error isn't fucking related to permissions. I figured out the fix after watching the temp folder while pip attempted to install the modules.

Apparently, the Windows pip version does something really weird with the setup.py and/or egginfo where it attempts a build on a empty setup folder for the get module causing the error. The fix is to just change the version. In this case for get==0.0.0 to get==0.0.20.

The pip install command should go off with no problems now.


Tuesday, August 30, 2016

Python zipfiles with the cloud is technologic

Working with zip files in Python is straight forward.

Write it

s3connection = self.get_s3_connection()
bucket = s3connection.get_bucket(aws_bucket_name)
k = Key(bucket)
k.key = key.name

k.get_contents_to_filename(file_path)

Cut it, Paste it, Save it

import shutil
shutil.copyfile(self.src_zipfile, self.temp_zipfile)

Load it

zf = zipfile.ZipFile(zip_file)   # turn file into a zipfile object
zf.extractall(path=unpack_path)  # extract all files to path
files = glob.glob(os.path.join(unpack_path, 'list_*.txt'))  #glob glob
for files in files:
    # to stuff to the files here

Check it

# In a test case
zf = zipfile.ZipFile(zip_file)
zf.extractall(path=unpack_path)
files = glob.glob(os.path.join(unpack_path, 'list_*.txt'))
self.assert(len(files),expected_file_count)


Quick - rewrite it

with open(some_file, 'r+') as file:
    #do file rewrite here
    file.seek(0)
    file.write('Quick!')
    file.truncate()

Now here's daftfunk - technologic

Wednesday, August 3, 2016

Tidbits on Python's virtualenv

Python being a mess that it is - *cough* Python 2 vs 3 *cough* - has a thing for virtual dev environments.

Virtual dev enviroments or virtualenv solves the mess by creating isolated dev environments. Each environment then can have it's own Python and lib versions, dependencies and even permissions and settings. Virtualenv is complimented by virtualenvwrapper.

This is all well and good, unfortunately, virtualenv + wrapper are terminal (command line) driven and I often forget the basic commands. Hence, this blog post:

Basic assumption here is that you've setup and configured virtualenv with virtualenvwrapper.

1. Make a new virtual environment with a specific version of Python: use the -p flag

mkvirutalenv -p [path-to-python] [project name]

example: mkvirtualenv -p /usr/bin/python project1

2. List all existing virtual environments; workon command without arguments also works

lsvirtualenv -b

-b flag = brief mode, disables verbose output

3. Delete or remove an existing virtual environment; only deletes environments found in the WORKON_HOME path

rmvirtualenv [env_name]

Virtual environment must be deactivated before removing.

4. Deactivate current active virtual environment

deactivate

Also:

- Command ref for virtualenvwrapper
- Ref for virtualenv

Monday, August 1, 2016

Facebook not playing nice with Twitter in an Ionic app

This bug I encountered when my Ionic was logged in via Facebook. It caused my Twitter routine not to retrieve user timelines. My Twitter routine depends on a Application only login to get an Oauth token.

The symptom of my problem was when doing a call to api.twitter.com/oauth2/token and api.twitter.com/1.1/statuses/user_timeline.json, Angular's $http wasn't sending the correct header values and I kept getting invalid request (code 99) error from twitter.

POST https://api.twitter.com/oauth2/token HTTP/1.1
Authorization: Basic [base64 code here]
Content-Type: application/x-www-form-urlencoded;charset=UTF-8

The thing to focus here is the Authorization value. Instead of a base64 encoded value for Twitter, Facebook overwrites it or I can't overwrite it via a $http config object.

With the bug understood, I found the solution was to use a $httpProvider interceptor to modify the header before sending it to the server.

function AuthHeaderInterceptor($rootScope, $q, $location, localStorageService) {
    // Function to handle a request
    var request = function (config) {

        var expression = /(https?:\/\/(.+?\.)?api.twitter.com\/1.1\/statuses\/user_timeline.json(\/[A-Za-z0-9\-\._~:\/\?#\[\]@!$&'\(\)\*\+,;\=]*)?)/g;
        var regex = new RegExp(expression);
        var test_url = config.url;

        if(test_url.match(regex)){           
            var authToken = localStorageService.get('twitter_token'); // get token from local storage
            config.headers.Authorization = 'Bearer ' + authToken;     // Force overwrite the Authorization header
        }

        // Return our config
        return config;
    };

    // Function to handle errors
    var responseError = function (rejection) {
        // Return our rejected promise
        return $q.reject(rejection);
    };

    return {
        request: request,
        responseError: responseError
    };
}

All that's left is to set this to my app config and I was good to go.

Monday, July 18, 2016

Typescript is what JavaScript should have been

When Brendan Eich added JavaScript to the Netscape browser some 20 years ago, he had roughly a month to do it and, context-wise, JavaScript was added to the Netscape browser as a reaction to the popularity of Java - applets - at that time.

JavaScript was something you could use to interact with the browser via small programs and scripts - think sub 1k lines of code. Unfortunately, that's no longer the situation. It has become common to see 1 million lines JavaScript projects. Projects of this size are unmanageable:

1. Tooling is bad - barely there intellisense, unsafe or no refactoring

2. Development flow sucks - you can't check for common errors until you refresh the page

3. JS Code bases of these sizes are hard to reason about for many reasons like duck typing especially if you got code with bad or lazy naming conventions

This is where Typescript comes in.

1. TypeScript's static typing and annotations are great for catching errors on the tool rather than waiting for a page refresh

2. Code is easier to reason about; example, function params are known if you're using interfaces

3. TypeScript allows safe refactoring and good intellisense support

4. Although from Microsoft, TypeScript is open source with a clear roadmap with rapid releases - typically 3-4 months

But Typescript is not without faults.

1. TypeScript is superset of JavaScript. So any valid JavaScript is also valid TypeScript. In that case, if you write crappy JavaScript, you still get crappy TypeScript. Somewhat fixed if you read and apply Douglas Crockford's advice in his seminal book, JavaScript: The Good Parts

2. Typings. Sort of an edge case problem because some javascript libraries don't have typings thus we don't have intellisense for that library

The good stuff out-weights the bad parts for me. Typescript is what JavaScript should have been.

Wednesday, July 6, 2016

2 very useful super nerd tricks with terminals

In Unix land and yes that includes the mac fangays, they are called terminals. In Windows land, they are called the command line and it makes Windows users cry.

Anyhow, too often I find myself traversing a long file path on a terminal. You know what I mean by where you're doing several CD commands and when you do a pwd it will show something like "/etc/var/someRandomAssAppFolder/config/local/" or "C:\Users\Jay L. Ginete\.android\avd\IonicDevice.avd\hardware-qemu.ini.lock\". You're just in too deep and then suddenly you need to open a damn window for whatever reason. If  you don't know the trick then you'll have to navigate to said path. Shit. Nobody got time for that.

So next time what you'll do is:

Windows:

C:\Users\killertilapia\Documents\WebstormProjects\Katrina>start .

On Macs

$ open .

And there you go, it opens a Explorer window if on Windows and a Finder if on a Mac (or *nix equivalent) on the same path your terminal is on.

Friday, June 10, 2016

Python and the MySQL driver hole I found myself

So I'm a Python developer now and Django to boot. To those who read this blog would have noticed that I primary do Java and AngularJS but I've used Python sparingly so this change ain't so bad.

But first off, this Python 2 and 3 is freakin' annoying but it still works out nicely with virtual environments. Then I walked into Python's mess of MySQL drivers. Working with Django with a MySQL backend, there's little or no mention of needing a MySQL driver, if a newbie walked into this error it take them a chuck of time to figure it out. And if they figure it out, will then stumble into the myriad of options (mysql-connector-python, PyMySQL, etc.) which lead me to the hole I mention on the title.

The whole start was just because I picked PyMySQL as my driver which a pure Python implementation of a MySQL connector. My Python script kept failing on me until I found that I need to install the damn thing:

try:
    import pymysql

    pymysql.install_as_MySQLdb()
except ImportError:
    pass

Before I run my __main__ function.

Phew.

Well, I can't back out now. I'm a Python dev now anyway.

Wednesday, May 11, 2016

Being stupid while calling Twitter's Search API

A few days ago, I just figured out how to authenticate my Ionic app using Twitter's application-only Oauth. Application only authentication allows you call Twitter APIs without that Twitter login screen.

The being stupid part started when I tried using the Search API.

    var twitterStreamURL = "https://api.twitter.com/1.1/search/tweets.json?q="; 
    var qValue = "queryString";
    var numberOfTweets = "&count=10";

It should be easy to see that to call the Search API you'll concatinate the qValue (or query string) and the numberOfTweets (number of tweets to get) to the twitterStreamURL. You'd do then a ajax call then get the resulting JSON.
    
    var cURL = "https://api.twitter.com/1.1/search/tweets.json?q=" + qvalue + numberOfTweets; 

These would be no problem with the query string had only one value or no special characters. This stumped me a bit because I used escape() function at first which I knew about. It still works but the escape() function is deprecated. Which lead me to encodeURI(). It worked until it was asked to search for strings with hash tags. I didn't read the fine print for encodeURI which said it doesn't encode certain special characters. This finally lead me to the encodeURIComponent() function.
    
    var cURL = "https://api.twitter.com/1.1/search/tweets.json?q=" + encodeURIComponent(qvalue) + numberOfTweets; 

The moral of this story is I need read the fine print.

Friday, April 8, 2016

Ionic, Satellizer, Facebook and that "Given URL is not allowed" error

You can have your Ionic mobile application use Facebook authentication. You can do it the hard way - i.e. do it yourself via $http calls - or go the easy route via Satellizer. Being the lazy bastard that I am, I'll be using Satellizer.

Satellizer can be setup quickly, do bower install, add the needed JavaScript bits to your index.html and reference it in you Ionic app.

angular.module('meAwesomeIonicApp', ['ionic', 'ngCordova', 'satellizer', 'ngAnimate']).config(...)

From here you'll need to go to Facebook Developer and register your app. You'll then add the FB application appId to your satellizer settings. It should look something like:

    var commonConfig = {
        popupOptions: {
            location: 'no',
            toolbar: 'yes',
            width: window.screen.width,
            height: window.screen.height
        }
    };

    if (ionic.Platform.isIOS() || ionic.Platform.isAndroid()) {
        commonConfig.redirectUri = 'http://localhost/';
        $authProvider.platform = 'mobile'
    }

    $authProvider.facebook(angular.extend({}, commonConfig, {
        clientId: 'YOUR FB APP ID HERE',
        url: 'http://localhost:3000/auth/facebook',
        responseType: 'token'
    }));

This moves us to the controllers. In the controllers we have access to a $auth service which is provided by Satellizer. The $auth service then provides a authenticate(string) function. So we have:
 
$auth.authenticate(provider)
        .then(function() {n
             // Success login
        })
        .catch(function(response) {
             // Error in login
        });
};

You can easily add this to a ng-click handler. And this is where we encounter the "Given URL is not allowed" error. What's happening is that when we call $auth.authenticate(), it will try to open a FB login page based on the url value we configured in the $authProvider.facebook() call instead we get the error page instead of the login form.

Fortunately, for me the fix was easy. I just didn't configure the settings in the FB developer app page correctly. It isn't enough to just configure the Basic Section in the Settings page. You need to open the Advance Section and also configure the Valid OAuth redirect URIs values also. So if you add the http://localhost value in the textfield, it should fix the "Given URL is not allowed" error.

Wednesday, February 17, 2016

Tapestry5 and the case of the disappearing bootstrap modal dialog

Tapestry5 has a pretty neat way to support JavaScript but once in a while I forget some things. Forgetting things then leads you to errors. In my case, the error was a bootstrap modal not properly displaying or disappearing immediately.

I was working on a simple but reusable modal Tapestry5 component. For some context on how to do this, go here. The important part here is the top part where you declare your JavaScript includes:

@Import(module = {"bootstrap/modal"}) 
public class ModalDialog implements ClientElement{...}

This loads the modal.js from bootstrap. Tapestry5 can do this because the framework is bootstrap aware. We then finish this up with the javascript and tml file for the component.

Thinking all was Ok, I then use this component on a page and this is where I encountered the "disappearing modal" dialog.

After debugging, I followed the bug to my layout component. Again, the important part here is where we declare the JavaScript includes:

@Import(stylesheet = {"context:datatables/css/dataTables.jqueryui.css","context:patternfly/css/patternfly.css", 
                      "context:patternfly/css/patternfly-additions.css"},
        library = {"context:patternfly/js/patternfly.js", "context:mybootstrap/js/bootstrap.min.js"})
public class Layout{...}

Apparently, bootstrap doesn't like when we double load modules. The bootstrap.min.js is a minified bootstrap and it already has the modal.js module. Then, when we load the page where we use the ModalDialog component which double loads the modal.js module.

This bug was easily fixed by removing the import annotation in my ModalDialog component.

I forgot about my JavaScript basics that invoking the script twice equals problems.

Also, this was confirmed by this StackOverflow question.

Wednesday, January 13, 2016

Reset Auto Increment ID in SQL Server

Been asked this way too many times and I google it every time. So during development, when you need to reset the ID for your auto increment column:

DBCC CHECKIDENT (mytable, RESEED, 0)

This command reseeds "mytable" to start at 1. Be careful that we don't have records higher than the seed value you are setting. You'll break the table.

Also:

  • http://stackoverflow.com/questions/510121/reset-autoincrement-in-sql-server-after-delete