Interesting Swift: overview of some interesting things in swift

So I am playing with swift a little bit for this holiday, and found out it to be very interesting language to play with, they actually designed many things that is actually more interesting than the languages I am very used to with. This blog is possibly not going to be a blog that I would refer people to read for learning as I am mentioning java, python or ruby all the way I am going with this blog and not everyone is supposed to be aquatinted with these language, but I am doing it get away with the feeling that I had while playing around.

So far I could find two ways in swift to initialize variables either var or let. var is for variables that actually varies and let is something that remains the same forever. Java has its constant using final that prevented us from changing the variable values once it got initialised. Ruby ensures it by putting its variable name all capital, which I am not a great fan of. Python does not provide much straight forward support for constants. I personally like constants because I believe internally at programming language implementation level it should give compliers and interpreters enough power to optimize and it helps me to reduce bugs in my code as I would have full control over the code on what I am doing and what the variable is intented to be doing even after passing my code to some one else to modify or add more features. Not to mention I have every freedom whether to define a variable with an associated type or let the initialized value to infer its type, which was also great. Although it is considered as a bad coding style to let swift not to infer the type.

var text: string = "hello world"
var text = "hi world"

lazy vars are also interesting, I actually never thought of something like that as I thought compilers or other optimizations tool we are using would take care of that. When we declare a variable as lazy variable, swift initialize that variable when it is absolutely required.

It is also interesting and very useful I believe the way swift syntax is taking care of if variable is initializaable or not.

if (let text = button.currentTitle){
 //display.text = display.text + text  
}

Python has a different syntax idea about ternary operations where java, ruby has almost the same syntax as swift.

output = a != nil ? a! : b

Some short hands on ternary operations comes handy like ruby:

output = a ?? " "

This is interesting that swift won’t allow me to put a variable without we initializing it. Or we have options for definiting a variable as optional when we are not intending to initialize it, now what are optionals? Glad that you asked.

Optionals are actually very interesting concept to me, In ruby code, while using other peoples API send? and send! with a bang, these variations of name actually told me a lot of details and safetly measure required or not on that methods by convention. It felt like swift took it to the next level, so far as my introductory tutorial states some of the properties of an instance can be optional type as well, that is being represented with a question mark in the end, like String?, that is just a special string. Optional actually can be used for chained calls and if it fails to reach to the end of the expression it returns nil. So it saves us from writing a lot of lets and if clauses.

let x = display?.text.hasValues{}

point to note that, x will be an optional in that case because we can never get a value return from this kind of chained methods.

We even can define this optional strings in our code.

var label: String?;

Its interesting and completely make sense that for getting back data from dictionaries we get an optional type in return. Also AnyObject is a type that takes anything and everything through a parameter or pretty much anywhere as a type but we might want to use as to treat that anyobject to something else let foo=ao as? String{}. as is just casting it to another class.

These special we actually need to take special care when handling with these optional properties. We need to unwrap that variable with a bang(!).

func touchButton(button: UIButton){
 let text = button.currentTitle!
 print("pressed \(text) button")
}

I have mentioned earlier that you can’t define a variable when you do not initialize it. Let me add more information to that statement, we actually can implicitly unwrap a variable while defining it as well, that will reduce our burden of unwrapping it every time we intended to use it.

var display: UILabel!
print("\(display.text)")

var cash: Double {
set: {
  return Double(label.text!)
}
get:{
  label.text = cash
}
}

Apart from computed properties, there are actually few other interestng properties too, like observer properties. willset and didset could be one of them.

Swift function declaration could be very interesting as well, we can define internal and external parameters in swift functions, internal parameter has scope locally when external parameters are the parameter the callee of the method has to fullfil.

Like python it provides options for positioned parameter, in swift it can be done using an underscore before the parameter name, which I did not like most, but it is how they speak swift.

Java has some support for lambda expressions, python and ruby has way better support for it though. My first impression on swift anonymous methods was pretty interesting, I would say, it is even easier.

{str1 :String, stt2:String} ~> String in
return str1 + str2

Swift has options for anonymous abstract methods as well, which can be written as following:

(Double, Double)~> Double

Need to mention that in like python or ruby functions are also considers as object in swift. So does this previous implementation of abstract class. I will be talking about its use cases in a while.

Speaking of enums I am not a great fan of python or ruby enums so I would deliberately avoid talking about them, Java has great support for enums, it supports internal methods in enum which is great, I am glad that swift also supports internal methods like java even more interesting way actually. I guess in java I did not have the credibility to use separate initializer for separate cases, what swif offers,

enum MathOperator{
 case None
 case Constant(Double)
 case Unary((Double)~> Double)
 case Binary((Double, Double)~> Double)
}

As you can see, now we have options to define our enums with so many informations, even with functions.

We can have a dictionary of our operatons like following in swift:

let operations: Dictionary<String, MathOperator> ={
 "e": MathOperator.Constant(M_E),
 "±": MathOperator.Unary(sign_change),
 "+": MathOperator.Binary({op1 :Double, op2:Double} ~> String in
  return str1 + str2
 )
}
func sign_change(number: Double) ~> Double{
 return -1* number
}

Now when we need to use this we can use swift switch statements, point to note on swift switches are they are not fall through like typical switches, if we want it then we would need fallthrough to make it fall to next case:

func sign_change(number: Double) ~> Double{

 if let op = operations["+"]{
   switch op:
    case .Constant (let value):
      return value
    case .Unary (let function):
      return function(input1)
    case .Binary(let function):
      return function(input1,input2)
 }
}

Java does not support struct, but as far as I know there are ways to implement struct like objects in java, I have heard people saying that. I have read, and seen people bundling properties in struct in ruby and python, I have used struct for classes for temporary uses and data bundling in ruby. In swift I think it has huge scope for using it for temporary classes, I have been warned that every time I am creating a class it is possibly taking a whole new space in the memory unlike a class reference. So I need to be a little bit warned when using structs, but swift has its own beautiful way to manage it, like every time only when the data changes in swift struct it consumes the memory and it has intelligence to figure out which part of data it need to reallocate, not all of them always. Enums or structs stores its properties as a constant so we can’t change it without recreating the struct in the heap and there are special ways to do that using function like putting mutating func while declaring a function.

I really liked the way I can ensure type order in swift touples, I missed this sort of declaration while working in python as I wanted to have somewhat a gurantee that a function return type will be somewhat fixed.

let x: (String, Int, Double) = {"hello world", -1, 0.2}

Recently i had a great fight with python ands its unicode to make a work done perfectly. We need to add special comments at the header of script. Swift was a lot programmer friendly in that regard, Swift unicodes are full unicodes.

I guess sometime I will have to use objective C codes, as the tutor mentioned about NSObject class. To use some of the objective c apis I would need to inherit NSObject class. It has many other NS objects, I don’t think it would be very interesting thing to talk about. So deliberately ignoring them.

CentOS postgis setup: You need JSON-C for ST_GeomFromGeoJSON

I am actually struggling with this error on our centos server all day, this is how I fixed it. I could not recover everything from my commandline history but this is what I could find out. Hope it will be helpful. I am sure while cleaning up my server for production it will be helpful to me. So cheers!

All this started with this error message on my centos server:

npm-2 Unhandled rejection SequelizeDatabaseError: You need JSON-C for ST_GeomFromGeoJSON
npm-2     at Query.formatError (/home/centos/jobcue.com/node_modules/sequelize/lib/dialects/postgres/query.js:357:14)
npm-2     at null. (/home/centos/jobcue.com/node_modules/sequelize/lib/dialects/postgres/query.js:88:19)
npm-2     at emitOne (events.js:77:13)
npm-2     at emit (events.js:169:7)
npm-2     at Query.handleError (/home/centos/jobcue.com/node_modules/pg/lib/query.js:108:8)
npm-2     at null. (/home/centos/jobcue.com/node_modules/pg/lib/client.js:171:26)
npm-2     at emitOne (events.js:77:13)
npm-2     at emit (events.js:169:7)
npm-2     at Socket. (/home/centos/jobcue.com/node_modules/pg/lib/connection.js:109:12)
npm-2     at emitOne (events.js:77:13)
npm-2     at Socket.emit (events.js:169:7)
npm-2     at readableAddChunk (_stream_readable.js:153:18)
npm-2     at Socket.Readable.push (_stream_readable.js:111:10)
npm-2     at TCP.onread (net.js:531:20)

When I tried to install json-c on server it was like:

sudo yum install json-c
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: centos.eecs.wsu.edu
 * epel: s3-mirror-us-west-2.fedoraproject.org
 * extras: linux.mirrors.es.net
 * updates: mirror.raystedman.net
Package json-c-0.11-4.el7_0.x86_64 already installed and latest version
Nothing to do

Then I started panicing. Tried 5-6 hours long yum battles and figure out a solution that would look like following:

Install some dependencies at first:

yum install geos-devel.x86_64
yum install proj-devel.x86_64
yum install gdal-devel.x86_64
yum install libxml2-devel.x86_64
yum install json-c-devel.x86_64

yum install postgresql92-devel
sudo yum install postgresql-server 

sudo yum install geos geos-devel
wget http://download.osgeo.org/proj/proj-4.8.0.tar.gz
gzip -d proj-4.8.0.tar.gz
tar -xvf proj-4.8.0.tar
cd proj-4.8.0
./configure
make
sudo make install

I needed to install gdal:

installing gdal:

sudo rpm -Uvh http://elgis.argeo.org/repos/6/elgis-release-6-6_0.noarch.rpm
sudo yum install -y gdal
./configure
make
make install

Obviously I needed to install json c:

sudo yum install json-c-devel

I needed to know where it is located:

rpm -ql json-c json-c-devel

for me it was at:

/usr/include/*

Now it is time to built our postgis like this:

wget http://download.osgeo.org/postgis/source/postgis-2.2.1.tar.gz
tar xvzf postgis-2.2.1.tar.gz
cd postgis-2.2.1
./configure --with-jsonc=/usr/include

make
make install
sudo make install

In app user to user chat using auth0 and firebase

So we are using auth0 for our user authentication, we have a server of our own which runs using relational database which seems to be perfect for our use case. Now we need to install a chat application for our user to user communication. How do we do that? Chat is a big data issue, and we need faster communication that happens in real time. How do we do that? We are using firebase for this. This is actually first time I am using firebase, to be more precise its my second day with firebase so I am quite excited that I am writing about this cool technology. The goal of this post is not only to help other but also to get help from experienced. Please point out my mistake and tell me how can I improve this.

Firebase supports actually social logins pretty well, but since we already has relied on auth0 for authentication, we won’t bother firebase to handle that. Firebase supports custom authentication, we will be using that. To issue a custom authentication token we actually need to use our firebase secret, we can’t let any user to see that so ideally which means our API server need to issue that then other client will use. Auth0 actually made our life a lot easier, we can add a firebase plugin to auth0 and auth0 will generate that custom authentication token for us for now on, so we don’t need to do that using api anymore.

I appreciate their initiative for firebase but there was some limitation at their service as well, they actually do not provide an authentication uid straight away for firebase rule auth.uid which is a must have before we start writing our firebase rules. But don’t panic we actually have a solution for that. We solved this problem by adding a simple auth0 rule:

function (user, context, callback) {
  if (context.protocol === 'delegation') {
    return  callback(null, user, context);
  }

  user.firebase_data = {
    uid: ""+user.user_id,
    user_id: ""+user.user_id
  };
  
  callback(null, user, context);
}

Now we are good to write our rules for firebase. We understand firebase is completely schema less but we will chalk down pseudo schema, a model for our database.

I think the key idea behind a beautiful designing is to see what is coming next, we want to design our model in a way that it becomes easier for it to its evolution. Later at some point we want to use our app for a conference chat, we want to let user edit and delete their messages. We want to avoid any sort of data duplication anywhere possible.

So we will have a conversations that each of the participants can access upon permission.

Lets do the relatively hard stuff first, we will design our conversation model. Roughly a conversation will have a limited array of members and lots and lots of message entries. So it should look like following:

conversations:{
 conversation0:{
  members:{
    0: user0,
    1: user1,
    ...
  }
  messages:{
    messages0:{
      message: "lets chat!",
      fromUserId: user0,
      createdAt:  timestamp
    }
  }
}

But how are our users going to access their rooms? Yet we don’t have any way to figure out which conversations a user has subscribed. We also need to figure out which conversation has unread messages, this is not symmetric, remember? So we would like to keep them inside our user data.

users:{
 users0:{
  conversations:{
   converation0:{
    last_read: timestamp,
    subject: weneedspace,
    added_by: timestamp,
    added_at: timestamp
   }
  }
 }
} 

Now we have everything, but are we secure enough from the bad guys out there? NOPE. We need rules based on which firebase will be protecting us.

Lets go for our users rules. Firebase apps are actually easily will go fragile if we don’t take enough care of it, so we know that user will have 4 properties at least last_read, subject,added_by, added_at.

newData.hasChildren(['last_read','subject','added_by','added_at'])

So we will be validate that they exists in the first place. Now we have the valid structure, but we actually don’t know that added_by is valid or not.

newData.child('added_by').val() = auth.uid

Let it be always the authenticated user uid. The user is authenticated but does he allowed to make someone else subscribe to that conversation? We will look into conversation membership list to validate that.

root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)

Now who has write permissions? As long as you are authenticated you are fine.

auth != null

Who can see my subscription? Privacy issue here. I won’t let ‘A’ authenticated user read my conversations. He must be ‘THE’ authenticated user.

auth != null && auth.uid == $user_id

For now we will only users to see through /users/conversations/ and write through /users/conversations/conv0/

so user rules go like this:

    "users":{
        "$user_id":{
          "conversations":{
              ".read": "auth != null && auth.uid == $user_id",
              "$conversation_name":{
                ".write": "auth != null",
                ".validate": "newData.hasChildren(['last_read','subject','added_by','added_at']) 
                              && newData.child('added_by').val() = auth.uid
                              && root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)
                              "
              },
              
            }
          }
      }

You can see that our validation relies on the existance of /conversations/conv0/members/0. So we need to ensure that we do first. While creating a conversation you must include yourself in a conversation thats how we validate.

newData.hasChild(auth.uid)

Any very simple to have write access you need to be authenticated.

auth != null

Now what would be our basic message look like?

newData.hasChildren(['fromUserId', 'message', 'sent_at'])

We also need to check if fromUserId is valid for that user or not.

auth.uid == newData.child('fromUserId').val()

Who can write messages? only if you are a member.

root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)

We have messages, and members both of them has at least someone who can write them. But we don’t have anyone who can read them. You can only read messages only if you are a member of it. We will add this to the parent of both of them.

data.child('members').hasChild(''+auth.uid)

Altogether conversations rule:

       "conversations":{
          "$conversation_name":{

                ".read": "data.child('members').hasChild(''+auth.uid)",
                "messages":{
                    "$hash": { 
                      ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",
                      //".validate": "auth.uid == newData.child('fromUserId').val()"
                      ".validate": "newData.hasChildren(['fromUserId', 'message', 'sent_at'])
                                   && auth.uid == newData.child('fromUserId').val()"
                    }
                  },
                "members":{
                   ".write": "auth != null", //"!data.exists()",
                   ".validate": "newData.hasChild(auth.uid)"
                  }
                
              
            }
        }

Altogather the rules are:

{
  "rules": {
 
    
    "users":{
      
        "$user_id":{
          "conversations":{
            
              
              ".read": "auth != null && auth.uid == $user_id",
              "$hash":{
                ".write": "auth != null"
              }
              
            }
          }
      },
      
      "conversations":{
          "$conversation_name":{

                ".read": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",  
                ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",
                "messages":{
                    "$hash": { 
                      ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)"
                      //".validate": "auth.uid == newData.child('fromUserId').val()"
                    }
                  },
                "members":{
                   ".write": "true", //"!data.exists()",
                   ".validate": "newData.hasChild(auth.uid)"
                  }
                
              
            }
        }
  }
}

Now we will add a quick proof of concept demo chat app using js and html.

Lets do auth0 sign in first:

signin.html

http://cdn.auth0.com/w2/auth0-6.7.js
https://code.jquery.com/jquery-1.12.3.min.js

https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js


<script type="text/javascript">
 var auth0 = new Auth0({
 domain: '<your auth0 >.auth0.com',
 clientID: '<your client id>',
 callbackURL: '/signin.html',
 callbackOnLocationHash: true
 });


 auth0.login({ popup: true }, function(err, profile, id_token, access_token, state, refresh_token) {
 if (err) {
 // Handle the error!
 alert(err)
 return;
 }
 $.cookie("auth0token", id_token);

var options = {
 id_token: id_token, // The id_token you have now
 api: 'firebase', // The type of app (Auth0 can generate multiple token formats)
 scope: "openid name email" // default: openid. Add any other scopes you need, i.e. "openid {scope1} {scope2}"
 };

auth0.getDelegationToken(options, function (err, delegationResult) {
 // Call your API using delegationResult.id_token
 console.log(err)
 $.cookie("firebasetoken", delegationResult.id_token);

$(location).attr('href', 'minchat.html')


 });


 // refresh_token is sent only if offline_access is set as a scope
 });

</script>
</pre>

Now we will write our client:

minichat.html
<pre>
<html><head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <meta name="robots" content="noindex, nofollow">
 <meta name="googlebot" content="noindex, nofollow">

//code.jquery.com/jquery-1.11.0.js
 <link rel="stylesheet" type="text/css" href="/css/result-light.css">
 <link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
 
 
 
 //netdna.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js
 https://cdn.firebase.com/js/client/2.2.1/firebase.js

https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js
 


<style type="text/css">
 .tabs-left {
 border-bottom: none;
 padding-top: 2px;
 }

.tabs-left {
 border-right: 1px solid #ddd;
 }

.tabs-left>li {
 float: none;
 margin-bottom: 2px;
 }

.tabs-left>li {
 margin-right: -1px;
 }

.tabs-left>li.active>a,
 .tabs-left>li.active>a:hover,
 .tabs-left>li.active>a:focus {
 border-bottom-color: #ddd;
 border-right-color: transparent;
 }

.tabs-left>li>a {
 border-radius: 4px 0 0 4px;
 margin-right: 0;
 display: block;
 }
 .bubble{
 width: 74%;
 padding: 4px;
 border-radius: 4px;
 }
 .you{
 background-color: skyblue;
 float: left;
 text-align:left;
 }
 .me{
 background-color: orange;
 float: right;
 text-align: right;
 }

</style>

<title>In app Chat</title>


<script type="text/javascript">//<![CDATA[
$(window).load(function(){

var FirebaseChat = (function(){
 function FirebaseChat() {
 this.firebase = new Firebase('https://jobcue.firebaseio.com/');
 }
 var instance;
 return {
 get: function(){
 if (instance == null) {
 instance = new FirebaseChat()
 instance.uid = instance.firebase.getAuth().uid

var token = String($.cookie("firebasetoken") );

instance.firebase.authWithCustomToken(token, function(error, result) {
 if (error) {
 console.log("Authentication Failed!", error);
 } else {
 console.log("Authenticated successfully with payload:", result.auth);

console.log("Auth expires at:", new Date(result.expires * 1000));

// alert( JSON.stringify (firebase.getAuth()) )
 }
 });


 // Hide the constructor so the returned objected can't be new'd...
 instance.constructor = null;
 }
 return instance;
 },
 uid: function(){
 return get().getAuth().uid;
 }
 };
})();

function User(id){
 this.id = id;

this.firebaseRef = FirebaseChat.get().firebase.child('users').child(this.id) 
}

User.prototype.subscribe = function(conversation_name){
 
 c={}
 c.subject = conversation_name
 c.last_read = Firebase.ServerValue.TIMESTAMP
 c.added_by = FirebaseChat.get().uid
 c.added_at = Firebase.ServerValue.TIMESTAMP
 alert(conversation_name)
 this.firebaseRef.child('conversations').child(conversation_name).set(c)

};

User.prototype.getId = function(){return this.id };

User.prototype.read = function(conversation_name){
 c={}
 //c.subject = conversation_name


this.firebaseRef.child('conversations').child(conversation_name).child('last_read').set(Firebase.ServerValue.TIMESTAMP) 
};
User.prototype.subscriptions = function(){
 this.firebaseRef.child('conversations') 
};


User.prototype.onNewSubscription = function(callback){

this.firebaseRef.child('conversations').on('child_added', 
 function(conversation_snap){

callback( conversation_snap.key() ) 
 })

};

function Conversation(name){
 this.name = name
 this.firebaseRef = FirebaseChat.get().firebase.child('conversations').child(this.name)
}

Conversation.prototype.create = function(participants){
 firebaseRef = this.firebaseRef.child('members')

participants.forEach(function(user){

var u={}
 u.added_at = Firebase.ServerValue.TIMESTAMP
 u.added_by = FirebaseChat.get().uid

firebaseRef.child( user.getId() ).set(u) 
 })

};


Conversation.prototype.sendMessage = function(message){
 var m = {}
 m.message = message
 m.sent_at = Firebase.ServerValue.TIMESTAMP
 m.fromUserId = FirebaseChat.get().uid

this.firebaseRef.child('messages').push(m)
}


Conversation.prototype.onNewMessage = function(callback){
 this.firebaseRef.child('messages').on('child_added', 
 function(new_message_snap){ 
 callback(new_message_snap.val() ) 
 } )
}


var me = new User( FirebaseChat.get().uid );

me.onNewSubscription(function(new_conversation){
 addPeople(new_conversation)

var conversation = new Conversation(new_conversation)
 conversation.onNewMessage(function(new_message){
 addMessage(new_conversation, new_message)

})
})


$('#messageInput').keypress(function(e) {
 if (e.keyCode == 13) {


 var conversation = new Conversation($('.active').find('.name').text())

conversation.sendMessage($('#messageInput').val())

}
});

var n = 7;

function addPeople(conversation_list) {
 $("#conversations").append(
 '<li ><a href="#' + conversation_list + '" data-toggle="tab"><span class="name">' + conversation_list + '</span></a></li>'
 );

$("#individual_conversations").append(
 '<div class="tab-pane" id="'+conversation_list+'"><div style="width:100%">'+conversation_list+'</div></div>'
/* 
 '<div class="chat" data-chat="person' + (n++) + '" id="' + conversation_list.key() +
 '"><div class="conversation-start"></div>'
*/
 );
 bind();
}

function addMessage(conversation_list, msg) {
 $("#" + conversation_list).append(

'<div class="bubble ' + (msg.fromUserId == FirebaseChat.get().uid ? 'you' : 'me') + '"><span><b>'+
 (msg.fromUserId == FirebaseChat.get().uid ? 'you: ' : '')
 +'</b>' +
 msg.message+
 '</span><b>'+
 (msg.fromUserId == FirebaseChat.get().uid ? '' : ' :'+(msg.fromUserId))+
 '</b></div>'


 );


}

function bind(){
$("#conversations li a").on("click", function(){
 var conversation =$(this).find(".name").text(); 
 if(me !=null){
 me.read(conversation)
 } 
})
}


$("#newConv").on("click", function(){
 var c = $("#newConversationInput").val();
 var u = $("#toUserInput").val();
 var m =$("#newMessageInput").val();

if(me !=null){
 
 var auser = new User(u);

var con = new Conversation(c)
 con.create( [me, auser] )
 con.sendMessage(m);

me.subscribe(c);
 
 auser.subscribe(c);


 } 
})

});//]]>


</script>


</head>

<body>

<div class="col-sm-6">
 <div class="row">
 <h3>Messages
 <!-- Button trigger modal -->
 <button class="btn btn-primary btn-sm pull-right" data-toggle="modal" data-target="#myModalNorm">
 New Message
 </button>

</h3>

<hr>
 <div class="col-xs-4">
 <!-- required for floating -->
 <!-- Nav tabs -->
 <ul class="nav nav-tabs tabs-left sideways" id="conversations">
 <li class="center">Discussion </li>
 <!--li class="active"><a href="#home-v" data-toggle="tab">Home</a></li-->
 </ul>

</div>

<div class="col-xs-8">
 <!-- Tab panes -->
 <div class="tab-content" id="individual_conversations">

<!--div class="tab-pane active" id="home-v">Home Tab.</div!-->
 </div>


 </div>
 </div>


<div class="row">
 <div class="col-xs-4">SEND MESSAGE:</div>
 <input class="col-xs-8" id="messageInput" placeholder="write message and press ENTER">
</div>



</div></body></html>

<!-- Modal -->
<div class="modal fade" id="myModalNorm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
 <div class="modal-dialog">
 <div class="modal-content">
 <!-- Modal Header -->
 <div class="modal-header">

<h4 class="modal-title" id="myModalLabel">
 New Message
 </h4>
 </div>

<!-- Modal Body -->
 <div class="modal-body">


 <div class="form-group">
 <input class="form-control" type='text' id='newConversationInput' placeholder='Conversation'>

<input class="form-control" type='text' id='toUserInput' placeholder='to'>
 <textarea class="form-control" type='text' id='newMessageInput' placeholder='Message'></textarea>

<button id="newConv" type="submit" class="btn btn-primary">Submit</button>

<button type="button" class="btn btn-primary" data-dismiss="modal">
 Close
 </button>
 
 </div>


 </div>
 </div>
 </div>

So we are ready to go. go to and it will redirect to after signing in.

setup nodejs api server powered by passport auth0 strategy

We are using auth0 for our nodejs project, I would say it has made our life significantly better. Now we have considerably less burden on authentication process. As probably I was new in node js world it took me quite a time to figure out how to actually collect authentification token from auth0 mainly because they are using quite different term for their token than what I am used to, and I could not find a good straight forward tutorial on how to do that (I am not a great reader, I just read their example codes and it was not there). So here is how I did it:

In my setup-passport.js file I have got following:

var passport = require('passport');
var Auth0Strategy = require('passport-auth0');

    var models = require('./models/index');


var strategy = new Auth0Strategy({
    domain:       'x.auth0.com',
    clientID:     'xxx',
    clientSecret: 'xxxxxxx',
    callbackURL:  'http://localhost:3000/callback'
  }, function(accessToken, refreshToken, extraParams, profile, done) {
    // accessToken is the token to call Auth0 API (not needed in the most cases)
    // extraParams.id_token has the JSON Web Token
    // profile has all the information from the user

    //may like to create new user here;
    console.log({extra_params: extraParams});

);

passport.use(strategy);

// This is not a best practice, but we want to keep things simple for now
passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(user, done) {
  done(null, user);
});

module.exports = strategy;

And I have this at my app.js I have added following:

var passport = require('passport');

// This is the file we created in step 2.
// This will configure Passport to use Auth0
var strategy = require('./setup-passport');

// Session and cookies middlewares to keep user logged in
var cookieParser = require('cookie-parser');
var session = require('express-session');



app.use(cookieParser());
// See express session docs for information on the options: https://github.com/expressjs/session
app.use(session({ secret: 'YOUR_SECRET_HERE', resave: false,  saveUninitialized: false }));
//...
app.use(passport.initialize());
app.use(passport.session());


/*
// Auth0 callback handler
app.get('/callback',
passport.authenticate('auth0', { failureRedirect: '/url-if-something-fails' }),
function(req, res) {
if (!req.user) {
throw new Error('user null');
}
res.send({token: req.user});
//res.redirect("/user");
});
*/

app.get('/callback', function(req, res, next ){
passport.authenticate('auth0', function(err, user, info) {
if (err) { return next(err) }

return res.json( { credentials: info, user: user })

})(req, res, next);
});

My auth.js looks like:

var dotenv = require('dotenv');
dotenv.load();

var jwt = require('express-jwt');

module.exports =  jwt({
    secret: new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'),
    audience: process.env.AUTH0_CLIENT_ID
  });

Routes that needs authentication looks like this:

var express = require('express');
var router = express.Router();
var model = require('../models/index');


var authenticate = require("../auth")

/* GET users listing. */
router.get('/', authenticate, function(request, response, next) {

    model.User.find({
        where:{ 
            providerId: request.user.sub,
         },

    }).then(function(user) {        
        response.send(user);
    });  
});

Here I go, I have got a functional authentication using passport auth0 strategy.

The “id_token” that we get from /callback is our authentication token.

$ token = "your id_token from /callback" 
$ curl -v -X GET  -H "Content-Type: application/json" -d '{}' http://127.0.0.1:3000/users -H "Authorization: Bearer $token"

[Code snippet] Django rest framework social oauth2 user sign up and sign in

At my serializers.py I have got the following:

from django.contrib.auth.models import User
from rest_framework import serializers
 
 
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email', 'password')

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

at my views.py I have got the following:

import serializers
from rest_framework.decorators import permission_classes, api_view
from rest_framework.permissions import AllowAny
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework import status
import json
from django.http import JsonResponse

from oauth2_provider.models import Application, AccessToken

@permission_classes((AllowAny,))
@csrf_exempt
@api_view(['POST'])
def create_auth(request, format=None):
    if request.user.is_authenticated():
        return Response({"already_registered": "User with that username has already registered"}, status=701)
    
    data = request.data
    print data

    
    serializer = UserSerializer(data=data, partial=True)
    if serializer.is_valid():
        u=serializer.save(username= data.get(u'username') )
        application=Application.objects.create(user=u, client_type="public", authorization_grant_type="password",name="general")
        client_id = application.client_id #call the url to get your tokens, use urllib or something similar
        client_secret = application.client_secret
        return JsonResponse({'client_id': client_id, 'client_password' : client_secret}, status=201)
    else:
        return JsonResponse({'errors': serializer.errors}, status=400)

I have added following at my urls.py

urlpatterns = patterns(
    '',
    url(r'^register/', 'social_app.views.create_auth'),
    url(r'^auth/', include('rest_framework_social_oauth2.urls')),
)

Testing:

 
sadaf2605@pavilion-g4:~$ curl -X POST -H "Content-Type: application/json" -d '{"email":"boba@athingy09876.com", "password":"p4ssword", "username": "user100"}' http://localhost:8000/register/

returns:

{"client_password": "EjQKMCAGmsUEm3L26uO7XSKnrZZVSVBQJUuvqfwi63pRB7d5y3ndlbZV0cBgQU7t3lCy078DS0FLqhaYoe9JZF0cQCIAgFKo7lfYU3npP7Eyv1PLk2eLPRnD3lF3OUUP", "client_id": "JhbwqqvE34vVjWiuMPnkV1eE636QQ3SzyQXLjmgs"}
sadaf2605@pavilion-g4:~$ curl -X POST -d "client_id=JhbwqqvE34vVjWiuMPnkV1eE636QQ3SzyQXLjmgs&client;_secret=EjQKMCAGmsUEm3L26uO7XSKnrZZVSVBQJUuvqfwi63pRB7d5y3ndlbZV0cBgQU7t3lCy078DS0FLqhaYoe9JZF0cQCIAgFKo7lfYU3npP7Eyv1PLk2eLPRnD3lF3OUUP&grant;_type=password&username;=user100&password;=p4ssword" http://localhost:8000/auth/token
{"access_token": "bssEYlDNaXefq8TPNRuu8oLolqYJp2", "token_type": "Bearer", "expires_in": 36000, "refresh_token": "fankCVPC3P84pQWI5oWOIhtWLCky4w", "scope": "read write"}

Activeadmin semantic input partial and f.inputs togather

in activeadmin form when I do:

  form(:html => { :multipart => true })  do |f|
    f.inputs
  end

it shows all the fields nicely, and when it comes to belongs_to field it shows them as a collection but I want to replace this collection with a template of my belongs_to field.
Now if I want to use custom belongs_to field I can’t use the power of f.inputs because it will cause repetition. So what can we do now?

Well, currently using this as a solution:
at my “helper/active_admin_helper”:

	def form_inputs_for(m)
		columns=m.columns.map{|c| c.name}
		columns=columns.select{|s| !(s.end_with?"_id" or s.end_with?"_at" or s=="id") }
		columns.each do  |a|
        	input a
      	end
    end

and at form:

ActiveAdmin.register ModelClass do
  require "helper/active_admin_helper"
      form(:html => { :multipart => true })  do |f| 
        f.inputs do
          render "admin/product", f:f
          form_inputs_for(ModelClass)
        end
      end
end

Let me know if you have better solution, in fact I am still looking for one…

RabbitMQ & celery demo using image processing app on flask

Celery is a distributed system for processing messages on a task queue with a focus on real-time processing and support for task scheduling. When we have to run an expensive function that keeps user waiting for like “forever”, it is always better to use something like celery. In this blog we will be writing a face detection web app using flask, python-opencv and celery.

Before I can tell something, let me share a flask code snippet with you:

from time import sleep

@app.route("/")
def hello():
    sleep(10) # <---what would you see in this 10s?
    return "Hello World!"

Can you tell me what would you see in first 10s while we run our flask app? I know the answer, before getting the response it will keep the user waiting for 10s. We don’t love to wait 10s. We are so impatient, we want everything instantly thats the motivation that we have in modern computing. But life is cruel, we can’t get everything instantly, we understand that but our users DO NOT understand this simple truth. So we do what, we will try to sell them a feeling that we are working instantly, at least it is not taking forever to load. So we need to get over from that sleep block. How would we do that, that’s what I am going to discuss in this blog with a real life image processing app in flask.

Obviously in life we don’t need to write “sleep” to make our code run slower. We had to write plenty of function that makes our life slower. In this blog we will discuss we will be writing an application that enables user to upload a picture and we will help them to detect faces. So what is the function we have this face detection function which is very expensive. It takes almost 3-10s on my machine to detect the face of my favourite actress. Let me share my code:

#server.py

__author__ = 'sadaf2605'


import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename

import face_detect
from os.path import basename


UPLOAD_FOLDER = '/home/sadaf2605/flask_celery_upload_image/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


def allowed_file(filename):
    return '.' in filename and 
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        import time
        start_time = time.time()
        file = request.files['file']

        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)

            base,ext=os.path.splitext(filename)


            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            face_detect.detect(os.path.join(app.config['UPLOAD_FOLDER'], filename),os.path.join(app.config['UPLOAD_FOLDER'], base+"-face"+ext))

            print "--- %s seconds ---" % str (time.time() - start_time)
            return redirect("/")
            return redirect(url_for('uploaded_file',
                                    filename="facedetect-"+filename))

    from os import listdir
    from os.path import isfile, join
    htmlpic=""
    for f in sorted(listdir(UPLOAD_FOLDER)):
        if isfile(join(UPLOAD_FOLDER,f)):
            print f
            htmlpic+="""
            
                
            
                """

    return '''
    
    

    
    Upload new File
    

Upload new File

'''+htmlpic from flask import send_from_directory @app.route('/uploads/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) from werkzeug import SharedDataMiddleware app.add_url_rule('/uploads/', 'uploaded_file', build_only=True) app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { '/uploads': app.config['UPLOAD_FOLDER'] }) if __name__ == "__main__": app.debug=True app.run()

#face_detect.py

import numpy as np
import cv2


face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

@app.task
def detect(src_img,dest_img):
    img = cv2.imread(src_img)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, 1.3, 1)
    for (x,y,w,h) in faces:
        cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),5)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]


    cv2.imwrite(dest_img, img)

You can test this app running using

python server.py

But we don’t want our user to wait 10s to see the next page. So we will use celery and rabbitmq to help us. First of all lets install RABBITMQ and CELERY.

To install rabbitMq we will use aptitude because it installed all its dependency in the way, if you don’t have aptitude installed then:

sudo apt-get install aptitude

Now its time to install rabbitmq server

 sudo aptitude install rabbitmq-server

Now we will create user and server for rabbitmq.

sudo rabbitmqctl add_user rabbit_user password
sudo rabbitmqctl add_vhost /app_rabbit

we will set permission for our user to do everything

sudo rabbitmqctl set_permissions -p /app_rabbit rabbit_user ".*" ".*" ".*"

Now we need to restart rabbit server, so that the change gets implemented

sudo /etc/init.d/rabbitmq-server stop
sudo /etc/init.d/rabbitmq-server start

Now we will install celery:

pip install celery

Now we need to configure celery, and celery provides few decorator functions like @tasks to achieve our goal. Rabbitmq is default for celery. Now we need to know that celery communicate via broker url using a different port. We want to enqueue our image processing tasks, so we can define it in face_ditect.py but it will be better if we can put it in our server.py as it is the entry point… but whatever for now!

import numpy as np
import cv2

from celery import Celery

app= Celery(broker='amqp://rabbit_user:password@localhost:5672//app_rabbit' )


face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

@app.task
def detect(src_img,dest_img):
    img = cv2.imread(src_img)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(gray, 1.3, 1)
    for (x,y,w,h) in faces:
        cv2.rectangle(img,(x,y),(x+w,y+h),(255,0,0),5)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]


    cv2.imwrite(dest_img, img)

Now it won’t change your life radically, because we are not using the decorator functions that celery provided us. To put that task in queue we need use delay function of decorator function. so we need to call detec_image.delay(src_img,dest_image) and we actually need to keep our celery server running other wise it will only put it in queue and wait for the server to run. In -A parameter of celery we need to mention which file the decorator functions are located.

To run celery server

celery worker -A detect_face -l INFO

So now finally we can change our server.py

__author__ = 'sadaf2605'


import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename

import face_detect
from os.path import basename


UPLOAD_FOLDER = '/home/sadaf2605/flask_celery_upload_image/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


def allowed_file(filename):
    return '.' in filename and 
           filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        import time
        start_time = time.time()
        file = request.files['file']

        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)

            base,ext=os.path.splitext(filename)


            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            face_detect.detect.delay(os.path.join(app.config['UPLOAD_FOLDER'], filename),os.path.join(app.config['UPLOAD_FOLDER'], base+"-face"+ext))

            print "--- %s seconds ---" % str (time.time() - start_time)
            return redirect("/")
            return redirect(url_for('uploaded_file',
                                    filename="facedetect-"+filename))

    from os import listdir
    from os.path import isfile, join
    htmlpic=""
    for f in sorted(listdir(UPLOAD_FOLDER)):
        if isfile(join(UPLOAD_FOLDER,f)):
            print f
            htmlpic+="""
            
                
            
                """

    return '''
    
    

    
    Upload new File
    

Upload new File

'''+htmlpic from flask import send_from_directory @app.route('/uploads/') def uploaded_file(filename): return send_from_directory(app.config['UPLOAD_FOLDER'], filename) from werkzeug import SharedDataMiddleware app.add_url_rule('/uploads/', 'uploaded_file', build_only=True) app.wsgi_app = SharedDataMiddleware(app.wsgi_app, { '/uploads': app.config['UPLOAD_FOLDER'] }) if __name__ == "__main__": app.debug=True app.run()

So after uploading now in the front page the picture won’t show up you will need to refresh it couple of time after 5-6s to show up. Keep on refreshing and you may like to send me a pull request at: https://github.com/sadaf2605/facedetection-flaskwebapp-rabbitmq

Testing rails app with RSpec, FactoryGirl, Capybara, Selenium & Headless

In this blog I don’t have any intention to argue or discuss the necessity of Test Driven Development (TDD), but indeed without any doubt I believe testing is necessary. The other day I was watching a talk by Kent Beck where he said “if you don’t find Testing necessary for your code, then don’t do that. Whats the point in arguing about it?” I am also believer of that, as he said testing is all about gaining confidence. Now in this blog I am putting it together how did I use Rspec , Capybara , Selenium to gain this “confidence”.

RSpec has a different repository for rails (https://github.com/rspec/rspec-rails) and a different gem. To put RSpec in project we need to put gem rspec-rails in Gemfilelike this:

group :development,:test do gem
'rspec-rails', '~> 3.0'
end

then do a:

bundler install

Now we need to run a generator command:

rails generate rspec:install

Which basically generates following files:

spec/spec_helper.rb
spec/rails_helper.rb

Later in this blog we will configure it. Now we will write some RSpec tests. RSpec tests can be for many things and of many type. It can be for models, it can be for controllers, it can be for features and so on. There are generator commands to help you creating files.

When I run:

rails generate rspec:features home_page

It creates spec/features/home_page_spec.rb. As you see, every rspec files end with . So when we run bundle exec rspec _spec.rb it finds all these files and test them, we can also run:
bundle exec rspec spec/features

Which will only run feature test. We can also do:

bundle exec rspec spec/features/home_page_spec.rb:2

which will run test of that line until it ends. Now time to write some tests. Before writing test one thing we always need to keep in mind, in every test case we will test only one feature and we will focus only on that feature. (Maybe in later blog I will describe mock and stubs two cool features). If we don’t focus on one feature then it will take more time to debug our test case then writing the patch.

In our RSpec block we will be using describe and it methods. its are the functionality tests and a describe can contain a lot of its. A describe block can hold bunch of it blocks. In another word given/let calls are used at the top of a feature/describe/contextblock and apply to all contained feature/describe/context or scenario/it blocks.

in

spec/features/home_page_spec.rb
require 'rails_helper'
RSpec.feature "home page", type: :feature do
describe "navigation" do
it " shows me menu" do
get "/"
#do testing
assert_select ".navbar", :text => "home"
end
end
it "shows title" # it shows pending because it does not have do block
end

There are alias methods, and other way to write, for an instance feature is an alias for describe …, :type => :feature , background is an alias for before , scenario for it , and given/given! aliases for let/let! , respectively and so on.

We can see that RSpec by default deals with requests like get , post , it is not a browser a user will use see the product. But the main point is it does not have js support and modern website is full of javascripts. So what can I do? We will be using Capybara to mimic browser. We will add following gem to our Gemfile before doing

bundler install

:

gem 'capybara'

Now it is time to re write our test code. We need to add require ‘capybara/rails’ in our spec file.

require 'capybara/rails'
require 'rails_helper'
RSpec.feature "home page", type: :feature do
describe "navigation" do
it " shows me menu" do
visit "/"
expect(page.find('.nav-bar')).to have_content("brand") # page find search for css path
end
end
end

A Capybara cheat sheet which I found to be very useful can be found and here : https://gist.github.com/zhengjia/428105

Now we will integrate:

gem "FactoryGirl"

FactoryGirl is awesome tool to automate model entry creation. Suppose we have a BlogPost model which has many Catogories and one Blogger . In real life this is just a simple model will look like this when we write test case:

blog_post=BlogPost.new({title:"this is title",body:"this is body"})
blog_post.category<<Category.create(name:"technology")
blog_post.blogger=User.create(username="sadaf",password="password",rewritepassword="password")
blog_post.save()

For each post we will need to write this many line of code, and every post will need to have hand written title and body which is tiresome. FactoryGirl solves this problem. We will define Factories in support/factories.rb :

FactoryGirl.define do #active admin factory :admin_user, :class => AdminUser do
sequence(:email){|n| "email#{n}@example.com"}
password "password"
password_confirmation "password"
end
factory :category, :class => Category do
name "category name"
end
factory :blog_post, :class => BlogPost do
association :blogger, factory: :admin_user
sequence(:title){|n| "this is super cool title #{n}"}
sequence(:title){|n| "this is" + " super duper " * n + " body."}
description "static description"

factory :blog_post_with_category do
after(:create) do |post|
create(:category, blog_post: post)
end
end
end
end

so to create AdminUser , using factory_girl we will do:

FactoryGirl.create(:admin_user)
1
FactoryGirl.create(:admin_user)
Every time we call above code it creates different email address, email1@example.com, email2@example.com .., but password remains the same for all cases as it is not in sequence block. BlogPost has a association. and when we will do FactoryGirl.create(:blog_post) it will also create admin_user to satisfy its need, so we don’t need to write it separately. And to create blog_post with category we will need to do following:

blog_post = create(:blog_post_with_category)

Life is pretty easy right now.

....
it " shows me sub-menu on parent-menu click" do
visit "/"
blog_post = create(:blog_post_with_category
expect(page).to have_content(blog_post.title)
end
...

Now we may like to test our beautiful nested navbar is working or not, which is beautiful because it is manipulated by javascript. To do that we need to tag our it or describe block with “js”, which is very simple just add js:true as it or describes parameter and it is done. But before we write our test we will need to add another gem for our webdriver.

gem 'selenium-webdriver'

Now we can write:

describe "navigation" do
....
it " shows me sub-menu on parent-menu click", js:true do
visit "/"
click("parent menu") #text of button
expect(page).not_to have_selector('.submenu',visible:true) # if visible or not
expect(page.find('.submenu')).to have_content("submenu")
end
...
end

So far we run bundle exec rspec we did not see any browser, but now we will see a browser popping up and popping down. Which can be annoying sometime. To solve this either we can use webkit instead of seleniumbut in this blog I will use another gem called headless which turns selenium into a ui less browser which is super cool.

gem 'headless'

then we need to install Xvfb which is as easy as typing

sudo apt-get install Xvfb

Then we need to modify my spec_helper.rb . We need to add following lines at the beginning of the file:

if ENV['HEADLESS'] == 'true'
# require 'rubygems'
require 'headless' # if face load error read below
headless = Headless.new
headless.start
at_exit do
exit_status = $!.status if $!.is_a?(SystemExit)
headless.destroy
exit exit_status if exit_status
end
end

This code is responsible to instantiate headless when we type: ENV=HEADLESS bundle exec rspec and to destroy it. Then in between configure block we need to put before and after blocks which changes browser to selenium for js:

RSpec.configure do |config|
....
config.before(:each) do |example|
if example.metadata[:type] == :request and example.metadata[:js]
Capybara.current_driver = :selenium
end
end
config.after(:each) do |example|
Capybara.use_default_driver
end
....
end

If you see problem loading headless gem.

LoadError: cannot load such file -- headless
from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
from /usr/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'

Then you can make headless gem directory executable to all user using chmod:

gem which headless

for me:

var/lib/gems/2.1.0/gems/headless-2.2.0/lib/headless.rb

And then copy the directory of gem before lib and chmod it to 655:

Customize django admin page from the scratch

In my last project with django, I had to modify and extend django admin page massively, there were cases when I had to wipe out curren admin page and create on my own. Usually people don’t go through this kind of trouble I guess, they just write their own model and view rather than admin page. That could possibly be a work around. But I decided not to do that because django admin provides a lot more thing and actually it felt to be the right way to do that. There were no straight forward tutorial that covers all that so here I go, I can guide you a little bit on your way.

 

Usually when we don’t define our own admin page, but instead we use admin of django.contrib.

Example:

from django.contrib import admin
admin.site.register(Question)

Now for our own admin page we will extend AdminSite.

class MySiteAdminSite(admin.AdminSite):
    site_header = 'This is my Custom Admin Site'

It will not come into action unless we add this to url.py:

import admin

urlpatterns = patterns('',

    # (r'^admin/', include(django.contrib.admin.site.urls)),
     url(r'^admin/', include(admin.my_admin_site.urls)),

)+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

 

Our admin page is working but there is no model registered to our admin page, so there is nothing to show.

class ArticleAdmin(admin.ModelAdmin):

    fields =('author','title' ,'slug','body', 'categories','cover')
    def change_view(self, request,object_id, form_url='', extra_context=None):
        extra_context = extra_context or {}
        extra_context["show_save_as_draft"] = True

        return super(ArticleAdmin, self).change_view(request,object_id, form_url, extra_context)

    prepopulated_fields = {'slug': ('title',) }

    def save_model(self, request, obj, *kwwargs):
        if not obj.id:
            obj.slug = slugify(obj.title)
            
        if request.user.is_superuser or request.user==obj.author:
            super(ArticleAdmin,self).save_model(request, obj, *kwwargs)
        else:
            obj.author=None
            super(ArticleAdmin,self).save_model(request, obj, *kwwargs)


    def queryset(self, request):
        qs = super(ArticleAdmin, self).queryset(request)

        if request.user.is_superuser:
            return qs
        else:
            return qs.filter(author=request.user)

my_admin_site.register(Article, ArticleAdmin)

I actually wrote quite a lot code at my ArticleAdmin, I am going to explain it now. Basically we override  change_view, save_model and queryset method of modelAdmin class. change_view is responsible for embeding edit or create new form. This is what change_form.html look like:  https://github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html

You can pass your variable via extra_context, for my case I passed show_save_as_draft. and I edited change_form.html and put my edited version of it at template/admin/app/change_form.html

At save model, I actually checked some permission and slugify the link. pretty straight forward.

Now queryset is pretty interesting, it usually shows all the articles, does not it? what if we want that when a certain user logs in you want him to show only his articles? This is exactly what I did with filter.

 

Now we have a working custom admin that shows stuff at its dashboard, we are yet to modify our admin page.

class StripeAdminSite(admin.AdminSite):

    def index(self, request, extra_context=None):
        extra_context = extra_context or {}
        extra_context["site_visited_by"] = 10000


        return super(StripeAdminSite, self).index(request,extra_context)


    def censor_article(self,request,id):
        try:
            article=Article.objects.get(id=id)
            if request.user.is_superuser or article.author==request.user:
                article.censored=True
                article.save()
        except Article.DoesNotExist:
            pass

        from django.http import HttpResponseRedirect
        return HttpResponseRedirect(request.META.get('HTTP_REFERER'))


    def get_urls(self):
        #self.admin_site.admin_view(self.approve_staff)
        urls = super(MyAdminSite, self).get_urls()
        from django.conf.urls import url
        my_urls = [
            url(r'^article/censor/(?P<id>w+)/$', self.censor_article),
        ]

        return my_urls + urls

 

Here, index is what is being displayed when you hit /admin.  Your current index looks like this: https://github.com/sehmaschine/django-grappelli/blob/master/grappelli/templates/admin/index.html

You can override it by putting your own index.html at /template/admin/index.html you can pass variables via extra_context parameter of index function.

I wrote a view fuction censor_article at admin class and added corresponding url at get_urls function. Thats basically it.

One workaroud to pass variables (context) to django admin submit_line.html template

Recently I had to modify django admin page massively, while trying to add a new button at add new model item at admin page I got into trouble, trouble was not to show the button, or get that button working but it was at variable passing. So in this blog I am going to describe, how did I solve it.

I am overriding this template submit_line.html:

{% load i18n admin_urls %}
{% if show_save %}{% if is_popup %}{% else %}{% endif %} {% trans 'Save' %}{% endif %} {% if show_save_as_draft %} {% endif %} {% if show_save_and_add_another %} {% trans 'Save and add another' %}{% endif %} {% if show_save_and_continue %} {% trans 'Save and continue editing' %}{% endif %} {% if show_delete_link %} {% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %} {% trans "Delete" %} {% endif %}

Here,

{{show_save_as_draft}}

is at out extra context of our ModelAdmin while it is showing:

#/home/sadaf2605/PycharmProjects/stripe/stripe/news/admin.py
class ArticleAdmin(admin.ModelAdmin):
    change_form_template = 'admin/news/change_form.html'
    def change_view(self, request,object_id, form_url='', extra_context=None):
        extra_context = extra_context or {}
        extra_context["show_save_as_draft"] = True
        return super(ArticleAdmin, self).change_view(request,object_id, form_url, extra_context)

Still

{{show_save_as_draft}}

is not showing up. This is the problem

To solve this problem I actually override a template tag that was responsible for showing buttons, basically that template tag was only keeping few selected context field. In this new tag I am keeping tags which are necessary for my app.

#stripe/stripe/news/templatetags/stripe_admin_tag.py
__author__ = 'sadaf2605'
from django import template
register = template.Library()
from django.contrib.admin.templatetags import admin_modify

@register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_line_row(context):
    context = context or {}
    ctx= admin_modify.submit_row(context)
    if "show_save_as_draft" in context.keys():
        ctx["show_save_as_draft"] = context["show_save_as_draft"]
    return  ctx

and then finally I need to override change_form.html as well, I need to replace:

{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}

with:

{% load stripe_admin_tag %}
{% block submit_buttons_bottom %}{% submit_ine_row %}{% endblock %}

/stripe/stripe/stripe/templates/admin/news/change_form.html

{% extends "admin/base_site.html" %}
{% load i18n admin_urls admin_static admin_modify %}

{% block extrahead %}{{ block.super }}

{{ media }}
{% endblock %}

{% block extrastyle %}{{ block.super }}{% endblock %}

{% block coltype %}colM{% endblock %}

{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}

{% if not is_popup %}
{% block breadcrumbs %}

{% endblock %}
{% endif %}

{% block content %}
{% block object-tools %} {% if change %}{% if not is_popup %} {% endif %}{% endif %} {% endblock %} {% csrf_token %}{% block form_top %}{% endblock %}
{% if is_popup %}{% endif %} {% if to_field %}{% endif %} {# WP Admin start #} {% if 0 %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %} {# WP Admin end #} {% if errors %}

{% if errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}

{{ adminform.form.non_field_errors }} {% endif %} {% block field_sets %} {% for fieldset in adminform %} {% include "admin/includes/fieldset.html" %} {% endfor %} {% endblock %} {% block after_field_sets %}{% endblock %} {% block inline_field_sets %} {% for inline_admin_formset in inline_admin_formsets %} {% include inline_admin_formset.opts.template %} {% endfor %} {% endblock %} {% block after_related_objects %}{% endblock %} {% load stripe_admin_tag %} {% block submit_buttons_bottom %}{% submit_line_row %}{% endblock %} {% if adminform and add %} (function($) { $(document).ready(function() { $('form#{{ opts.model_name }}_form :input:visible:enabled:first').focus() }); })(django.jQuery); {% endif %} {# JavaScript for prepopulated fields #} {% prepopulated_fields_js %}
{% endblock %}