Choose Your Own Adventure  Presentation

Hamad AlGhanim

Kuwait University Graduate

Research and development engineer at KGL

Computer Engineering Masters student

Background:

Interests:

Python

Web Development

Android & iOS Development

#1

Live Tracking

Using WebSockets to track employees (Django Channels)

Things We Will Use:

  • Django
  • ASGI_Redis
    • Asynchronous Server Gateway Interface (replaces WSGI)
  • Daphne
    • HTTP, HTTP2, and WebSocket protocol server for ASGI
  • The new Django Channels

Websocket Server:

from channels import Group
from channels.sessions import channel_session
import json
from main.models import Employee


@channel_session
def connect(message):
    pk = message['path'].strip('/')
    Group('employee-'+ pk).add(message.reply_channel)
    message.channel_session['employee'] = pk
    

@channel_session
def ws_receive(message):
    pk = message.channel_session['employee']
    data = json.loads(message['text'])
    employee = Employee.objects.get(pk=pk)
    employee.location.latitude = data['location']["latitude"]
    employee.location.longitude = data['location']["longitude"]
    employee.save()
    Group('employee-' + pk).send({'text': json.dumps(data)})


@channel_session
def disconnect(message):
    pk = message.channel_session['employee']
    type = message.channel_session['type']
    Group('employee-' + pk).discard(message.reply_channel)

Websocket Client:

function initMap() {
    var myLatLng = {lat: -25.363, lng: 131.044};
    var marker;
    var map = new google.maps.Map(document.getElementById('map'), {
        zoom: 16,
        center: myLatLng
    });

    marker = new google.maps.Marker({
        position: myLatLng,
        map: map,
        title: 'Hello World!'
    });
    ws = new WebSocket("ws://livelocation.herokuapp.com/1");

    ws.onmessage = function(event) {
	var ws_location = $.parseJSON(event.data).location;
	var myLatLng = {lat: ws_location.latitude, lng: ws_location.longitude};
	marker.setPosition(myLatLng);
        map.panTo(myLatLng);
    };

    ws.onclose = function() {
	console.log("Socket closed");
    };

    ws.onopen = function() {
	console.log("Connected");
    };
}

Demo

#2

Web Scraping

Scraping My University's Portal System

What Do We Want to Achieve

  • Check University portal every n minutes
  • Update database with new seats
  • Notify people if the section they need has an empty seat (email and push)

Easy, right? Should take a day maximum? two?

WRONG!

Gonna tell you the sad truth about estimations

What Happened?

  • The website was an oracle ADF website.. which means?
  • Simple script won't cut it, used Selenium with Chrome
  • Switched to PhantomJS with Selenium
  • Docker + Celery + Django = ??

The Fix?

  • Heroku + DjangoQ + Django = Profit?
  • PhantomJS is too slow, no support for selenium..
  • [ 4 hours later looking at packets ]       Requests FTW!!

Improvements 

  • Down from 300 lines of code that fails most of the time to less than 100 reliable lines
  • Used to take nearly 3+ minutes for each subject, now can check nearly 8 subjects in 1 minute or less

Couldn't find the old screenshot :(

Future Plans?

  • Use Docker with a Linux box and not Heroku to get the best performance with no delay in scheduled tasks
  • Hopefully launch the app to the general public
  • Expand to other universities

A look at what the system scraped on its own

#3

5 Must Have Android Libraries

#1

Butter Knife

Before and After

class ExampleActivity extends Activity {
  @BindView(R2.id.user) EditText username;
  @BindView(R2.id.pass) EditText password;
    @Override
    public void onCreate(Bundle bundle) {
      super.onCreate(bundle);
      setContentView(R.layout.simple_activity);
      ButterKnife.bind(this);
      // TODO Use fields... (Yes thats it)
    }

    @OnClick(R.id.submit)
    public void submit(View view) {
      // TODO submit data to server...
    }
}
class ExampleActivity extends Activity {
  EditText username;
  EditText password;

  @Override
  public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView(R.layout.simple_activity);
    username=(EditText)findViewById(R.id.user);
    password=(EditText)findViewById(R.id.pass);
    btn=(Button)findViewById(R.id.submit);

    btn.setOnClickListener(
      new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            // Do something here
        }
    });
  }
 
}

#2

Glide

Before and After

// Download image
Glide.with(this)
     .load("http://goo.gl/gEgYUd")
     .into(imageView);

// Yes this is it

// ------ also you can load gifs -------
// Customizable
Glide.with(this)
     .placeholder(R.mipmap.loading)
     //can be a resource or a drawable
     .error(R.drawable.sad_taco)
     //fallback image if error
     .fit()
     // reduce the image size to dimensions 
     // of imageView
     .resize(imgWidth, imgHeight) 
     //resizes the image in pixels
     .centerCrop() //or .centerInside()
     .rotate(90f) 
     //or rotate(degrees, pivotX, pivotY)
     .noFade() //dont fade all fancy-like

// and more
private Bitmap DownloadImage(String url)
{
  Bitmap bitmap = null;
  InputStream in = null;
  String TAG = "DownloadImage";

  try
  {
      in = OpenHttpGETConnection(url);
      bitmap = BitmapFactory.decodeStream(in); 
      in.close();
  }
  catch (Exception e)
  {
      Log.d(TAG, e.getLocalizedMessage());
  }

  return bitmap;
}

#3 + #4 + #5

Retrofit + Gson + Realm

Why All Three?

  • They all work together beautifully
    1. Retrofit (API calls)
    2. GSON (Serialize JSON objects)
    3. Realm (Replaces SQLite DB)
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

Call<List<Repo>> repos = service.listRepos("octocat");

GSON + Realm

public class Repo extends RealmObject {

    @SerializedName("name") //name in JSON
    private String name;

}

RealmConfiguration realmConfig =
    new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);

// Get a Realm instance for this thread
Realm realm = Realm.getDefaultInstance();

realm.beginTransaction();

// Persist unmanaged objects
final Repo repo = realm.copyToRealm(oldRepo);

// Create managed objects directly
Repo newRepo = realm.createObject(Repo.class);

realm.commitTransaction();

Realm Queries

RealmQuery<User> query = realm.where(Repo.class);

// Add query conditions:
query.equalTo("name", "octocat");
query.or().equalTo("name", "Dreamersoul");

// Execute the query:
RealmResults<User> result1 = query.findAll();
// thats it

Any Questions?

Choose Your Own Adventure

By Hamad Al-Ghanim

Choose Your Own Adventure

  • 913