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
- Retrofit (API calls)
- GSON (Serialize JSON objects)
- 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