[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-pim
Subject:    Re: Grouping + importing
From:       Don Sanders <dsanders () cch ! com ! au>
Date:       1999-07-22 23:48:50
[Download RAW message or body]

On Wed, 21 Jul 1999, Rik Hemsley wrote:
> On 21-Jul-99 Don Sanders wrote:
Ok I think we basically agree on the broader details (we are on the same
"wavelength") and we have to pin down the finer details now.

> > Currently the ABEE uses a ContactEntry class which is basically a QDict. The
> > ABEB uses a ContactEntryList is basically a list of QDict.
From what you have said I think I should  change the way the GUI is
implemented so that I use a dictionary of ContactEntries (aka Entities), with
each ContactEntry (aka Entity) providing a dictionary like interface for
setting/getting fields and values.
So ContactEntryList dies and is replaced with something like:

class EntityDict {
  QStringList defaultFields(); // get a list of field names
  QString i18nField( QString field ); //return a localised version of field name
  Entity getEntity( QString Entitykey ); // gets a handle to an entity
  replaceEntity( QString key, Entity e );
}
and an EntityDictIterator used to iterate over all entries in the EntityDict.


Notes:
1) I've introduced a new problem here the localisation problem.
2) Entity is an Address Book Entry (aka Contact). This is just a handle, the
actual representation could be loaded on demand. So EntityDict is a dictionary
of entity handles.
3) I talk about the Entity class later.

> Yes, this is also the approach I am using internally in the example
> (called 'v2'). As you say, using a QDict to hold an entire addressbook is
> plain daft. I'm only using a QDict as a fake backend. You give a key and you
> get a value.
So you are providing something similar to what I just defined above Y/N? We
roughly understand each other here?

>I decided that keys should be unique ids rather than names or
> whatever.
Good.
> The plan would be to change the QDict to an interface to a backend
> which acts like a database, giving you data when you pass it a key. This could
> actually be done even with something simple like gdbm as a start. /me leafs
> through the gdbm docs.
Will we require gdbm? Most people won't need a real database I think. Is a
simple "plaintext" implementation planned. I guess plaintext is the wrong word
(because of the unicode issue, it would be nice if this format was easily
processed by text processing tools but I'm not too worried about that), what I
mean is dependency free. Using gdbm worries me a bit, I am under the
impression that there are still licensing "issues" with linking gdbm to kde
apps

>
> > There is also the issue of whether/how the ABEB view should be updated when
> > another client modifies teh address book 'database' the enduser could be
> > required to manually refresh the view, (The outlook express ABEB seems to
> > take
> > this approach), alternatively CORBA could provide a nice network transparent
> > soln to this.
>
> I have been thinking about this whilst working on Empath. Empath will update
> the display when new mail has arrived, without the user checking. A similar
> approach can be applied with the addressbook UI.
There is one possible problem with this method concerning usability. The MS
explorer window has a tendency to refresh itself at annoying times, resorting
the view so that you end up clicking on the wrong file. I guess this wouldn't
be so annoying if performance was better.

> > So as far an "OO model" goes I have these concerns:
> > A) I think a QDict like interface with methods like
> > ContactEntry::insert( QString field, QString value )
> > ContactEntryList::defaultFields( QStringList fields )
> > is the most natural for AB clients like the GUI stuff I have been doing.
>
> It is the _easiest_ API but it doesn't fulfil the requirements.
> You need to be able to store more than just QString as values. You can't use
> QCString as a NUL will terminate the data, and QByteArray has gone. Therefore
> I derived a 'CharBuf' from QShared and pass around (a typedef to)
> KSharedPtr<CharBuf> called CharPtr. This allows you to read and write raw data
> and has the advantage of reference counting.
OK, I think I understand now. The GUIs don't currently support non QString
type fields but I will update them so that they do.

>
> defaultFields(QStringList) is a useful idea, and it is already in the original
> API.
Why the QStringList parameter?

>
> FieldList fields(const QString & fieldName)
> and
> FieldList fields(const QRegExp & expression)
What are the parameters for here?

> are your friends here.
>
> There's also stuff like:
>
> FieldList fieldsWithValueType(ValueType)
> and
> FieldList fieldsWithValueType(const QString &)
>
> for a similar purpose. Note that you can use one of the standard value types
> by passing a ValueType enum or ask for an extension type by using the string
> parameter.
>
> These aren't in v2 yet, but I'll add them later.
>
> > I'm not convinced that your second objective
> > 2) Easy access to all parts of addressbook entities
> > has been achieved, rather I think
> > 2*) Easy access to all parts of vCardd address book entities
> > has been achieved.
> >
> > I think your API is an excellent vCard API but I am not convinced it is the
> > most natural AB API. (Note basically this just means a QDict to vCard mapping
> > is required, this has to be done at some point regardless, the question is
> > when, I thinking a ContactEntry -> vCard Entity mapping needs to be written,
> > I'm willing to do this but I need to compile your stuff first and will
> > probably ask for help/clarification on some fields)
>
> Are you saying 'it's a good API for vCard' or 'it is a vCard API' ? It's not
> a vCard API. vCard is completely separate. v2 doesn't know anything about vCard.
> You can see this where the import function is used - you only give a string to
> identify the kind of format you think the data is in. The plugin is looked
> for and loaded then handles the rest. Only the plugin knows about vCard (and
> gets all its functionality from libvcard).
>
> If you're saying that the API looks like a vCard API, I have to plead guilty.
> It's similar, yes, but then the vCard model is a good one. I've simply replaced
> it with an extensible format.
From a technical PIM point of view I don't think it matters, but, I said the
former but was really trying to say the latter in a polite way. I was just
trying to e-mail in a friendly tone of voice, not be ambiguous and
purposefully deceive you or anything.
/end me being ultra paranoid :-)

Having heard your reply I'm closer to being convinced that it is an acceptable
AB API (ie it looks like it will be a sensible amount of work for me to change
my code to use it)
>
> vCard:
> Set of value types that are well defined. You may add extra information but
> you cannot specify the type of that data and therefore cannot parse it.
>
> v2:
> Set of value types that are well defined. You may add extra information and
> you _can_ specify the type of that data to facilitate parsing (and therefore
> searching / sorting).
Thanks for clarifying that.

>
> I've basically just provided what you talk about above - a name to value map,
> but I've added an API that allows easy access (for the programmer) to the
> common fields. I hope this doesn't look like it's intended to nail down a set
> of field names and value types.
Unfortunately that is exactly what it looked like to me. One of my first
thoughts was, oh no, I want to call
 set( "DisplayName", "Don Sanders" );
not
 setDisplayName( "Don Sanders" );

> You can use it as a name-value map if you
> so wish. The extra parts of the API are simply for convenience.
I'm not convinced the extra parts will be convenient for me. It is most
convenient for me to define a class

class myWidget : QthingWidget {

 myWidget( QString fieldName, Entity* e, ... )
  : QThingWidget( fieldName ... ) {
    QObject::connect( e, SIGNAL( changed()), this, SLOT( sync() ));
  }

  virtual void focusLost() {
    e->set( name(), currentWidgetText() );
	// e emits changed()
  }

  virtual void sync() {
  if (currentWidgetText() != e->get( name() ))
	setCurrentWidgetText( e->get( name() ));
  }
}

and in my code write
myWidget *bob = new myWidget( "DisplayName", DocumentAndObserverForThisView );
myWidget *joe = new myWidget( "Webpage", DocumentAndObserverForThisView );
...

the alternative using methods like setDisplayName would make my code
horrendously more complicated.
I don't handle different types of fields. In this case I think it would
be sensible to assume QString by default and have other methods for setting
other types like

Entity::setDate( QString fieldName, DateType value );

or perhaps less convenient but more general
set( ValueType type, QString fieldName, CharPtr value );

I also need a way of getting a string representation for values of each type
even if this is just something like "N/A" for a generic binary (pixmap say)
type.

>
> I've tried to make the standard fields so that they're completely general.
> It's extremely difficult to do this for the whole planet. After a lot of
> thought I decided that it would be better to associate people with a location,
> rather than store a location within a person's record. In a similar vein,
> I've provided for associating a 'Comms' object with any entity. This means
> you can associate a 'Comms' object with a group (say a company) and have
> a contact telephone number and address for the company. You can add extra
> 'Comms' objects to a person through their xValues. This means if you have
> two 'work' contacts (here and overseas) and three 'home' contacts (home,
> on the move and at the mistress) you can add them as extras. There's only
> one 'Comms' object defined for a person by default.
Seems like a good idea but the UI doesn't fully support this. I would have
to change it so that one could either enter a new address or select from a
list of know addresses.

>
> > B) Have you given any though to dealing with large/remote address books, I'm
> > thinking of dealing with this later. I think a whole new API and modified GUI
> > is need to handle this
> > (eg. server give me records x to y when sorted by field abc)
>
> I think the API shouldn't be written to reflect the back end, but obviously
> there will need to be parts to handle this kind of thing.
> For example,
> QStringList KAB::addressBooks();
> returns 'Local', 'LDAP server at work', 'SomeOtherKabAcrossTheInternet'.
>
> The GUI could then provide for selecting which addressbooks should be used
> and call something like:
> QStringList iWillUse;
> iWillUse << "Local" << "LDAP server at work";
> KAB::useAddressBooks(iWillUse);
>
> This should be easy to do. Configuring new servers could be done through
> the GUI or via a kcm module.
I didn't myself clear. Let me start again. There may be cases where an address
book is so large that the list of entities in a particular folder wouldn't
fit in the client machines main memory. There may also be cases where the
address book is stored remotely and the cost of transferring the complete
list of entries in a particular folder over the network is prohibitively
expensive (either in time or money). (Perhaps even a list of entity keys
couldn't be stored/transferred).

A new API and GUI would be required to deal with this. Let us for the moment
consider these problems outside the scope of our intial implementation.

As far as selecting remote addressbooks yes that's definitely a feature we
want, and not just for address books. It would be nice if the Entity and
EntityDict classes are general enough to handle e-mail for instance as well as
address book entries.
<begin talking without knowing what I'm talking about>
Perhaps then an IMAP implementation could be used for
accessing e-mail like LDAP is used for accessing addressbooks,
</>

> > I thinking the best way to handle addressbooks is to assumme that they have
> > been broken up into sensible size folders/groups and load all entries in a
> > folder at once, (so that an arbitrary set of fields can be displayed).
> > Perhaps
> > this could be optimized by providing a method that returns all subfolders in
> > a
> > folder so that a explorer like tree control for navigation could be provided.
>
> Really ? I don't know if assuming that you have a small group to read is
> necessarily a good plan. Loading everything into memory at once can't be
> a smart move. Certainly a group browser would be very useful. I think the
> back end should handle all storage and only communicate one entity at a time.
> This way, kab will search by loading each record in turn and looking at it.
> It's not the fastest method but should be acceptable and won't eat your swap :)

Looks like I lost it for I minute and my thinking got sloppy. Thanks for
correcting me. Even when communicating one entity at a time we might want to
just communicate a handle, if the entity contains a pixmap field say then we
only want to load the value for that field up if we use it.

As far as a folder having a method that returns a list of subfolders I think
that's still a good idea. The implementation could be optimized so that folder
can be navigated without having to wait for the list of files in that folder
to be created.

> > C) Dynamic updating
> > Does you model deal with this? I can see several different solns, require the
> > user to manually update, chunky updates where the entire AB folder is
> > reloaded
> > whenever the AB is modified, or finer updating incrementally handling
> > insertions/deletions and only refreshing/resorting modified entries.
>
> Yes, I think as I said above this should be ok. kab can use a timer to check
> if the database has been updated and when it has, tell the GUI.
I'm going to think about this more. Can we share an address book between
different processes, different users (security comes into play), different
users on different machines?

>
> > Other issues:
> > Import abilities through a plugin interface sounds damn cool.
>
> It is :) You can't imagine how happy I was when I wrote the code and it just
> worked first time !
>
> > You are using namespaces. I'm using egcs 1.03 at the moment and will have to
> > upgrade before I can compile your code.
>
> Yes, sorry about that. It would restrict what I'm doing if I didn't.
Ok I'm on egcs 1.1xx now.

>
> > My UI doesn't support all vCard info. For instance your sample vCard contains
> > the line
> > EMAIL;TYPE=INTERNET: rik@kde.org
> >
> > currently my UI just has email, email-2 and email-3 fields. The draw back
> > here
> > is that if someone edits the sample vCard in my editor they will lose the
> > TYPE
> > inforamtion, and I can't handle more than 3 email fields. Also the entire TZ
> > field will be lost as the GUI doesn't currently support it. (Perhaps I could
> > automatically put this in the Custom fields tab).
>
>
> Well, I don't support all vCard info either, not directly anyway. Certainly
> I can add extra 'Comms' object to a person when they have more than one email
> address, and I also (through the 'Member' entity) provide for associating
> one email address with the person as a group member, and a separate one for
> home. The vCard plugin does some nasty stuff to try and find either the one
> and only email address given, or the one marked as preferred. This is then
> used in the Person's Comms object.
OK. Well if I can replace my ContactEntry class with the Entity class
and a copy constructor is defined for the Entity class I should be
able to update Entities without losing information.

One thing I use for my ContactEntry class provides is a method for getting all
custom field names used in a ContactEntry instance.

I'm expecting someting like this for the entity class:

class Entity {
 // default and copy constructor
 QFieldList customFields();
 QString field( QString field ); // returns the value for a specified
 // QString type field
 void setField( QString field, QString value ); // set the value
 // for a specified field QString type field
 DateType dateField( QString field )
 void setDateField( QString field, DataType value );
 ...
}

> I just drop the TZ field in the plugin, but I could simply do:
> XValueList l = person->xValues();
> XValue * x = new XValue;
> x->setName("TimeZone");
> x->setValue(timezoneString);
> l.add(x);
> person->setXValues(l);
>
> (Apologies for the crap API for that. Will be fixed)
From the GUI point of view
    // set an X QString type field
  person->setField( "X-myXname", myQStringValue );
    // set an X DateType field
  person->setDateField( "X-myDateXname", myDateValue );
  etc...
will make things a lot easier.

There is still a problem if I support importing of an "X" type fields into
the custom fields tab, I need a representation of the name of the field
in the current language. This could be done for "TimeZone" and other known
(but not fully supported) vCard fields but not for "X" type fields in general.

So unknown "X" type fields will just be ignored by the GUI I guess.

>
> > Okay I hoped that made sense. By all accounts you have implemented an
> > excellent vCard API but I think more discussion needs to take place regarding
> > the AB API.
>
> Certainly you made sense. I'm concerned you think that I've written a vCard API
> though. This is supposed to be a general AB API with support for import filters
> and an object model that matches the real world as closely as possible without
> being locale dependent.
>
> By all means discuss further ! I will not claim that I've provided the perfect
> API by any means. This is draft #0.000001 and I've put out a Request For
> Comments ;)
Does the API support searching? Is a version of the API checked into CVS?

BFN,
Don.

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic