29 May 2011

Examples on how to use cl-odesk

First of all you need to get keys to use oDesk API.

Let's load cl-odesk. There are 2 ways: to use quicklisp (as of now, 29 May 2011, quicklisp version of cl-odesk has no all the features) or to do it by hand.
Quicklisp way:
(ql:quickload 'odesk)

By hand: fetch latest version from github. Than run at your repl:
(load "odesk.asd")
(require :odesk)


Now let's do some queries to odesk. First we need to define connection. You will need PUBLIC_KEY, PRIVATE_KEY and TOKEN. The last one you can get only by authenticating yourself in your favourite web browser. You can check the logic of this at restas-odesk. Pleas see routes.lisp file.

Let's imagine you already have that token.
We can create connection by instantiating API class:

(defparameter *test-connection*
(make-instance 'odesk:api-json
:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN"))


Here is macro that can ease your life a lot:

(with-odesk (:connection con
:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN")
(print con))


And another macro that can help too:

(connect-odesk (:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN"))


Now let's do request to get info about user.
1) If we go first way and created variable *test-connection*

CL-USER> (odesk:hr/get-user :connection *test-connection*)
("{\"server_time\":\"1306690187\",\"auth_user\":{\"first_name\":\"Dmitriy\",
\"last_name\":\"Budashny\",\"uid\":\"dbudashny\",\"mail\":\"dbudashny@odesk.com\",
\"messenger_id\":\"\",\"messenger_type\":\"\",\"timezone\":\"EET\",
\"timezone_offset\":\"10800\"},\"user\":{\"timezone\":\"UTC+02:00 Eastern Europe\",
\"status\":\"active\",\"timezone_offset\":\"10800\",
\"public_url\":\"https:\\/\\/www.odesk.com\\/users\\/~~d6114868dcf921c5\",
\"last_name\":\"Budashny\",\"email\":\"dbudashny@odesk.com\",\"reference\":\"1001150\",
\"id\":\"dbudashny\",\"is_provider\":\"1\",\"first_name\":\"Dmitriy\"}}")

2) If we go the second way and will use with-odesk macro.
a) subvariant with defining connection explicitly:

CL-USER> (odesk:with-odesk
(:connection my-con
:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN")
(odesk:hr/get-user :connection my-con))
("{\"server_time\":\"1306690187\",\"auth_user\":{\"first_name\":\"Dmitriy\",
\"last_name\":\"Budashny\",\"uid\":\"dbudashny\",\"mail\":\"dbudashny@odesk.com\",
\"messenger_id\":\"\",\"messenger_type\":\"\",\"timezone\":\"EET\",
\"timezone_offset\":\"10800\"},\"user\":{\"timezone\":\"UTC+02:00 Eastern Europe\",
\"status\":\"active\",\"timezone_offset\":\"10800\",
\"public_url\":\"https:\\/\\/www.odesk.com\\/users\\/~~d6114868dcf921c5\",
\"last_name\":\"Budashny\",\"email\":\"dbudashny@odesk.com\",\"reference\":\"1001150\",
\"id\":\"dbudashny\",\"is_provider\":\"1\",\"first_name\":\"Dmitriy\"}}")

b) subvariant with default connection key:

CL-USER> (odesk:with-odesk
(:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN")
(odesk:hr/get-user))
("{\"server_time\":\"1306690187\",\"auth_user\":{\"first_name\":\"Dmitriy\",
\"last_name\":\"Budashny\",\"uid\":\"dbudashny\",\"mail\":\"dbudashny@odesk.com\",
\"messenger_id\":\"\",\"messenger_type\":\"\",\"timezone\":\"EET\",
\"timezone_offset\":\"10800\"},\"user\":{\"timezone\":\"UTC+02:00 Eastern Europe\",
\"status\":\"active\",\"timezone_offset\":\"10800\",
\"public_url\":\"https:\\/\\/www.odesk.com\\/users\\/~~d6114868dcf921c5\",
\"last_name\":\"Budashny\",\"email\":\"dbudashny@odesk.com\",\"reference\":\"1001150\",
\"id\":\"dbudashny\",\"is_provider\":\"1\",\"first_name\":\"Dmitriy\"}}")


3) Third way is for those who want to create connection globally and then use it in the code with-out the need to use with-odesk macro:

CL-USER> (odesk:connect-odesk (:public-key "PUBLIC"
:secret-key "SECRET"
:api-token "TOKEN"))
#<ODESK:API-JSON {CB4EF11}>
CL-USER> (odesk:hr/get-user)
("{\"server_time\":\"1306690187\",\"auth_user\":{\"first_name\":\"Dmitriy\",
\"last_name\":\"Budashny\",\"uid\":\"dbudashny\",\"mail\":\"dbudashny@odesk.com\",
\"messenger_id\":\"\",\"messenger_type\":\"\",\"timezone\":\"EET\",
\"timezone_offset\":\"10800\"},\"user\":{\"timezone\":\"UTC+02:00 Eastern Europe\",
\"status\":\"active\",\"timezone_offset\":\"10800\",
\"public_url\":\"https:\\/\\/www.odesk.com\\/users\\/~~d6114868dcf921c5\",
\"last_name\":\"Budashny\",\"email\":\"dbudashny@odesk.com\",\"reference\":\"1001150\",
\"id\":\"dbudashny\",\"is_provider\":\"1\",\"first_name\":\"Dmitriy\"}}")

23 May 2011

Django Offline Messages on PyPI

Finally decided to add Django Offline Messages to PyPI. Was very wondered that it could be done so easy, just type

python setup.py register



And got the result in some seconds:
running register
warning: register: missing required meta-data: url
Registering django-offline-messages to http://pypi.python.org/pypi
Server response (200): OK

05 April 2011

Common Lisp bindings for oDesk API

Today I've released cl-odesk 0.1.0 and few days ago was also released restas-odesk 0.1.0.



UPD: You can find examples here.

07 February 2011

Django Offline Messages

There was a way in Django to leave a message in User.message_set, so the user will be able to see this message after login. But this storage is deprecated as of now and will be totally removed in Django 1.4. That forced me to create database storage for messages. Here it is: django-offline-messages.

To use it you have to add some options to your settings.py file:

  • Add 'offline_messages' to INSTALLED_APPS

  • Set MESSAGE_STORAGE to 'offline_messages.storage.OfflineStorageEngine'



If you want to create a message, just type:

from offline_messages.utils import create_offline_message

user = User.objects.get(username='dbudashny')
create_offline_message(user, "This is a message")

25 October 2010

PyCon Ukraine. How Emacs-user can hack oDesk and get a job :)


(defmacro hack-odesk ()
'(http-post "http://spreadsheets.google.com/formResponse?formkey=dGVtN19mWlpLUDA2THJVU1U5a1h4NlE6MQ&ifq"
(list (cons "entry.3.group" "Emacs")
(cons "entry.4.group" "Emacs")
(cons "entry.5.group" "Emacs")
(cons "entry.6.group" "Emacs")
(cons "entry.7.group" "Emacs")
(cons "entry.8.group" "Emacs")
(cons "entry.9.group" "Emacs")
(cons "entry.10.single" (read-from-minibuffer "Enter how we can contact you: "))
(cons "pageNumber" "0")
(cons "backupCache" "")
(cons "submit" "True"))
'utf-8
'(("User-Agent" . "Mozilla/5.0 (X11; U; Linux i686; uk; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10."))))

26 October 2009

Quick Guide to SNMP and Mnesia

There is a good documentation on SNMP, Mnesia and MIB Writing, but it's impossible to find any good document that will guide you step-by-step to understanding howto create SNMP System which you can fill from Mnesia. So let's start ...

Imagine, you need to write database for some company, and one of the tables, let's name it empTable (keep in mind that name of the table in Mnesia and name of the table in MIB-file must be the same) contains such attributes: Id, Name, Phone Number.

First, we are going to write small MIB file w/ name EmpMIB.mib:



EmpMIB DEFINITIONS ::= BEGIN

IMPORTS
DisplayString, RowStatus FROM SNMPv2-TC
OBJECT-GROUP FROM SNMPv2-CONF
OBJECT-TYPE, MODULE-IDENTITY, enterprises FROM SNMPv2-SMI;

empMib MODULE-IDENTITY
LAST-UPDATED "200910261250"
ORGANIZATION "Riot"
CONTACT-INFO "E-mail: dmitriy.budashny@gmail.com
Dmitriy Budashny"
DESCRIPTION
"Module for employees"
::= { enterprises 66666 }


emp OBJECT IDENTIFIER ::= { empMib 5 }

empTable OBJECT-TYPE
SYNTAX SEQUENCE OF EmpEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A table with information about employees."
::= { emp 1}

empEntry OBJECT-TYPE
SYNTAX EmpEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
""
INDEX { empDepNo, empName }
::= { empTable 1 }

EmpEntry ::=
SEQUENCE {
empDepNo INTEGER,
empName DisplayString,
empTelNo DisplayString,
empStatus RowStatus
}

empDepNo OBJECT-TYPE
SYNTAX INTEGER ( 1..65535 )
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"Number of department"
::= { empEntry 1 }

empName OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"The name of the employee"
::= { empEntry 2 }

empTelNo OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"The phone number"
::= { empEntry 3 }

empStatus OBJECT-TYPE
SYNTAX RowStatus
MAX-ACCESS read-write
STATUS current
DESCRIPTION
"Status ?!"
::= { empEntry 4 }

empGroups OBJECT IDENTIFIER ::= { empMib 6 }

empGroup OBJECT-GROUP
OBJECTS { empDepNo, empName, empTelNo, empStatus }
STATUS current
DESCRIPTION
"A collection of objects providing employee information"
::= { empGroups 1 }


END


Also we need company.erl file:



-module(company).

-export([init/0, insert_record/1]).

init() ->
mnesia:create_table([{name, empTable},
{snmp, [{key, {integer, string}}]},
{attributes, [key, telno, row_status]}]).

insert_record(Record) ->
Fun = fun() ->
mnesia:write(Record)
end,
mnesia:transaction(Fun).

Then we will create config files for snmp agent and snmp manager:


snmp/agent/conf/agent.conf:

{intAgentIpAddress, [127,0,0,1]}.
{intAgentUDPPort, 4000}.
{snmpEngineID, "agent's engine"}.
{snmpEngineMaxMessageSize, 484}.

snmp/agent/conf/community.conf:

{"public", "public", "initial", "", ""}.

snmp/agent/conf/standard.conf:

{sysName, "SNMP Quick Start HowTo Demo"}.
{sysDescr, "Erlang/OTP Agent"}.
{sysContact, "vances@motivity.ca"}.
{sysLocation, "5th Floor machine room, rack 21A"}.
{sysObjectID, [3,6,1,4,1,193,19]}. % {ericsson otp}
{sysServices, 72}.
{snmpEnableAuthenTraps, enabled}.

snmp/agent/conf/vacm.conf:

{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.%% vacmViewTreeFamilyTable in the SNMP-VIEW-BASED-ACM-MIB.
{vacmSecurityToGroup, v2c, "initial", "initial"}.
{vacmSecurityToGroup, usm, "initial", "initial"}.
{vacmAccess, "initial", "", any, noAuthNoPriv, exact, "restricted", "", "restricted"}.
{vacmAccess, "initial", "", usm, authNoPriv, exact, "internet", "internet", "internet"}.
{vacmAccess, "initial", "", usm, authPriv, exact, "internet", "internet", "internet"}.
{vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.

snmp/manager/conf/agents.conf:

{"simple_user", "otp agent", "public", [127,0,0,1], 4000, "agent's engine",
infinity, 484, v2, v2c, "initial", noAuthNoPriv}.

snmp/manager/conf/manager.conf:

{port, 5000}.
{address, [127,0,0,1]}.
{engine_id, "manager's engine"}.
{max_message_size, 484}.

snmp/manager/conf/users.conf:

{"simple_user", snmpm_user_default, undefined}.

And now the real fun begins:


dym@enfer:~/tmp/proga/erlang/mnesia_snmp$ mkdir EmpDB
dym@enfer:~/tmp/proga/erlang/mnesia_snmp$ erl -sname ms@enfer -mnesia dir '"./EmpDB"' -config snmp/
Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.7.3 (abort with ^G)
(ms@enfer)1> mnesia:create_schema([node()]).
ok
(ms@enfer)2> mnesia:start().
ok
(ms@enfer)3> c(company).
{ok,company}
(ms@enfer)4> company:init().
{atomic,ok}
(ms@enfer)5> application:start(snmp).

=ERROR REPORT==== 26-Oct-2009::15:46:52 ===
** Configuration error: [FRAMEWORK-MIB]: missing context.conf file => generating a default file

=INFO REPORT==== 26-Oct-2009::15:46:52 ===
[ snmp : agent : snmp_user_based_sm_mib : <0.95.0> ]
USM: Incomplete configuration. Generating empty usm.conf.
ok
(ms@enfer)6> application:which_applications().
[{snmp,"SNMP CXC 138 13","4.13.5"},
{mnesia,"MNESIA CXC 138 12","4.4.11"},
{stdlib,"ERTS CXC 138 10","1.16.3"},
{kernel,"ERTS CXC 138 10","2.13.3"}]

So you see, applications mnesia and snmp are running


(ms@enfer)7> snmpc:compile("EmpMIB", [{db, mnesia}]).
{ok,"./EmpMIB.bin"}
(ms@enfer)8> snmpa:load_mibs(["EmpMIB"]).
ok

As of now our table is empty, but we are going to change this:


(ms@enfer)9> company:insert_record({empTable, {1,"Dmitriy Budashny"}, "1201", 1}).
{atomic,ok}

Let's open another console:


dym@enfer:~$ snmpwalk -c public -v 2c localhost:4000 .1.3.6.1.4.1.66666
SNMPv2-SMI::enterprises.66666.5.1.1.1.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.2.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "Dmitriy Budashny"
SNMPv2-SMI::enterprises.66666.5.1.1.3.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "1201"
SNMPv2-SMI::enterprises.66666.5.1.1.4.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1

And then we'll add another guy with phone number 1305:


(ms@enfer)10> company:insert_record({empTable, {2,"Andrew Sidlyarenko"}, "1305", 1}).
{atomic,ok}

Run snmpwalk again:


dym@enfer:~$ snmpwalk -c public -v 2c localhost:4000 .1.3.6.1.4.1.66666
SNMPv2-SMI::enterprises.66666.5.1.1.1.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.1.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = INTEGER: 2
SNMPv2-SMI::enterprises.66666.5.1.1.2.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "Dmitriy Budashny"
SNMPv2-SMI::enterprises.66666.5.1.1.2.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = STRING: "Andrew Sidlyarenko"
SNMPv2-SMI::enterprises.66666.5.1.1.3.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = STRING: "1201"
SNMPv2-SMI::enterprises.66666.5.1.1.3.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = STRING: "1305"
SNMPv2-SMI::enterprises.66666.5.1.1.4.1.16.68.109.105.116.114.105.121.32.66.117.100.97.115.104.110.121 = INTEGER: 1
SNMPv2-SMI::enterprises.66666.5.1.1.4.2.18.65.110.100.114.101.119.32.83.105.100.108.121.97.114.101.110.107.111 = INTEGER: 1

PS: It's not complete guide about Mnesia or SNMP or MIBs. If you want more, I can recommend you some books/articles/sites:


Total Pageviews

Followers