Canonical Voices

What The Raving Rick talks about

Posts tagged with 'slip-cover'

Rick Spencer

I removed the ViewDesignerDialog, and changed it to a ViewDesignerBox. Basically, a widget that is now embedded into Slip Cover. I also wrote some code to extract the views that currently exist in a database, and offer those under "Views" so you can click on them and see the views map function, and the views reduce function if it has one.

Besides editing views and trying out your edits, this lets you look at the databases you have and see how they work. In the example above, you can see how the views for your contacts are set up. Handy if you want a similar feature in one of your databases.



This screenshot shows two things. First, it shows the view I constructed to get the record type for each record in a database, and also whether the record has been deleted. It's a bit "meta", but the purpose of this is visible in the Record Types list. I use the view to list out the record types, and how many records are deleted and how many active.

Still a few missing features:
  1. The "Create View" button doesn't work yet (though shouldn't be too very hard to make)
  2. "Save View" also doesn't work yet.
  3. I want to add options for executing your view, such as Group and such.
  4. I want to add a bit more data about each database, and whether you can sync it or not. I'm thinking about doing this on the server info tab, maybe an editable DictionaryGrid?
Also, in terms of quickly-widgets, I hate writing the same notebook code for tabbed interfaces over and over again. We should have a quidget that makes it easy and fun, and handles close buttons on the tabs, has a free document menu, and has alt # navigation to documents.

Read more
Rick Spencer

Desktop Couch View Editor

As I've been learning to use DesktopCouch, I've come to understand the key role that writing map and reduce functions play. Basically, a view is stored map function and optional reduce function. I still haven't quite figured out reduce functions, especially wrt rereduce.


Anyway, I was finding it way to tedious to iterate on views in iPython. A map or reduce function looks to DesktopCouch as a string, thought it is a function written in javascript. And there is no syntax highlighting, or really any help at all writing this function as they look like strings in gedit and iPython.

So yesterday, my ViewDesignDialog was born, and added to Slip Cover. Currently, it supports running a map/reduce function as a temp view. This has been very helpful for me already, as I've been able to finally write a map function gives me each record, it's record and type, and whether that record is active or deleted. I want this for the record types view in slip cover, so you can see if there are any active records associated with the record type. I might be able to go on and build an "undelete" feature which would be handy for undo actions in slip cover.

I will add a "Copy as DesktopCouch" code next, which will just put the code on the buffer so you can paste it into your Python code as desired. Also, I'll add "Save". To do this I'll need to do some work with design_docs.

Eventually, I expect Slip Cover will use this so you can create new views, and edit existing views, for a database using Slip Cover, in addition to having a handy tool for generating DesktopCouch code (or just browsing a database I suppose).

It's not integrated into Slip Cover to well atm. Maybe I'll get a chance to work more on this tonight, but I am plan to do some things that don't involve a keyboard for a while this weekend ;)


Read more
Rick Spencer

Today I spent a few minutes to add an "Add Record Type" feature. As you can see, this was a matter of creating a button, and then in the handler, creating a single record with only that record type in it.

    def add_record_type(self, widget, data=None):
title = _("Add Record Type")
msg = _("""
Specify a string name for the record type. By convention, the
name should be a URL where you can document the user of the record type.
""")
response, val = prompts.string(title,msg,"http://wiki.ubuntu.com/")
if response == gtk.RESPONSE_OK:
r = Record({"record_type":val})
self.database.put_record(r)
self.record_type_grid.append_row({"Record Type":val,"editor":None})

After adding a record type, you can use add/delete rows/keys to full design and populate a data set. I'm kind of working inside out with regard to making a full desktopcouch designer. I started with modifying rows, then add/removing rows, then adding/removing keys, and now adding record types.


The next step is to implement New/Delete Database, and I'll a functional designer. I'll probably add Slip Cover to my PPA at that point.

There is no "Delete Record Type" function. This is because desktopcouch never permanently deletes a record, it only marks it as deleted, and as such does not return it when you query for records. This has the odd effect of making Record Types last forever. I am considering adding a "deleted count", and an "active count" column to the grid for the record types. This would take more Map/Reduce skills than I have right now, so would probably be a good thing for me to learn.

Read more
Rick Spencer

So as planned, today I added the feature for creating databases. This was one of those sweet features where it ended up on the tip of previous coding, so it took literally a matter of minutes to implement. Just get a name from the users, create the database, then load it in the UI,

    def new_database(self, widget, data=None):
title = _("New database")
msg = _("Specify a name for the new database")
response, val = prompts.string(title,msg)
if response == gtk.RESPONSE_OK:
CouchDatabase(val, create=True)
self.load_db(self,val)

So now I have an end to end tool for designing my desktopcouch databases.

Use File->New and make a name for my database:
Get an empty screen for the database.
Use the "+" to create a new record type.
Use Add keys to add keys for record type.
Then add rows and fill in the data.

Read more
Rick Spencer

Because I added "Add Key" yesterday, today I wanted to be symmetrical, so I added "delete keys". This turned out to be much more work. First, I ended up creating another quickly.prompt, and then I had to dive into the internals of CouchGrid, and also do some hair raising mucking with desktop couch.

quickly.prompts.checklist()
When the developer clicks the "delete keys" button, I wanted to present them with a list of keys they could choose from. And then have all of those get deleted. This seemed like the kind of thing that I'd want to use in other programs, so I decided to solve this problem generically by adding it to quickly.prompts.

I accomplished this by deriving from quickly.prompts.Prompt, and also creating a helper function. CheckListPrompt works as you would expect for prompt. You set it up by passing in some configuration info, including a dictionary of strings as keys, which will be labels for the checkboxes, and a bool value to determine if the box is checked by default.

You get back a response and val. The val is a dictionary of keys again, with bools for whether the checkboxes are active or not.

So to use the CheckListBox, I just pass in a dictionary of the keys for the CouchGrid, and then see if any were selecct:

       val = {}
for k in self.grid.keys:
val[k] = False
response, val = checklist(title, message, val)
if response == gtk.RESPONSE_OK:
#do stuff

Hair Raising Munging
Since "do stuff" is pretty destructive, I use a quickly.prompts.yes_no to confirm that the users wants to blow away all the data and screw up their database. Assuming they do want to delete the keys and values in the desktopcouch database, it turns out to be *not* easy to do the deletion without reading way into CouchGrid. The issue here is the couchdb reserves anything staring with a "_" for itself. But DictionaryGrid uses "__" as a convention to determine that a key should be hidden in the grid by default. So as a result of this CouchGrid munges _id and _rev and record_type before it reads to and from the database.

The second troublesome part was dealing with desktopcouch. It turns out that you can't just delete a key from a record. You have a delete the whole record and then create a new record without that key. so as a result the code deletes and recreates each and every row.

I really think this code belongs inside CouchGrid:
    def delete_keys_from_store(self, model, path, iter, keys_to_delete):
for k in keys_to_delete:
d = model.get_value(iter,len(self.grid.keys))
if k in d:
del(d[k])
if '__desktopcouch_id' in d:
keys = d.keys()
for k in keys:
if k.startswith("__desktopcouch"):
dc_key = k.split("__desktopcouch")[1]
d[dc_key] = d[k]
del(d[k])
if k == "__record_type":
d["record_type"] = d["__record_type"]
del(d["__record_type"])
self.database.delete_record(d['_id'])
del(d["_rev"])
del(d["_id"])
self.database.put_record(Record(d))

Who would ever be able to figure out to do all this?

Refresh
So after this the refresh function was trivial. Just tell the CouchGrid to reset, and then recreate the grid:
    def refresh(self, widget, data=None):
self.grid._refresh_treeview()
self.remove(self.filt)
self.filt = GridFilter(self.grid)
self.pack_start(self.filt, False, False)
self.reorder_child(self.filt,1)
self.filt.show()

desktopcouch Editor
So now with adding a removing records and keys, along with freshing, I have a functional desktopcouch editor. This tool has already proved a bit useful in getting a peak into certain database. However, I can't actually create new record types yet. Maybe tomorrow?

Read more
Rick Spencer

I started out wanting to add "refresh". However, I just wiped my netbook and set up Lucid UNE Beta 2 on it, so only had one database in my local desktopcouch and it didn't have any records. I didn't know how I'd test the feature. I haven't set up my Ubuntu One yet on this computer, because I've been waiting for gwibber to get fixed (it is!) so that I could test out the Social From the Start Experience.

This is a long winded way of saying that I decided to implement another feature first to make it easier for me to test refresh out, editing an existing database. The first step of this was to be able to add columns. So I added an "add column" button.

I used quickly.prompts to get the string from the user. To actually add the column required a bit of intimate knowledge of the internals of DictionaryGrid. It occurred to me that folks might want to be able to add a key/column to a Grid themselves, so I logged a bug to remind me to make this easier.

Since Fagan asked to see the code when I posts :) -

    def add_column(self, widget, data=None):
response, val = prompts.string("New Column","Please select a name for the new column")
if response == gtk.RESPONSE_OK:
self.grid.keys.append(val)
self.grid._refresh_treeview()

Read more