Selective auto-capitalization in org-buffers

| categories: emacs, orgmode | tags:

I have been using auto-capitalize.el for a short time to automatically capitalize the beginning of sentences. I mostly like what it does, but in org-mode I tend to write short code blocks while still in org-mode, and it is pretty irritating for auto-capitalize to "fix" the capitalization of your code. Of course, I can type C-c ' to edit the block in its native mode, but I do not always want to do that.

Below, I illustrate an approach to turn off auto-capitalize-mode when the cursor is inside a code-block. Basically, we write a function that checks if you are in a src-block, and if auto-capitalize is on, turn it off. If you are not in the code-block, we turn auto-capitalize on if it is not on. Then we hook the function into post-command-hook, which will run it after every emacs command, including cursor movements.

Here is that code:

(defun dwiw-auto-capitalize ()
  (if (org-in-block-p '("src"))
      (when auto-capitalize
        (auto-capitalize-mode -1))
    (unless auto-capitalize
      (auto-capitalize-mode 1))))

(add-hook 'post-command-hook 'dwiw-auto-capitalize)
dwiw-auto-capitalize

It works! Now the minor mode turns on and off depending on where the cursor is in my org document.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

Machine-gradable quizzes in emacs+org-modex

| categories: emacs, org, education | tags:

In a previous post , we considered interactive quizzes in emacs and org-mode. Here we consider a variation of that idea with the aim of creating a machine-gradable quiz, i.e. a modern version of a scantron.

The idea is simple, we will let each question be represented by an org headline, and the choices will be clickable links that store the answer as a property in the headline. Each headline will have a unique id. The grading key will contain these ids and the correct answer, and a function will determine if the right answer was selected for each question.

Here is a simple link that will store the value of the link as a property. Note that the path must be at least two characters long to be recognized as a link, unless you wrap the link in double brackets. We will have the link insert a comment to indicate to the user what they chose. We do that because the PROPERTIES drawer is usually hidden, and it is not obvious it was changed.

(org-add-link-type 
 "mc" 
 (lambda (link)
   (save-restriction
     (org-narrow-to-subtree)
     (goto-char (point-max))
     (insert (concat
              (unless (bolp) "\n")
              (format "# you chose %s" link))))
       
   (org-entry-put (point) "ANSWER" link)))

Next, we add a subheading with some questions to test the link.

1 Some questions

1.1 question 1

What is 2 + 2?

a 1

b 2

c 4

1.2 question 2

What is 2 - 2?

a 0

b 2

c 4

2 Grading

We will store an alist of id and answer for each problem. To grade, we simple map over the alist, go to the section with the id, and compare the answers. When the answer is correct, we save a point, and when not, no point. We can use the org-mode machinery to jump to the problems and get the stored answer. We put some feedback at the end of the file to see what was right, and what was wrong.

(let* ((key '(("19C7BA30-A761-4C94-9F3B-E6010E263949" . "c")
              ("38FCCF3D-7FC5-49BF-BB77-486BBAA17CD9" . "a")))
       (MAX (length key))
       (points 0)
       (answer))
  
  (dolist (tup key)
    (save-excursion
      (org-open-link-from-string
       (format "id:%s" (car tup)))
      (setq answer (org-entry-get (point) "ANSWER"))
      (if (string= answer (cdr tup))
          (progn
            (setq points (+ 1 points))
            (goto-char (point-max))
            (insert (format "# id:%s: %s correct\n" (car tup) answer)))
        (goto-char (point-max))
        (insert (format "# id:%s: %s wrong (%s is correct)\n"
                        (car tup)
                        answer
                        (cdr tup))))))
  (goto-char (point-max))
  (insert (format
           "#+GRADE: %s" (/ (float points) (float MAX)))))

That works pretty well. I need to think about how to codify the key, since this would usually be stored in some file. We would also need to wrap the code block in a function that we could call easily. The org-id key is easy, but not very readable. It would make it easy to keep a database of these problems though.

Just for completeness, I want to save the key to a file, and use it. We simply write the alist in a file. Here are the contents, which are tangled to key.el. One alternative might be to have a solution copy of the quiz which has the answers in it, and we read the answers from the file.

(("19C7BA30-A761-4C94-9F3B-E6010E263949" . "c")
 ("38FCCF3D-7FC5-49BF-BB77-486BBAA17CD9" . "a"))

Now, we read it in like this. The rest of the code is basically the same.

(let* ((key (with-temp-buffer 
              (insert-file-contents "key.el")
              (read (current-buffer))))
       (MAX (length key))
       (points 0)
       (answer))
  
  (dolist (tup key)
    (save-excursion
      (org-open-link-from-string
       (format "id:%s" (car tup)))
      (setq answer (org-entry-get (point) "ANSWER"))
      (if (string= answer (cdr tup))
          (progn
            (setq points (+ 1 points))
            (goto-char (point-max))
            (insert (format "# id:%s: %s correct\n" (car tup) answer)))
        (goto-char (point-max))
        (insert (format "# id:%s: %s wrong (%s is correct)\n"
                        (car tup)
                        answer
                        (cdr tup))))))
  (goto-char (point-max))
  (insert (format
           "#+GRADE: %s" (/ (float points) (float MAX)))))

It is probably much easier to have a solution version of the quiz, and generate the key from it. For example, we can collect the ID and ANSWER from the problems in this file like this.

(let ((key '()))
  (org-map-entries
   (lambda ()
     (let ((id) (ans))
       (when (and
              (setq id (org-entry-get (point) "ID"))
              (setq ans (org-entry-get (point) "ANSWER")))
         (add-to-list 'key (cons id ans))))))
key)
(("38FCCF3D-7FC5-49BF-BB77-486BBAA17CD9" . "a")
 ("19C7BA30-A761-4C94-9F3B-E6010E263949" . "c"))

So, if we had a master solution file, we could read the key from there. That is the way to do this.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

Accessing web of science entry, citing and related articles from a doi in emacs

| categories: emacs, orgmode, org-ref | tags:

I have been investigating how to more deeply integrate online resources, scientific bibliographies and writing in Emacs. One feature I have been wanting is integration with Web Of Science , especially to find citing and related articles from a DOI. This service is not free, but is available at many places where science is done. I came across this API http://wokinfo.com/media/pdf/OpenURL-guide.pdf to make links to the things I am interested in here. Based on that document, here are three links based on a 10.1021/jp047349j that take you to different Web Of Science (WOS) pages.

  1. go to article in WOS: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info:doi/10.1021/jp047349j
  2. citing articles: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F10.1021/jp047349j&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.citing=yes
  3. related articles: http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F10.1021/jp047349j&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.related=yes

These are pretty easy to construct, so we can write functions that will create them and open the url in our browser. There are some other options that could be considered, but since we usually have a doi, it seems like the best way to go for creating the links. Here are the functions.

(defun doi-utils-wos (doi)
  "Open Web of Science entry for DOI"
  (interactive "sDOI: ")
  (browse-url
   (format
    "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info:doi/%s" doi)))

(defun doi-utils-wos-citing (doi)
  "Open Web of Science citing articles entry. May be empty if none are found"
  (interactive "sDOI: ")
  (browse-url
   (concat
    "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F"
    doi
    "&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.citing=yes")))

(defun doi-utils-wos-related (doi)
  "Open Web of Science related articles page."
  (interactive "sDOI: ")
  (browse-url
   (concat "http://ws.isiknowledge.com/cps/openurl/service?url_ver=Z39.88-2004&rft_id=info%3Adoi%2F"
           doi
           "&svc_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Asch_svc&svc.related=yes")))
doi-utils-wos-related

These are exciting because they could be integrated into org-ref or doi-utils to make citations in an org-document even more functional! There are some other interesting things here about Scopus and issues with ISI that I note for reference to future me.

Some of these are now included in jmax-bibtex.el and doi-utils.org .

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

words - some interesting utilities for text in emacs

| categories: emacs | tags:

Recently I posted about an org-link with a menu. Here I extend the idea to a command that will do approximately the same thing. The idea is a command called words that will be run interactively. It will grab the word at point, or operate on a selected region, and then offer a menu to lookup the definition, find it in a thesaurus, google it, etc… We structure the code so you can add functions to it later, without directly modifying this code. The only limitation of this code is that the functions must have a signature with no arguments. That does not seem terribly limiting, as we can check for a region, and use it if we want (see the words-google function).

(defun words-dictionary ()
  (interactive)
  (browse-url
   (format
    "http://dictionary.reference.com/browse/%s?s=t"
    (thing-at-point 'word))))

(defun words-thesaurus ()
  (interactive)
  (browse-url
   (format
    "http://www.thesaurus.com/browse/%s"
    (thing-at-point 'word))))

(defun words-google ()
  (interactive)  
  (browse-url
   (format
    "http://www.google.com/search?q=%s"
    (if (region-active-p)
        (url-hexify-string (buffer-substring (region-beginning)
                                             (region-end)))
      (thing-at-point 'word)))))


(defvar words-funcs '()
 "functions to run in `words'. Each entry is a list of (key menu-name function).")

(setq words-funcs
  '(("d" "ictionary" words-dictionary)
    ("t" "hesaurus" words-thesaurus)
    ("g" "oogle" words-google)))
 

(defun words ()
  (interactive)
   (message
   (concat
    (mapconcat
     (lambda (tup)
       (concat "[" (elt tup 0) "]"
               (elt tup 1) " "))
     words-funcs "") ": "))
   (let ((input (read-char-exclusive)))
     (funcall
      (elt 
       (assoc
        (char-to-string input) words-funcs)
       2))))
words

This works nicely. Now, let us add a new function that looks up the word or selection on twitter. We just define a new function, and add the menu selection to the words-func variable.

(defun words-twitter ()
  (interactive)
  (browse-url
   (format
    "https://twitter.com/search?q=%s"
    (if (region-active-p)
        (url-hexify-string (buffer-substring (region-beginning)
                                             (region-end)))
      (thing-at-point 'word)))))

(add-to-list 'words-funcs
  '("w" "twitter" words-twitter)
  t) ; append
d ictionary words-dictionary
t hesaurus words-thesaurus
g oogle words-google
w twitter words-twitter

Finally, the most complicated idea: spelling and grammar. I know there is flyspell, and such, but they are build on an ancient dictionary. Here, for fun, we explore a web api. This next function is not a trivial one, and I will not explain it here beyond saying it sends a selection of text to a url, gets an xml response back, and that response is parsed and printed to a buffer by this function. The main point is to illustrate we can do interesting things with a selection of text!

(defun words-atd ()
  "Send paragraph at point to After the deadline for spell and grammar checking."
  (interactive)
  
  (let* ((url-request-method "POST")
         (url-request-data (format
                            "key=some-random-text-&data=%s"
                            (url-hexify-string
                             (thing-at-point 'paragraph))))
         (xml  (with-current-buffer
                   (url-retrieve-synchronously
                    "http://service.afterthedeadline.com/checkDocument")
                 (xml-parse-region url-http-end-of-headers (point-max))))
         (results (car xml))
         (errors (xml-get-children results 'error)))
    
    (switch-to-buffer-other-frame "*ATD*")
    (erase-buffer)
    (dolist (err errors)
      (let* ((children (xml-node-children err))
             ;; for some reason I could not get the string out, and had to do this.
             (s (car (last (nth 1 children))))
             ;; the last/car stuff doesn't seem right. there is probably
             ;; a more idiomatic way to get this
             (desc (last (car (xml-get-children children 'description))))
             (type (last (car (xml-get-children children 'type))))
             (suggestions (xml-get-children children 'suggestions))
             (options (xml-get-children (xml-node-name suggestions) 'option))
             (opt-string  (mapconcat
                           (lambda (el)
                             (when (listp el)
                               (car (last el))))
                           options
                           " ")))

        (insert (format "** %s ** %s
Description: %s
Suggestions: %s

" s type desc opt-string))))))

(add-to-list 'words-funcs
  '("s" "spell/grammar" words-atd)
  t) ; append
words-atd

My final words menu looks like:

If I have the cursor in the previous paragraph, run the words command and select "s" I get a buffer with these contents:

#+BEGINEXAMPLE

1 flyspell ** (spelling)

Description: (Spelling) Suggestions: flywheel flyball

2 are build on ** (grammar)

Description: (Auxiliary Verb Agreement) Suggestions: are built on

3 api ** (spelling)

Description: (Spelling) Suggestions: app ape apt ai ami

4 url ** (spelling)

Description: (Spelling) Suggestions: urn ure curl hurl burl

5 xml ** (spelling)

Description: (Spelling) Suggestions: xl ml

6 selection ** (suggestion)

Description: (Complex Expression) Suggestions: choice

7 an xml ** (grammar)

Description: (Wrong article) Suggestions: a xml

8 a selection of ** (grammar)

Description: (Hidden Verbs) Suggestions:

9 is parsed ** (grammar)

Description: (Passive voice) Suggestions:

10 selection ** (suggestion)

Description: (Complex Expression) Suggestions: choice

11 a selection of ** (grammar)

Description: (Hidden Verbs) Suggestions: #+ENDEXAMPLE

It might be nice to link back to those words, so you could click on them and fix them, but that is a beyond today's goal. In summary, today we looked at a framework to create a user-modifiable menu of commands that are launched from a single command. Here we called the command words, and then built up some different things we might want to do with the word or selection at point. While you can of course just remember the individual commands, remembering one command and then being prompted might have some advantages.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter

Using Pymacs to integrate Python into Emacs

| categories: emacs, python | tags:

Pymacs is a project that aims to integrate Python into Emacs, and vice versa. In this post, I am going to examine the Python into Emacs integration. I cloned the git repository, ran make install, and setup my init.el file like this, as suggested in the manual.

(add-to-list 'load-path (expand-file-name "Pymacs" starter-kit-dir))
(require 'pymacs)
(autoload 'pymacs-apply "pymacs")
(autoload 'pymacs-call "pymacs")
(autoload 'pymacs-eval "pymacs" nil t)
(autoload 'pymacs-exec "pymacs" nil t)
(autoload 'pymacs-load "pymacs" nil t)
(autoload 'pymacs-autoload "pymacs")

Pymacs provides some mapping of Python modules to emacs-lisp functions. You load modules in emacs-lisp, and then a dash-mangled version of the Python functions are available, in emacs lisp. Here is an example. We will load numpy, and find the maximum element of an array. For comparison, here is the Python script.

import numpy as np
print np.max(np.array([[1, 1], [2, 4]]))
4

Now, the corresponding emacs version using Pymacs.

(pymacs-load "numpy" "np-")
(np-max (np-array '((1 1) (2 4))))
4

Neat! The dot notation is basically replaced with dash notation, and we use a lisp list as the argument instead of an array. Otherwise, this looks almost identical. Now, let us consider something more complicated, and get the determinant of the array. We add a PREFIX to the load statement for numpy.linalg similar to what we would do in Python:

import numpy as np
import numpy.linalg as la
print la.det(np.array([[1, 1], [2, 4]]))
2.0

And in emacs-lisp:

(pymacs-load "numpy" "np-")
(pymacs-load "numpy.linalg" "la-")
(la-det (np-array '((1 1) (2 4))))
2.0

We can call functions from matplotlib to make a figure. For example:

(pymacs-load "matplotlib.pyplot" "plt-")
(let* ((x  '(1 2 3 4))
       (y  (mapcar (lambda (z) (* z z)) x)))
  (plt-plot x y)
  (plt-xlabel "x values")
  (plt-ylabel "x$^2$")
  (plt-savefig "plt-pymacs.png"))

This was a little subtle. It was necessary to save the lists as variables, and use the variables in the plot command.

I am not sure what this offers over just having a Python block present in org-mode though. Maybe it is more useful in emacs-lisp libraries where you want to bring in some numerical analysis. Or if you have some custom library of Python you would like to use in elisp. Here is a highly contrived example. Suppose we have a Python module with this special function that converts an argument to "J":

def special_func(x):
    return "J"

In Python, we might use it like this:

import my_python as mp
print [mp.special_func(x) for x in [1, 2, 3]]
['J', 'J', 'J']

We can import the module, and use the function in emacs-lisp too. The underscore in the function name is turned into a dash, which is a little confusing, but it works otherwise.

(pymacs-load "my_python" "mp-")
(mapcar 'mp-special-func '(1 2 3))
J J J

It does not seem possible to do everything though. For example, It is not clear how to pass functions through either side. For example, this does not work for fsolve, although it seems like it should.

(pymacs-load "scipy.optimize" "so-")

(defun objective (x)
  (- x 5))

(so-fsolve 'objective 3)

I get an error like this:

Pymacs loading scipy.optimize...done
pymacs-report-error: Python: Emacs: "(wrong-type-argument number-or-marker-p (pymacs-python . 47))"

The Python equivalent is here:

from scipy.optimize import fsolve
def objective(x):
    return x - 5

print fsolve(objective, 3)
[ 5.]

There is an open question on StackOverflow here on this issue. Overall, I find the project very interesting. It would be awesome if you could extend emacs more easily in other languages, especially scripting languages such as Python that have numerical and plotting capabilities. Right now, this is possible in limited ways. For example, Xah Lee describes an approach where an arbitrary script can take data on stdin, process it, and output the results to stdout. Emacs can capture this and use it to modify the buffer. This uses the shell-command features in Emacs. These scripts could be written in Python, Perl, Ruby, etc… This seems like a simpler and more flexible approach, except that it requires creating the shell commands and putting them on the executable path (as opposed to having Python modules on a PYTHONPATH). These lack the deep integration of documentation you get with emacs-lisp and Python functions.

Copyright (C) 2014 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.7c

Discuss on Twitter
« Previous Page -- Next Page »