product database project
Documentation - Brice Leroy

Setup

This section will explain how to setup the server and its dependancies on a Ubuntu platform. Ubuntu must be setup and running on the server. For instance, I'm using the version 8.04 on the production server:

brice@pdb:~$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 8.04.2
Release:	8.04
Codename:	hardy
You can download this version at ubuntu download page. Feel free to use an upgraded version, it shouldn't be an issue since Django sources are used. My user(brice) has sudo right access. First, make sure that your repository informations and your ubuntu are up to date:
brice@pdb:~$ sudo apt-get update
[sudo] password for brice: 
Get:1 http://security.ubuntu.com hardy-security Release.gpg [189B]
Hit http://archive.ubuntu.com hardy Release.gpg
...
Get:16 http://archive.ubuntu.com hardy-updates/universe Sources [45.5kB]                                                                                                                                               
Fetched 1329kB in 10s (129kB/s)                                                                                                                                                                                        
Reading package lists... Done
brice@pdb:~$ sudo apt-get dist-upgrade
Reading package lists... Done
Building dependency tree       
...
brice@pdb:~$

You should now be ready for the next step.

firewall

We will only need the SSH, HTTP and HTTPS open on our server. So lets close all the other port on the server. Edit a file named /etc/iptables.up.rules to put those iptables rules:

brice@pdb:~$ sudo -i
root@pdb:~# vi /etc/iptables.up.rules 
root@pdb:~# iptables-restore < /etc/iptables.up.rules 
root@pdb:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            
REJECT     all  --  anywhere             127.0.0.0/8         reject-with icmp-port-unreachable 
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www 
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:https 
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh 
ACCEPT     icmp --  anywhere             anywhere            icmp echo-request 
LOG        all  --  anywhere             anywhere            limit: avg 5/min burst 5 LOG level debug prefix `iptables denied: ' 
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
REJECT     all  --  anywhere             anywhere            reject-with icmp-port-unreachable 

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere            
root@pdb:~# 

Here is the iptables configuration:

# Generated by iptables-save v1.3.8 on Mon Oct 19 16:04:41 2009
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -d 127.0.0.0/255.0.0.0 -i ! lo -j REJECT --reject-with icmp-port-unreachable
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -j ACCEPT
COMMIT
# Completed on Mon Oct 19 16:04:41 2009

Now we need to setup this connection to start right before the network interface are loaded. To do so, you need to edit the /etc/network/interfaces and add the pre-up line just after iface lo inet loopback:

# Used by ifup(8) and ifdown(8). See the interfaces(5) manpage or
# /usr/share/doc/ifupdown/examples for more information.
# The loopback network interface
auto lo
iface lo inet loopback
pre-up iptables-restore < /etc/iptables.up.rules

# The primary network interface
# Uncomment this and configure after the system has booted for the first time
auto eth0
iface eth0 inet static
    address 174.143.153.5
    netmask 255.255.255.0
    gateway 174.143.153.1
    dns-nameservers 72.3.128.240 72.3.128.241
...

web server

Nginx will be used as a proxy and static file server, it will also manage the SSL certificate. since Django is not a web server, it's better to use a third part application to manage the communication. So lets install NGinX:

brice@pdb:~$ sudo apt-get install nginx
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following NEW packages will be installed:
  nginx
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
...
Unpacking nginx (from .../nginx_0.5.33-1ubuntu0.1_amd64.deb) ...
Setting up nginx (0.5.33-1ubuntu0.1) ...

brice@pdb:~$ 
We will now need to generate the CSR certificate to be able to sign ou SSL certificate. We will use openssl:
brice@pdb:~$ sudo apt-get install openssl
[sudo] password for brice: 
Reading package lists... Done
Building dependency tree       
...
Setting up openssl (0.9.8g-4ubuntu3.8) ...
brice@pdb:~$ openssl req -new -newkey rsa:2048 -nodes -out pdb_fulham_com.csr \
> -keyout pdb_fulham_com.key -subj \
> "/C=US/ST=California/L=Hawthorne/O=Fulham Co. Inc./CN=pdb.fulham.com"
Generating a 2048 bit RSA private key
........................................................................................................................................+++
....................+++
writing new private key to 'pdb_fulham_com.key'
-----
brice@pdb:~$ 
Once generated, I generated the SSL cerficate through godaddy and validated it by checking it@fulham.com (official fulham.com mail). The next step was the generation of the web certificate and placing it at the right place on the server config directory

brice@pdb:~$ sudo su
[sudo] password for brice: 
root@pdb:/home/brice# sudo cat pdb.fulham.com.crt gd_bundle.crt > /etc/nginx/pdb.fulham.com.crt
root@pdb:/home/brice# cp pdb_fulham_com.key /etc/nginx/                   

Now we will setup nginx site to enable the ssl.

root@pdb:/home/brice# rm /etc/nginx/sites-enabled/default 
root@pdb:/home/brice# vi /etc/nginx/sites-available/pdb.fulham.com

and here is the configuration for pdb.fulham.com:

server {
    # redirect every HTTP request to HTTPS
    listen   80;
    server_name  pdb.fulham.com;
    location / {
        rewrite ^ https://pdb.fulham.com$request_uri? permanent;
    }
}


server {
    listen 443;
    ssl on;
    ssl_certificate /etc/nginx/pdb.fulham.com.crt;
    ssl_certificate_key /etc/nginx/pdb_fulham_com.key;

    server_name pdb.fulham.com;

    access_log  /var/log/nginx/pdb.fulham.com.access.log;
    error_log  /var/log/nginx/pdb.fulham.com.error.log;

    #add_header           Front-End-Https    on;
    proxy_set_header X-Url-Scheme $scheme;

    location / {
        proxy_pass http://127.0.0.1:9000/;
        proxy_set_header X-Forwarded-Protocol "https";
        proxy_set_header  Host       $host;
        proxy_set_header  X-Real-IP  $remote_addr;
        client_max_body_size       3000m;
    }

    location /media/ {
        alias /home/brice/productdatabase/media/;
    }

    location /static/ {
        alias /home/brice/productdatabase/static/;
    }

    location /adminmedia/ {
        alias /usr/lib/python2.5/site-packages/django/contrib/admin/media/;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /var/www/nginx-default;
    }
}

Then we have to:

root@pdb:/home/brice# ln -s /etc/nginx/sites-available/pdb.fulham.com /etc/nginx/sites-enabled/pdb.fulham.com
root@pdb:/home/brice# ls -l /etc/nginx/sites-enabled/   
total 0
lrwxrwxrwx 1 root root 41 Oct 15 21:10 pdb.fulham.com -> /etc/nginx/sites-available/pdb.fulham.com
root@pdb:/home/brice# mkdir /var/log/nginx
root@pdb:/home/brice# /etc/init.d/nginx stop 
Stopping nginx: nginx.
root@pdb:/home/brice# /etc/init.d/nginx start
Starting nginx: nginx.

That's all for nginx, it should be now up and running on port 443 (https).

database server

MySQL V5 is used to store the data. A couple of other dependencies should come with it, so don't be surprised. The installation process will also ask you for a password. The password doesn't have to be too complicated because the dbd will only be accessible on localhost and an additionnal firewall will be setup to protect it.

root@pdb:/home/brice# sudo apt-get install mysql-server
Reading package lists... Done
Building dependency tree       
...
root@pdb:/home/brice# mysql -u root -p
mysql> create database pdb;
Query OK, 1 row affected (0.00 sec)

mysql> grant usage on *.* to pdb_user@localhost identified by 'your_password_here';
Query OK, 0 rows affected (0.00 sec)

mysql> grant all privileges on pdb.* to pdb_user@localhost;
Query OK, 0 rows affected (0.00 sec)

The database should now be accessible localy by the user pdb_user and the password you set. If you want you can check the db creation status with this command in the mysql console:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema | 
| mysql              | 
| pdb                | 
+--------------------+
3 rows in set (0.00 sec)

Now we will create a database and a user for this database.

django

We will install django framework using the official repository. to do it we need subversion to be installed. We will first leave the root mode for more security.

root@pdb:/home/brice# exit
exit
brice@pdb:~$ sudo apt-get install subversion
[sudo] password for brice: 
Reading package lists... Done
Building dependency tree       
...
brice@pdb:~$ svn co http://code.djangoproject.com/svn/django/trunk/ django-trunk 
A    django/LICENSE
A    django/django
...
 U   django
Checked out revision 11627.
brice@pdb:~$

Now that we have the repository downloaded in our Django, we have to install it. We will also need the python to mysql library, so Django can use MySQL.

brice@pdb:~$ sudo ln -s `pwd`/django-trunk/django /usr/lib/python2.5/site-packages/
brice@pdb:~$ sudo ln -s `pwd`/django-trunk/django/bin/django-admin.py /usr/local/bin
brice@pdb:~$ sudo apt-get install python-mysqldb
Reading package lists... Done
Building dependency tree       
Reading state information... Done
...
brice@pdb:~$ 

external app

The product database application is using differents application to work correctly. Those are django pluggable ones.

django_restapi

This API is used to provide access to the database through the protocol REST.

brice@pdb:~$ svn checkout http://django-rest-interface.googlecode.com/svn/trunk/ django-rest-interface 
A    django-rest-interface/__init__.py
A    django-rest-interface/django_restapi
A    django-rest-interface/django_restapi/__init__.py
...
brice@pdb:~$ sudo ln -s `pwd`/django-rest-interface/django_restapi /usr/lib/python2.5/site-packages/

Django rest interface is not including the user in the request. This patch is adding this functionality.

Index: authentication.py
===================================================================
--- authentication.py	(revision 81)
+++ authentication.py	(working copy)
@@ -62,6 +62,12 @@
             return False
         auth = auth.strip().decode('base64')
         username, password = auth.split(':', 1)
+
+        from django.contrib.auth import authenticate
+        user = authenticate(username=username, password=password)
+        if user is not None and user.is_active:
+            request.user = user
+
         return self.authfunc(username=username, password=password)
 
 def digest_password(realm, username, password):

Without this patch, 80% of the rest product unit test will fail. To apply the patch, save this in file named authentication.py in /django-rest-interface/django_restapi folder and run the following command:

brice@pdb:~/django-rest-interface/django_restapi$ patch authentication.py authentication.py.patch

django extension

Django extension provide all kind of small tool in addition to the django manager built in functionnalities. We will also install graphviz to be able to use the generated db schema.

brice@pdb:~$ sudo apt-get install graphviz python-pygraphviz
Reading package lists... Done
Building dependency tree       
...
brice@pdb:~$ hg clone http://hgsvn.trbs.net/django-command-extensions
brice@pdb:~$ cd django-command-extensions/
brice@pdb:~/django-command-extensions$ sudo python setup.py install
[sudo] password for brice: 
running install
...
running install_egg_info
Writing /usr/lib/python2.5/site-packages/django_extensions-0.4.egg-info
brice@pdb:~/django-command-extensions$ 

To generate a picture of the actual db schema use the following command

brice@pdb:~$ cd productdatabase
brice@pdb:~/productdatabase$ ./manage.py graph_models -a -g -o  ../documentation/db_schema.png
brice@pdb:~$ 

The picture should be available here : http://pdb.fulham.com/doc/db_schema.png

djangodblog

This library simplify error management in the website by storing all the differents exception raised by the software

brice@pdb:~$ sudo apt-get install git-core
Reading package lists... Done
Building dependency tree       
Reading state information... Done
...
brice@pdb:~$ git clone git://github.com/dcramer/django-db-log.git
Initialized empty Git repository in /home/brice/django-db-log/.git/
remote: Counting objects: 60, done.
remote: Compressing objects: 100% (42/42), done.
remote: Total 60 (delta 18), reused 47 (delta 14)
Receiving objects: 100% (60/60), 7.90 KiB, done.
Resolving deltas: 100% (18/18), done.
brice@pdb:~$ sudo ln -s `pwd`/django-db-log/djangodblog/ /usr/lib/python2.5/site-packages/

sorl.thumbnail

SORL is used to manage thumbnails easily inside django template. This library need image magick library to work and mercurial to be downloaded.
brice@pdb:~$ sudo apt-get install mercurial
Reading package lists... Done
Building dependency tree       
Reading state information... Done
...
brice@pdb:~$ hg clone https://sorl-thumbnail.googlecode.com/hg/ sorl
requesting all changes
adding changesets
adding manifests
adding file changes
added 362 changesets with 640 changes to 86 files (+5 heads)
30 files updated, 0 files merged, 0 files removed, 0 files unresolved
brice@pdb:~$ sudo ln -s `pwd`/sorl/sorl/ /usr/lib/python2.5/site-packages/ 

mail server

I use postfix as mail server. postfix has to be installed as an website internet server, the address I use is pdb.fulham.com

brice@pdb:~/productdatabase$ sudo apt-get install postfix
[sudo] password for brice: 
Reading package lists... Done
...
brice@pdb:~/productdatabase$

spawning

Spawning will multithread django which make it looks even more cool !

brice@pdb:~/productdatabase$ sudo apt-get install python-setuptools build-essential libssl-dev
[sudo] password for brice: 
Reading package lists... Done
Building dependency tree       
Reading state information... Done
...
brice@pdb:~/productdatabase$ sudo easy_install -U setuptools
brice@pdb:~/productdatabase$ sudo easy_install spawning
Searching for spawning
...
Finished processing dependencies for spawning
brice@pdb:~/productdatabase$ 

supervisor

In order to maintain the process as easy as possible, supervisor will be mastering django launches !

brice@pdb:~/productdatabase$ sudo easy_install supervisor 
Searching for supervisor
...
Finished processing dependencies for supervisor
brice@pdb:~/productdatabase$ sudo mkdir /var/log/supervisord/
brice@pdb:~/productdatabase$ 
Then you can create supervisord.conf and put this configuration inside. Remember to change the directory of pdb (here /home/brice/productdatabase) by yours:
[unix_http_server]
file=/tmp/supervisor.sock   ; (the path to the socket file)

[supervisord]
logfile=/var/log/supervisord/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB       ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10          ; (num of main logfile rotation backups;default 10)
loglevel=debug               ; (log level;default info; others: debug,warn,trace)
pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false              ; (start in foreground if true;default false)
minfds=1024                 ; (min. avail startup file descriptors;default 1024)
minprocs=200                ; (min. avail process descriptors;default 200)
user=root                 ; (default is current user, required if root)
childlogdir=/var/log/supervisord/            ; ('AUTO' child log dir, default $TEMP)

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL  for a unix socket

[program:pdb]
directory=/home/brice/productdatabase
command=/usr/bin/spawn  --factory=spawning.django_factory.config_factory settings --port=9000
process_name=%(program_name)s ; process_name expr (default %(program_name)s)
autostart=true                ; start at supervisord start (default: true)
autorestart=true              ; retstart at unexpected quit (default: true)
user=brice
stdout_logfile=/var/log/pdb_dev.log        ; stdout log path, NONE for none; default AUTO
stdout_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
stdout_logfile_backups=10     ; # of stdout logfile backups (default 10)
stdout_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)
stderr_logfile=/var/log/pdb_dev_error.log        ; stderr log path, NONE for none; default AUTO
stderr_logfile_maxbytes=1MB   ; max # logfile bytes b4 rotation (default 50MB)
stderr_logfile_backups=10     ; # of stderr logfile backups (default 10)
stderr_capture_maxbytes=1MB   ; number of bytes in 'capturemode' (default 0)

Make sur that /usr/local/bin/spawn is the right path (the command whereis spawn will show it) to your installation of spawning. To run spawning we will use an init script. Create /etc/init.d/supervisord

#!/bin/bash -e

supervisord=/usr/local/bin/supervisord
supervisorctl=/usr/local/bin/supervisorctl
name="supervisor"

[ -f $supervisord ] || exit 1
[ -f $supervisorctl ] || exit 1

RETVAL=0

start() {
     echo -n "Starting $name: "
     $supervisord
     RETVAL=$?
     [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$name
     echo
     return $RETVAL
}

stop() {
     echo -n "Stopping $name: "
     $supervisorctl shutdown
     RETVAL=$?
     [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$name
     echo
     return $RETVAL
}

case "$1" in
         start)
             start
             ;;

         stop)
             stop
             ;;

         restart)
             stop
             start
             ;;
esac

exit $REVAL

Again, make sure that supervisord and supervisorctl are the right places ! Then we can add this script by adding it to the correct init script, so the server will turn on by itself if the server is restarted

brice@pdb:~$ sudo chmod +x /etc/init.d/supervisord
brice@pdb:~$ sudo update-rc.d supervisord start 89 S .
brice@pdb:~$ sudo /etc/init.d/supervisord start

product database software

It's now time to install our main software. For now, there is no central repository for the product database server, so the process will be a more complicated - thanks to Peter - and will not explain how to do it here because it doesn't make sense to copy a repository instead of cloning it! Next step is the setup file settings_local.py in our productdatabase project folder. Create this file and put this inside:

# Django settings for productdatabase project.
DEBUG = True

ADMINS = (
    ('Brice Leroy', 'bleroy@fulham.com'),
)


DATABASE_ENGINE = 'mysql'           # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
DATABASE_NAME = 'pdb'             # Or path to database file if using sqlite3.
DATABASE_USER = 'pdb_user'             # Not used with sqlite3.
DATABASE_PASSWORD = 'mysal_password'         # Not used with sqlite3.
DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.

# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'America/Chicago'

# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'

SITE_ID = 1

# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True


# Make this unique, and don't share it with anybody.
SECRET_KEY = '2fvk@#oSD%(;Has235S9B3C#&AMV83%DKktucA60/.]1!@~113'

You will have to replace the SECRET_KEY by another random value of your choice and set the variable DATABASE_PASSWORD with the password you set for the user pdb_user. We can now synchronize the database:

brice@pdb:~/productdatabase$ mkdir media
brice@pdb:~/productdatabase$ mkdir media/product_document
brice@pdb:~/productdatabase$ mkdir media/product_picture
brice@pdb:~/productdatabase$ sudo apt-get install python-imaging
Reading package lists... Done
Building dependency tree       
...
ldconfig deferred processing now taking place
brice@pdb:~/productdatabase$ ./manage.py syncdb
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table picture_picture
Creating table product_category
Creating table product_product
Creating table product_import
Creating table document_document
Creating table inventory_warehouse
Creating table inventory_inventory
Creating table djangodblog_errorbatch
Creating table djangodblog_error
Creating table stockquery_stockcheck

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'brice'): 
E-mail address: bleroy@fulham.com
Password: 
Password (again): 
Superuser created successfully.
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for admin.LogEntry model
Installing index for product.Product model
Installing index for inventory.Inventory model
Installing index for djangodblog.ErrorBatch model
Installing index for djangodblog.Error model
Installing index for stockquery.StockCheck model
brice@pdb:~/productdatabase$ 

maintenance

Unit Test

The Product database server has been developed with tests to ensure consistency. Each time you make a modification it must run them to avoid functionnality breaking. To run all of them:

brice@pdb:~/productdatabase$ ./manage.py test
Creating test database...
Creating table auth_permission
Creating table auth_group
...

You should see a . for every successful test. Remember that without the authentification patch you will get a couple of F meaning a failure.

Backup

Since the source are GIT managed you can just clone it. To backup the whole site, you need to save the media directory, the database and the settings_local.py

brice@pdb:~$ mysqldump  --add-drop-table -c -u pdb_user pdb -p > django.sql
Enter password: 
brice@pdb:~$ tar czf django_backup.tgz productdatabase/media productdatabase/settings_local.py django.sql                 

Those command will provide you a full backup of your actual installation. You have now to transfert the file django_backup.tgz on a safe place.

administration

Django comes with a ready to use administration. Only users with staff property checked will have an access to the admin page. To access the admin page go to https://pdb.fulham.com/admin.

It's recommanded to manage your user rights through group and them adhere them to this group

  1. Allow you to quickly add a new record
  2. Redirect you to the list of items contained by this module to operate changes
  3. List the previous changes you've done
  4. Direct access to module, act like change
  5. Change password and logout from the admin

group creation

To start, in the group module section, click on add link. As an exemple, we will create the distributor group that can see products regular prices, distributor stock price and can see product availability:

  1. Filter, you can enter a part of the permission title
  2. To delete a permission you can double click it
  3. Each edition page can be saved by those buttons

The principals rights are:

user creation

When you have setup the groups, you can now create user using the add user button in the user module

This example show you a rest user with full access to the rest interface. It is not using the group so I can show which right are selected. It's always recommended to use group to manage right.

inventory import

An import consist in a CSV file. Common spreadsheet allow you to import a table to a CSV. The first column should contain the item number, the other column are the quantity in stock. The import will read the first line and generate warehouse if they don't exist. Here is an exemple

  1. To create a new import, click here.
  2. This section allow you to make short filter on existing import record.
  3. Each record you want to import have to be checked here.
  4. Once you have checked the needed records, select "Import selected Inventory" and click go.

web user interface

product list

The product list view show you paginated version of the products. It display up to 10 items per pages.

  1. On every page you will see this link to log yourself out of the system.
  2. You can filter the list by indicating a category or a part of the item number. If only one item is find with the filter you set the product will directly show up.
  3. This line show how many document this item contains and a part of the description.
  4. The next and previous link allow you to browse the product list.

product detail

  1. shorcut to the search. The search is done on the item number.
  2. Property tables, will show different properties according to your permissions in the system.
  3. Every picture and document link to the current item can be see by clicking here. You can also have the stock appearing here or the stock query if you cannot access the quantity in stock.
  4. This link bring you back to the product list page

rest interface

Authentication

REST API needs you to authentify using HTTP1.0 (rfc1945). I choosed this protocol because it is protected by the SSL layer. The authentification has to be include the header of the request. Following is an exemple of authentification using python:

from binascii import b2a_base64
import urllib2
req = urllib2.Request('https://pdb.fulham.com/product/xml/TLMR160355CW/')
req.add_header('AUTHORIZATION','Basic %s' % b2a_base64('username:password')[:-1])
response = urllib2.urlopen(req)
print response.read()

If you copy and past the commands above in a python console, you should obtain this result:

brice@ubuntuserver64:~/django-db-log$ python
Python 2.6.2 (release26-maint, Apr 19 2009, 01:58:18) 
[GCC 4.3.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from binascii import b2a_base64
>>> import urllib2
>>> req = urllib2.Request('https://pdb.fulham.com/product/xml/TLMR1607HP40C/')
>>> req.add_header('AUTHORIZATION','Basic %s' % b2a_base64('username:password')[:-1])
>>> response = urllib2.urlopen(req)
>>> print response.read()

The last line is supposed to give you this result (depending how many things you did import):

<?xml version="1.0" encoding="utf-8"?>
<objects>
<object pk="3" model="product.product">
    <property name="item_number">TLMR1607HP40C</property>
    <property name="title">TLMR1607HP40C</property>
    <property name="description">LED MR 16 7W 40 DEG. 5500K </property>
    <property name="length" uom="in">None</property>
    <property name="width" uom="in">None</property>
    <property name="height" uom="in">None</property>
    <property name="activity_code">A</property>
    <property name="un_spsc_code">32111503</property>
    <property name="dist_velocity_code">C</property>
    <property name="wattage">8W</property>
    <property name="base">GX 5.3</property>
    <property name="page">Catalog pg # 6</property>
    <property name="upc"></property>
    <object pk="81" model="picture.picture">
        <property name="title"></property>
        <property name="url">http://pdb.fulham.com/media/product_picture/tmpb_s6TL.jpg</property>
    </object>
    <object pk="148" model="document.document">
        <property name="title"></property>
        <property name="url">http://pdb.fulham.com/media/product_document/tmpCBF5oZ.pdf</property>
    </object>
    <object pk="13487" model="inventory.inventory">
        <property name="warehouse">Avail-CA</property>
        <property name="quantity">75.0</property>
        <property name="date">2009-10-19 14:52:22</property>
    </object>
    <object pk="13488" model="inventory.inventory">
        <property name="warehouse">Avail-FL</property>
        <property name="quantity">25.0</property>
        <property name="date">2009-10-19 14:52:22</property>
    </object>
    <object pk="13489" model="inventory.inventory">
        <property name="warehouse">InTransit</property>
        <property name="quantity">0.0</property>
        <property name="date">2009-10-19 14:52:22</property>
    </object>
</object>
</objects>

rest URLs

Here is the couple of exemple of urls that can be used on the rest API.

reading record

To read data, the method should be GET.

deleting record

To delete a record, the method should be DELETE, then calling https://pdb.fulham.com/product/xml/TLMR1607HP40C/ will delete the product with the item_number TLMR1607HP40C

updating record

to update a record, the method should be PUT, then calling https://pdb.fulham.com/product/xml/TLMR1607HP40C/ will update the tiem TLMR1607HP40C. Each property you send will update the record designated by the url.

creating record

Same as updating record but will create a new one. The url is https://pdb.fulham.com/product/xml/ and you have to provide all the informations