Change History
==============
Version 0.6 -
-------------
- Added pseudo-accessor "by" to Tables, to make access to indexed fields
  read more naturally:
  
    print students.id[12345]

  is now
  
    print students.by.id[12345]

- Table.compute renamed to Table.add_field, to more clearly describe
  what this method does.

- Modified __getitem__ so that retrievals of slices return new Tables
  instead of just lists of objects (essentially adding sliced indexing
  as another chained accessor).

- Added count_fn to the dump_counts method of PivotTable, so that a
  summarizing function other than mere counting can be used for each cell
  in the pivot table.  Here is an example of summarizing population by
  state and elevation, where each record has attributes state, elevation
  (reduced to nearest 1000'), and population:
  
    piv = places.pivot('state elevation')
    piv.dump_counts(count_fn=lambda recs:sum(r.population for r in recs))

- Added sort(), initial version contributed by Adam Sah. sort will take
  a key function, or a string containing comma-separated attribute names.
  Attributes can be qualified with "desc" to indicated sort to be done
  in descending order.

- Modified insert() and add_field() to return self, for chaining support.

- Renamed maxrecs parameters to 'limit', to be more similar to the same
  concept in SQL.
  
- Merged query and where into a single consolidated function named 'where'
  for selecting matching records from a table.

- Add union function to add records of two tables. '+' between two tables
  will perform union; '+' between join terms, or a table and a join term,
  will perform join.

- join() verifies that all named attributes exist in one of the source
  tables

- join() will automatically create indexes for join columns if indexes
  do not already exist


Version 0.5 - 
-------------
- Added groupby() method to Table (thanks, Adam Sah!) to generate tables 
  of computed values keyed by an existing or computed attribute.

- Added optional name fields to clone and copy_template.

- Modified create_index and insert_many to return self, so that a 
  simple table creation and indexing could be done in a single chained 
  statement (also suggested by Adam Sah, thanks!)

- Fixed possible bug in PivotTable.values - now values are returned
  in the order of matching keys returned by keys().

- Fixed bugs in Table.pivot(), see pivot_demo.py


Version 0.4 -
-------------
- Added compute() method to Table to support global update of all objects 
  in the table, to add or modify a given attribute on each object.  compute()
  takes a function that computes the new or modified attribute value, taking 
  the entire object as its single input argument.  Can override DataObject's 
  write-only functionality (for example, when converting attributes entered 
  as a string to an int or float).  Also accepts a default value to use, in 
  case the computing function raises an exception.  Useful when creating a 
  new attribute that would be computed based on other values in the object.
  
- Added transforms argument to csv_import, to simplify the conversion of 
  string data values to int, float, or other non-string type.  The transforms 
  argument is a dict mapping attribute names to conversion functions, each 
  function taking the as-imported string value and return a new transformed 
  value.
  

Version 0.3 -
(renamed project from dulce to littletable)
-------------
- Improved exception message when duplicate or None value is given for a 
  unique index attribute.
  
- Support for namedtuples and __slots__-defining objects supplied by Colin 
  McPhail, thanks!

- Added Table.pivot() method, to return a pivot table on one or more 
  attributes.  New PivotTable class includes methods for extracting data in 
  both Table and tabular formats.

- Added more details to the docstring for Table.join, to more completely 
  describe the sequence of steps to join 2 or more Tables, using join(), or 
  join_to and '+'. Also, removed table_name argument in Table.join, to 
  simplify this call (the resulting table can be easily renamed using Table 
  call form).

- Python 3 compatibility changes, importing simplejson or json, as 
  appropriate - thanks again to Colin for the Python compatibility testing.

- Renamed _TableJoin to JoinTerm, and added documentation on how it is used 
  to build up table joins using '+' addition notation.


Version 0.2 -
-------------
- Fixed typo in module docstring, "wishlists" should be "wishitems" (as shown 
  correctly in dulce_demo.py). Also fixed typo in docs for Table.create_index, 
  caused by my lack of epydoc-fu.  Plus some general cleanup of the 
  docstrings.  Thanks, Colin McPhail for reporting these!

- Changed _orderby and _orderbydesc flags to just _orderby, taking a single 
  string containing a comma-separated list of attribute names (this is 
  necessary since **kwargs does not preserve arguments order).
  
- Changed Table attribute "name" to "table_name", so as not to collide with 
  a user-defined index on an attribute named "name".

- Added support for unique indexes to allow or disallow null key values.  If 
  null keys are allowed, then all records with a null key are stored in an 
  internal list.

- Added some join performance pickup, using the table with the smaller number 
  of keys as the outer loop in finding matching records.
  
- Added query performance pickup, when using multiple indexed attributes in 
  query.


Version 0.1 - 16 October 2010
-----------------------------
Initial prototype release
