Closed jave closed 6 years ago
Nope, I don't think anyone's ever done this. It's something I'd like to support, though, if you're willing to be a guinea pig :)
Quick question: do you have contacts with multiple values for single property types? Meaning, more than one email address, for instance? How does that look in the Org file?
Here's what I've got so far. Unfortunately I don't know how to parse street addresses, in part because I don't know how org-contacts stores them, but also just because it's very hard to read an address from a free-form string. The current implementation just sticks the addresses in another buffer, and pops that up; you'd have to add them yourself. I'll try to figure out a better solution to that.
(defvar ebdb-migrate-org-simple-correspondences
'((org-contacts-email-property . ebdb-default-mail-class)
(org-contacts-tel-property . ebdb-default-phone-class)
(org-contacts-note-property . ebdb-default-notes-class)
(org-contacts-nickname-property . ebdb-field-name-simple)
(org-contacts-alias-property . ebdb-default-name-class))
"The simplest property-to-field correspondences.
This variable only holds correspondences for fields that require
no processing beyond calling `ebdb-parse' on the string values.")
(defun ebdb-migrate-from-org-contacts ()
"Migrate contacts from the org-contacts format."
(interactive)
(require 'org-contacts)
(unless ebdb-sources
(error "First set `ebdb-sources' to the location of your EBDB database."))
(let ((db (ebdb-prompt-for-db))
;; Postpone evaluation of the symbols until run time.
(prop-fields
(mapcar
(pcase-lambda (`(,prop . ,class))
(cons (symbol-value prop)
(if (class-p class) class (symbol-value class))))
ebdb-migrate-org-simple-correspondences))
(count 0)
(address-buffer (get-buffer-create "*EBDB Migration Addresses*"))
record records n f)
(with-current-buffer address-buffer
(erase-buffer))
(message "Migrating records from org-contacts... %d records" count)
(pcase-dolist (`(,name ,_ ,fields) (org-contacts-db))
(setq record (make-instance ebdb-default-record-class))
(setq n (ebdb-parse ebdb-default-name-class name))
(ebdb-record-change-name record n)
(dolist (field fields)
(setq f
(if (assoc-string (car field) prop-fields)
(ebdb-parse (cdr (assoc-string (car field) prop-fields))
(cdr field))
(pcase (car field)
((pred (equal org-contacts-address-property))
(with-current-buffer address-buffer
(insert (format "%s: %s\n" name (cdr field)))))
((pred (equal org-contacts-birthday-property))
(make-instance 'ebdb-field-anniversary
:date (calendar-gregorian-from-absolute
(org-time-string-to-absolute
(cdr field)))
:object-name "birthday")))))
(when f
(ebdb-record-insert-field record f)))
(push record records)
(message "Migrating records from org-contacts... %d records"
(cl-incf count)))
(dolist (r records)
(ebdb-db-add-record db r))
(message "Migrating records from org-contacts... %d records"
(length records)))
(ebdb-display-records records)
(with-current-buffer address-buffer
(unless (= (point-min) (point-max))
(pop-to-buffer address-buffer
'(display-buffer-pop-up-window . ((width . 50)))))))
thanks, I will try it out.
atm my contacts db is spread out over a lot of org files, and an old bbdb file, so it will take a lot of time to convert to ebdb.
It shouldn't be too hard, org-contacts-db
will find all the contacts in your agenda files, you don't have to do it file by file.
One call to ebdb-migrate-from-bbdb
and another to ebdb-migrate-from-org-contacts
should do it.
If you can show me an example of what an address property looks like, I might be able to handle those automatically, too.
ebdb-contacts-db lacks function definition.
Sorry! That should be org-contacts-db
! My brain stopped for a moment.
ebdb-migrate-from-org-contacts Migrating 0 records... Updating Org Contacts Database...done cl-no-applicable-method: No applicable method: ebdb-record-change-name, #s(#s(eieio--class ebdb-record-person "A record class representing a person." (#s(eieio--class ebdb-record-entity "An abstract class representing basic entities that have mail, phone and address fields." (#s(eieio--class ebdb-record "An abstract base class for creating EBDB records." (#s(eieio--class eieio-instance-tracker "This special class enables instance tracking. Inheritors from this class must overload `tracking-symbol' which is a variable symbol used to store a list of all instances." nil [] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data ( ...)) (ebdb-formatter ebdb-record semanticdb-project-database) nil [#s(cl-slot-descriptor tracking-symbol unbound symbol ...)] [unbound] ...)) [#s(cl-slot-descriptor uuid nil (or null ebdb-field-uuid) nil) #s(cl-slot-descriptor creation-date nil (or null ebdb-field-creation-date) nil) #s(cl-slot-descriptor timestamp nil (or null ebdb-field-timestamp) nil) #s(cl-slot-descriptor fields nil (list-of ebdb-field-user) ((:documentation . "This slot contains all record fields except those built-in to record subclasses."))) #s(cl-slot-descriptor image nil (or null ebdb-field-image) nil) #s(cl-slot-descriptor notes nil (or null ebdb-field-notes) ((:documentation . "User notes for this contact."))) #s(cl-slot-descriptor dirty nil boolean ((:documentation . "Does this record have changed fields?"))) #s(cl-slot-descriptor cache nil (or null ebdb-cache) nil)] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (uuid 0 creation-date 1 timestamp 2 fields 3 image 4 notes 5 dirty 6 cache 7 ...)) (ebdb-record-mailing-list ebdb-record-entity) ((:uuid . uuid) (:creation-date . creation-date) (:timestamp . timestamp) (:fields . fields) (:image . image) (:notes . notes) (:dirty . dirty) (:cache . cache)) [#s(cl-slot-descriptor tracking-symbol ebdb-record-tracker symbol ((:documentation . "The symbol used to maintain a list of our instances. The instance list is treated as a variable, with new instances added to it.")))] [ebdb-record-tracker] ...)) [#s(cl-slot-descriptor uuid nil (or null ebdb-field-uuid) nil) #s(cl-slot-descriptor creation-date nil (or null ebdb-field-creation-date) nil) #s(cl-slot-descriptor timestamp nil (or null ebdb-field-timestamp) nil) #s(cl-slot-descriptor fields nil (list-of ebdb-field-user) ((:documentation . "This slot contains all record fields except those built-in to record subclasses."))) #s(cl-slot-descriptor image nil (or null ebdb-field-image) nil) #s(cl-slot-descriptor notes nil (or null ebdb-field-notes) ((:documentation . "User notes for this contact."))) #s(cl-slot-descriptor dirty nil boolean ((:documentation . "Does this record have changed fields?"))) #s(cl-slot-descriptor cache nil (or null ebdb-cache) nil) #s(cl-slot-descriptor mail nil (list-of ebdb-field-mail) nil) #s(cl-slot-descriptor phone nil (list-of ebdb-field-phone) nil) ...] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (uuid 0 creation-date 1 timestamp 2 fields 3 image 4 notes 5 dirty 6 cache 7 mail 8 phone 9 ...)) (ebdb-record-organization ebdb-record-person) ((:uuid . uuid) (:creation-date . creation-date) (:timestamp . timestamp) (:fields . fields) (:image . image) (:notes . notes) (:dirty . dirty) (:cache . cache) (:mail . mail) (:phone . phone) ...) [#s(cl-slot-descriptor tracking-symbol ebdb-record-tracker symbol ((:documentation . "The symbol used to maintain a list of our instances. The instance list is treated as a variable, with new instances added to it.")))] [ebdb-record-tracker] ...)) [#s(cl-slot-descriptor uuid nil (or null ebdb-field-uuid) nil) #s(cl-slot-descriptor creation-date nil (or null ebdb-field-creation-date) nil) #s(cl-slot-descriptor timestamp nil (or null ebdb-field-timestamp) nil) #s(cl-slot-descriptor fields nil (list-of ebdb-field-user) ((:documentation . "This slot contains all record fields except those built-in to record subclasses."))) #s(cl-slot-descriptor image nil (or null ebdb-field-image) nil) #s(cl-slot-descriptor notes nil (or null ebdb-field-notes) ((:documentation . "User notes for this contact."))) #s(cl-slot-descriptor dirty nil boolean ((:documentation . "Does this record have changed fields?"))) #s(cl-slot-descriptor cache nil (or null ebdb-cache) nil) #s(cl-slot-descriptor mail nil (list-of ebdb-field-mail) nil) #s(cl-slot-descriptor phone nil (list-of ebdb-field-phone) nil) ...] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (uuid 0 creation-date 1 timestamp 2 fields 3 image 4 notes 5 dirty 6 cache 7 mail 8 phone 9 ...)) nil ((:uuid . uuid) (:creation-date . creation-date) (:timestamp . timestamp) (:fields . fields) (:image . image) (:notes . notes) (:dirty . dirty) (:cache . cache) (:mail . mail) (:phone . phone) ...) [#s(cl-slot-descriptor tracking-symbol ebdb-record-tracker symbol ((:documentation . "The symbol used to maintain a list of our instances. The instance list is treated as a variable, with new instances added to it.")))] [ebdb-record-tracker] ...) nil #s(#s(eieio--class ebdb-field-creation-date nil (#s(eieio--class ebdb-field-timestamp nil (#s(eieio--class ebdb-field "Abstract class for EBDB fields. Subclass this to produce real field types." nil [] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data ( ...)) (ebdb-field-domain ebdb-field-image ebdb-field-relation ebdb-field-timestamp ebdb-field-notes ebdb-field-phone ebdb-field-address ebdb-field-mail ebdb-field-role ebdb-field-name ...) nil [#s(cl-slot-descriptor actions nil (list-of cons) (...))] [nil] ...)) [#s(cl-slot-descriptor timestamp nil list nil)] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (timestamp 0 ...)) (ebdb-field-creation-date) ((:timestamp . timestamp)) [#s(cl-slot-descriptor actions nil (list-of cons) ((:documentation . "A list of actions which this field can perform. Each list element is a cons of string name and function name.")))] [nil] ...)) [#s(cl-slot-descriptor timestamp nil list nil)] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (timestamp 0 ...)) nil ((:timestamp . timestamp)) [#s(cl-slot-descriptor actions nil (list-of cons) ((:documentation . "A list of actions which this field can perform. Each list element is a cons of string name and function name.")))] [nil] ...) (23019 31212 570825 250000)) #s(#s(eieio--class ebdb-field-timestamp nil (#s(eieio--class ebdb-field "Abstract class for EBDB fields. Subclass this to produce real field types." nil [] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data ( ...)) (ebdb-field-domain ebdb-field-image ebdb-field-relation ebdb-field-timestamp ebdb-field-notes ebdb-field-phone ebdb-field-address ebdb-field-mail ebdb-field-role ebdb-field-name ...) nil [#s(cl-slot-descriptor actions nil (list-of cons) ((:documentation . "A list of actions which this field can perform. Each list element is a cons of string name and function name.")))] [nil] ...)) [#s(cl-slot-descriptor timestamp nil list nil)] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (timestamp 0 ...)) (ebdb-field-creation-date) ((:timestamp . timestamp)) [#s(cl-slot-descriptor actions nil (list-of cons) ((:documentation . "A list of actions which this field can perform. Each list element is a cons of string name and function name.")))] [nil] ...) (23019 31212 570888 35000)) nil nil nil nil #s(#s(eieio--class ebdb-cache nil nil [#s(cl-slot-descriptor name-string nil string ((:documentation . "The \"canonical\" name for the record, as displayed in the EBDB buffer."))) #s(cl-slot-descriptor alt-names nil (list-of string) ((:documentation . "A list of strings representing all other alternate names for this record."))) #s(cl-slot-descriptor organizations nil list ((:documentation . "A list of strings representing the organizations this record is associated with."))) #s(cl-slot-descriptor mail-aka nil list nil) #s(cl-slot-descriptor mail-canon nil list nil) #s(cl-slot-descriptor sortkey nil string nil) #s(cl-slot-descriptor database nil (list-of ebdb-db) ((:documentation . "The database(s) this record belongs to.")))] #s(hash-table size 65 test eq rehash-size 1.5 rehash-threshold 0.8125 data (name-string 0 alt-names 1 organizations 2 mail-aka 3 mail-canon 4 sortkey 5 database 6 ...)) nil ((:name-string . name-string) (:alt-names . alt-names) (:organizations . organizations) (:mail-aka . mail-aka) (:mail-canon . mail-canon) (:sortkey . sortkey) (:database . database)) [] [] ...) nil nil nil nil nil nil nil) nil ...), #("Elisabeth Verona" 0 16 (org-category "Contacts" fontified nil))
i got an error, see above
God I hate EIEIO error message. Give me a bit and I'll put up a new version of the function. Thanks for bearing with me.
Okay, I've updated the function above, will you give that a shot?
now I enter an other error state(the error is a bit truncated because terminal issues)
Debugger entered--Lisp error: (wrong-number-of-arguments #
stringp("File: ~/.emacs.d/ebdb" 2 1)
eieio-persistent-convert-list-to-object((ebdb-db-file "File: ~/.emacs.d/ebdb" :object-name "File: ~/.emacs.d/ebdb" :file "ebdb" :uuid (ebdb-field-uuid "ebdb-field-uuid-33044f0" :uuid "552b155a-1b7a-4cb0-a893-$
eieio-persistent-read("~/.emacs.d/ebdb" ebdb-db t)
ebdb-load()
ebdb-search-display(display ((ebdb-field-name "joakim") (organization "joakim") (ebdb-field-mail "joakim") (ebdb-field-notes "joakim") (ebdb-field-user "joakim") (ebdb-field-phone "joakim") (ebdb-field-addres$
ebdb(display "joakim" #
funcall-interactively(ebdb display "joakim" #
call-interactively(ebdb record nil)
command-execute(ebdb record)
funcall(#f(compiled-function (cmd) #<bytecode 0x484e761>) "ebdb")
(unwind-protect (funcall action x) (ivy-recursive-restore))
(save-current-buffer (set-buffer (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 13))) (unwind-protect (funcall a$
(prog1 (save-current-buffer (set-buffer (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 13))) (unwind-protect (fu$
(if (eq action 'identity) (funcall action x) (select-window (ivy--get-window ivy-last)) (prog1 (save-current-buffer (set-buffer (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'w$
(let ((collection (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 2))) (x (cond ((and (consp collection) (consp $
(progn (let ((collection (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 2))) (x (cond ((and (consp collection) $
(if action (progn (let ((collection (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) (aref ivy-last 2))) (x (cond ((and (consp c$
(let ((action (ivy--get-action ivy-last))) (if action (progn (let ((collection (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (list 'ivy-state ivy-last))) $
(if ivy-inhibit-action nil (let ((action (ivy--get-action ivy-last))) (if action (progn (let ((collection (progn (or (and (memq (type-of ivy-last) cl-struct-ivy-state-tags) t) (signal 'wrong-type-argument (l$
ivy-call()
(prog1 (unwind-protect (let ((fun (function ivy--minibuffer-setup)) setup-hook) (setq setup-hook (function (lambda nil (remove-hook 'minibuffer-setup-hook setup-hook) (funcall fun)))) (unwind-protect (progn ($
(let ((ivy-recursive-last (and (active-minibuffer-window) ivy-last)) (transformer-fn (plist-get ivy--display-transformers-list (or caller (and (functionp collection) collection)))) (ivy-display-function (if ($
(progn (let ((extra-actions (delete-dups (append (plist-get ivy--actions-list t) (plist-get ivy--actions-list this-command) (plist-get ivy--actions-list caller))))) (if extra-actions (progn (setq action (cond$
(progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '(:predicate :require-match :initial-input :history :preselect :def :keymap :update-fn :sort :action :unwind :re-build$
(let ((predicate (car (cdr (plist-member --cl-rest-- ':predicate)))) (require-match (car (cdr (plist-member --cl-rest-- ':require-match)))) (initial-input (car (cdr (plist-member --cl-rest-- ':initial-input)$
ivy-read("M-x " ("ebdb" "ffap" "ebdb-migrate-from-org-contacts" "list-packages" "shell" "Info-mode" "emacs-lisp-mode" "gnus-alias-select-identity" "org-release-time" "org-freeze-time" "whereiam" "revert-buffe$
counsel-M-x()
funcall-interactively(counsel-M-x)
call-interactively(counsel-M-x nil nil)
command-execute(counsel-M-x)
This is actually a problem with Emacs, not with EBDB, though coincidentally the problem in Emacs is also my fault :(
You'll have to pull Emacs master and rebuild, sorry about that.
okay! Ill try it out
now i get a really long backtrace. it is so long that i cant copy it here in full, because my terminals hang. here is an excerpt:
Debugger entered--Lisp error: (void-variable field) (car field) (assoc-string (car field) prop-fields) (if (assoc-string (car field) prop-fields) (ebdb-parse (cdr (assoc-string (car-field) prop-fields)) (cdr field)) (let ((val (car-field))) (cond ((equal org-contacts-address-property val) (save-current-buffer (set-buffer address-buffer) (insert (format "%s: %s\n" name (cdr field))))) ((equal org-contacts-birthday-property val) (make-instance 'ebdb-field-anniversary :date (calendar-gregorian-from-absolute (org-time-string-to-absolute (cdr field))) :object-name "birthday")) (t nil)))) (ebdb-record-insert-field record (if (assoc-string (car field) prop-fields) (ebdb-parse (cdr (assoc-string (car-field) prop-fields)) (cdr field)) (let ((val (car-field))) (cond ((equal org-contacts-address-property val) (save-current-buffer (set-buffer address-buffer) (insert (format "%s: %s\n" name (cdr field))))) ((equal org-contacts-birthday-property val) (make-instance 'ebdb-field-anniversary :date (calendar-gregorian-from-absolute (org-time-string-to-absolute (cdr field))) :object-name "birthday")) (t nil)))))
I replaced the earlier code with a new version! You'll have to copy-and-paste the code from the earlier comment again. Sorry if I didn't make that clear.
thanks, now the migration starts at least.
Now it stops during migration:
Debugger entered--Lisp error: (ebdb-unparseable "joakim")
signal(ebdb-unparseable ("joakim"))
(sorry for the snipping)
my contacts file is likely malformed. It would be nice with some kind of pointer to the original contacts file so i can find out which part is malformed and fix it
The only way I can see this happening is if you had the string "joakim" for a contact's email address. I'll add some more error checking into the process.
Did any of your contacts have a street address? How do they look?
I dont think i had any street adresses. the way i used org-contacts devolved into mostly doing isearch in the file. Thats why I wanted to try ebdb!
Okay, cool. Well let me know if there are any other conveniences you're missing. I'm going to add another layer of error checking to the org-contacts migration function, then close this issue.
cool!!!
currently i have my contacts in an org-contacts file. I'm looking at converting to ebdb. Is there any way to do this already?