YAT - yet another template strategy

| categories: orgmode, emacs | tags:

I have another need for a template that is dynamically evaluated. I previously wrote about this here , and today I am going to do a variation of the theme. We will still use a syntax of $(expression), but a new approach to evaluating the expression. I saw this interesting function to evaluate and replace an s-expression in a buffer Eval and Replace - Emacs Redux . I am going use that to replace a template expression in a string, with a little variation to avoid replacing non-sexp variations, e.g. $(. Here we go.

(defun eval-and-replace ()
  "Replace the preceding sexp with its value."
  (interactive)
  (backward-kill-sexp)
  (condition-case nil
      (princ (eval (read (current-kill 0)))
             (current-buffer))
    (error (message "Invalid expression")
           (insert (concat "$" (current-kill 0))))))

(defun j-format (s)
  "Replace all instances of $(expression) in S with the evaluated
expression."
  (with-temp-buffer
    (insert s)
    (goto-char (point-min))
    (while (re-search-forward "$(" nil t)
      (backward-char)
      (when (sexp-at-point)
        ;; get rid of the $
        (delete-char -1)
        ;; go to the end of the sexp and then eval-and-replace it.
        (end-of-sexp)
        (eval-and-replace)))
    ;; return the formatted text.
    (buffer-string)))


(let ((some-var "You got me"))
  (j-format "Test of 4 + 5 = $(+ 4 5). $(  $(foobar). $(progn (setq x 5) \"\")
and then we have 2x=$(prin1 (* 2 x)).

some-var = $(print some-var)"))
Test of 4 + 5 = 9. $(  $(foobar).
and then we have 2x=10.

some-var = You got me

That seems pretty ok. I obviously have not tested it extensively, but it looks pretty promising.

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

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Saving the current restriction and restoring it while following links

| categories: orgmode, emacs | tags:

On the org-mode mailing list there has been some discussion about following id links. The issue is that if your buffer is narrowed, clicking on the link does not change the restriction to actually take you to the entry. This is debatably desirable. If I click on a link, I want it to go where it points. But, I might also like to go back to my narrowed view. So here consider how to save the state of narrowing, and restore it. We modify the function that opens an id link to save the restriction, and widen the buffer if necessary.

Saving the restriction seems easy, we just save a marker to point, and the point-min and point-max. We save the marker for a convenient way to get the buffer, and perhaps the actual point. We advise the C-c & function to restore the restriction after we leave it. This should fix the restriction in whatever buffer we undid it in.

Here is the code that seems to work for me. Thanks to Rasmus for the idea on saving the restriction data.

(defvar *saved-restriction* nil
 "A global var containing the current restriction.
Returns (current-buffer point-min point-max")

(defun save-current-restriction ()
  "Save the current restriction at point."
  (setq *saved-restriction*
        (if (buffer-narrowed-p)
            (list (current-buffer) (point-min) (point-max))
          nil)))

(defun restore-saved-restriction ()
  "Restore the last saved restriction."
  (when *saved-restriction*
    (set-buffer (car *saved-restriction*))
    (narrow-to-region (nth 1 *saved-restriction*)
                      (nth 2 *saved-restriction*)))
  (setq *saved-restriction* nil))

;' actually modify this function to save the restriction, and widen if needed.
(defun org-id-open (id)
  "Go to the entry with id ID."
  (org-mark-ring-push)
  (let ((m (org-id-find id 'marker))
        cmd)
    (unless m
      (error "Cannot find entry with ID \"%s\"" id))
    ;; Use a buffer-switching command in analogy to finding files
    (setq cmd
          (or
           (cdr
            (assq
             (cdr (assq 'file org-link-frame-setup))
             '((find-file . switch-to-buffer)
               (find-file-other-window . switch-to-buffer-other-window)
               (find-file-other-frame . switch-to-buffer-other-frame))))
           'switch-to-buffer-other-window))
    (if (not (equal (current-buffer) (marker-buffer m)))
        (funcall cmd (marker-buffer m)))
    (save-current-restriction)
    (when (> m (point-max))
      (widen))
    (goto-char m)
    (move-marker m nil)
    (org-show-context)))


;; And we advise the function going back to restore the restriction.
(defadvice org-mark-ring-goto (after restore-my-restriction () activate)
  "Restore narrowing."
  (restore-saved-restriction))
org-mark-ring-goto

This seems to preserve restrictions in the current buffer and in other buffers, as long as I use C-c & to invoke org-mark-ring goto. I am not sure how easy it would be to make this work for all links. Each link has its own function for following so I am not sure we can easily get them all to do this unless there is some high level function to advise like org-mouse-down-mouse or something similar. It also has the limitation that the restoration only occurs using org-mark-ring-goto, unless you specifically run the (restore-saved-restriction) function yourself. That could be made an interactive function for that purpose. Otherwise, this seems like a reasonable approach.

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

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

Line numbers in org-mode code blocks

| categories: orgmode, emacs | tags:

Some of my students have wanted to show line numbers in code blocks. This is especially useful for when you run a Python block, and you get an error message with a line number in it. Right now, to figure out which line that is, you have to into the code block, type C-c ' to get into edit mode, and turn line numbers on. We look into how to achieve that here.

You may want to see the video here: https://www.youtube.com/watch?v=kinWijGzXms .

First, we need to get the region that is the code block. We can find some info in the org-element, but, the :begin and :end include lines we don't want, like the header lines, and the results. But, we can get the beginning, and maybe from there search forward to the block. Run this code block to see where the point goes.

;; a boring comment

(progn
  (+ 40 2))

(goto-char (org-element-property :begin (org-element-context)))
(re-search-forward (regexp-quote (org-element-property :value (org-element-context))))
(goto-char (match-beginning 0))
;; number of lines in block. The last carriage return doesn't count.
(1- (length (s-split "\n" (org-element-property :value (org-element-context)))))
9

So, we can get the number of lines, and move the point to the first line. For numbers, we will use overlays. Here is a simple way to put a number at the beginning of a line.

(let (ov)
  (beginning-of-line)
  (setq ov (make-overlay (point) (point)))
  (overlay-put ov 'before-string "1"))
1

The next thing to do is make a function that puts a number at the beginning of a line. We might as well store these overlays in a variable, so they are easy to remove later. This is just for exploration of how to do it. Later we combine all these pieces together.

(defvar number-line-overlays '()
  "List of overlays for line numbers.")

(make-variable-buffer-local 'number-line-overlays)

(defun number-line (N)
 "Put an overlay at the beginning of a line."
  (beginning-of-line)
  (let (ov)
    (setq ov (make-overlay (point) (point)))
    (overlay-put ov 'before-string (format "%3s" (number-to-string N)))
    (add-to-list 'number-line-overlays ov)))

(number-line 4)
#<overlay from 1782 to 1782 in blog.org>

That looks promising. Let's make a function to clear those overlays. It is so easy it may not even be worth writing.

(defun number-line-clear ()
  (mapc 'delete-overlay number-line-overlays)
  (setq number-line-overlays '()))

(number-line-clear)

Finally, we are ready to hack up the code block numbering code. The numbers will not automatically update, so we will write a function that numbers the block, but only temporarily. Any key press will get rid of the numbers so we can get back to work. I am going to go ahead and make this a stand-alone function and block.

(defvar number-line-overlays '()
  "List of overlays for line numbers.")

(make-variable-buffer-local 'number-line-overlays)

(defun number-line-src-block ()
  (interactive)
  (save-excursion
    (let* ((src-block (org-element-context))
           (nlines (- (length
                       (s-split
                        "\n"
                        (org-element-property :value src-block)))
                      1)))
      (goto-char (org-element-property :begin src-block))
      (re-search-forward (regexp-quote (org-element-property :value src-block)))
      (goto-char (match-beginning 0))

      (loop for i from 1 to nlines
            do
            (beginning-of-line)
            (let (ov)
              (setq ov (make-overlay (point) (point)))
              (overlay-put ov 'before-string (format "%3s" (number-to-string i)))
              (add-to-list 'number-line-overlays ov))
            (next-line))))

  ;; now read a char to clear them
  (read-key "Press a key to clear numbers.")
  (mapc 'delete-overlay number-line-overlays)
  (setq number-line-overlays '()))

(number-line-src-block)

I am not sure how to get the numbers to automatically update smoothly like they do in linum-mode. That code uses a lot of hooks to make updates work, and embeds them in a minor mode to get rid of them. It also puts them in the fringe I think, but it is not clear how that is done.

We could modify what happens after the numbers are put on, e.g. pressing numbers might jump to a line, or some other kind of functionality. I don't have a critical need for this right now, so I didn't explore it more. Let me know if you have any good ideas for it!

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

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter

A checkbox list in org-mode with one value

| categories: orgmode, emacs | tags:

A while ago I had a need for a checklist in org-mode where only one value would be checked at a time. Like a radio button in a browser form. That isn't as far as I know a feature yet, but it was not hard to achieve thanks to the org-element api. My simple idea is to make a function that will be added to the org-checkbox-statistics-hook. The function will uncheck all the boxes, and recheck the one you just clicked with a hybrid of manipulating the cursor and inserting characters with org-element code. We will use an attribute on the checklist to indicate it is a "radio" list. This seems like a feature that might already exist, but I couldn't find it.

Here is the code we run. First, we make sure we are on a plain list that has an attr_org property of ":radio", that way this won't apply to all lists, just the radio lists. Then, we loop through each element in the structure, and if it is checked, we replace [X] with [ ]. Then, we reinsert the X and delete a space, which puts [X] where we originally clicked, or used C-c C-c. Finally, we add it to the hook, so it only gets run when a checkbox is changed via clicking with org-mouse, or C-c C-c. Of course, this doesn't work if you type X in the box.

(require 'dash)
(defun check-hook-fn ()
  (when (-contains? (org-element-property
                     :attr_org
                     (org-element-property :parent (org-element-context)))
                    ":radio")
    (save-excursion
      (loop for el in (org-element-property :structure (org-element-context))
            do
            (goto-char (car el))
            (when (re-search-forward "\\[X\\]" (line-end-position) t)
              (replace-match "[ ]"))))
    (forward-char)
    (insert "X")
    (delete-char 1)))

(add-hook 'org-checkbox-statistics-hook 'check-hook-fn)
check-hook-fn

Here is a regular checklist. You can check as many as you want.

  • [X] one
  • [X] two
  • [ ] three

Now, here is a radio checklist. Only one item at a time can be checked. Nice!

  • [ ] a
  • [ ] b
  • [X] c

It is worth noting here that if we put a name on the list, it becomes an addressable data source. First we need this convenient function to get the data associated with a named list.

(defun org-get-plain-list (name)
  "Get the org-element representation of a plain-list with NAME."
  (catch 'found
    (org-element-map
        (org-element-parse-buffer)
        'plain-list
      (lambda (plain-list)
        (when
            (string= name (org-element-property :name plain-list))
          (throw 'found plain-list))))))
org-get-plain-list

Now, let's use that to get the value of the checked item in the "test" list. We define the item as everything after the [X] and get it from a regular expression match.

(defun get-radio-list-value (list-name)
  "Return the value of the checked item in a radio list."
  (save-excursion
    (loop for el in (org-element-property
                     :structure
                     (org-get-plain-list list-name))
          if (string= (nth 4 el) "[X]")
          return (progn
                   (let ((item (buffer-substring (car el) (car (last el)))))
                     (string-match "\\[X\\]\\(.*\\)$" item)
                     (match-string 1 item))))))

(get-radio-list-value "test")
c

Perfect. This has lots of potential applications. Data collection and quizzes come to mind, with associated ability to autograde and aggregate the data!

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

org-mode source

Org-mode version = 9.0

Discuss on Twitter

A framework for automated feedback with Python and org-mode

| categories: python, emacs | tags:

Autolab is an autograding service that automatically grades code assignments. It uses a program to evaluate a program on a secure virtual system. Using this requires you to run a server, and run code from students. I have never liked that because it is hard to sandbox code well enough to prevent malicious code from doing bad things. Autolab does it well, but it is a heavy solution. Here we explore a local version, one that is used to test for correctness, and not for grading. Here, if you are malicious, you reap what you sow…

The basic idea I am working towards is that Emacs will provide content to be learned (through org-mode) with active exercises. The exercises will involve a code block, and the user will run a command on their code (or an advised C-c C-c) that checks the solution for correctness. A user will be able to see the solution, and maybe get hints.

Suppose we have a problem to solve \(e^x = 3\). This is a simple problem to solve, and here is a solution.

from scipy.optimize import fsolve
import numpy as np

def objective(x):
    return np.exp(x) - 3

def solve():
    return fsolve(objective, 3)

print solve()
[ 1.09861229]

We would like to test this for correctness. We code this in a function-based form because we will later use the function solve to test for correctness. Let's see how we could test it with a test function. We will use exec on a string representing our code to get it into our namespace. I don't see a security issue here. You are writing the code! Eventually, we will be passing code to the test framework this way from an org-mode source block.

import unittest
TOLERANCE = 1e-5

s = '''from scipy.optimize import fsolve
import numpy as np

def objective(x):
    return np.exp(x) - 3

def solve():
    return fsolve(objective, 3)[0]

print solve()'''

def test_solve(s):
    exec s in globals()
    if (abs(np.log(3) - solve()) <= TOLERANCE):
        print('Correct')
    else:
        print('incorrect')

test_solve(s)
1.09861228867
Correct

Next, we need to think about how we could generate an import statement from a code block name, import in python, and run a test function. We can assume that the test code will be in a file called "test_%s.py" on your python path. Here are the contents of test_solve.py.

import numpy as np
TOLERANCE = 1e-5

def solve_solution():
    from scipy. optimize import fsolve
    import numpy as np

    def objective(x):
        return np.exp(x) - 3

    return fsolve(objective, 3)[0]


def test_solve(s):
    exec s in globals()
    if (abs(solve_solution() - solve()) <= TOLERANCE):
        print('Correct!')
    else:
        print('Incorrect')

Now, we can import that, and use the functions. Here is the Python script we need to run to test it.

import test_solve
test_solve.test_solve('''
from scipy. optimize import fsolve
import numpy as np

def objective(x):
    return np.exp(x) - 3

def solve():
    return fsolve(objective, 3)[0]

print solve()''')
1.09861228867
Correct!

Now, an elisp block to do that. One way to do this is to just run a shell command passing the string to a python interpreter. This is a short way away from an Emacs command now.

(let* ((string "import test_solve
test_solve.test_solve('''
from scipy. optimize import fsolve
import numpy as np

def objective(x):
    return np.exp(x) - 3

def solve():
    return fsolve(objective, 3)[0]

print solve()''')"))
  (shell-command-to-string (format "python -c \"%s\"" string)))
1.09861228867
Correct!

Ok, now to wrap it all up in a function we can run from Emacs in a code block to test it. With the cursor in a code block, we get the name, and build the python code, and run it. The function is more complex than I anticipated because I end up running the code block essentially twice, once to get a results block and once to get the test results. For short problems this is not an issue. I also add the test results in a way that is compatible with the current results.

(defun check ()
  (interactive)
  (let* ((src-block (org-element-context))
         (name (org-element-property :name src-block))
         (code (org-element-property :value src-block))
         (end (org-element-property :end src-block))
         (results)
         (template (format "import test_%s
test_%s.test_%s('''%s''')" name name name code))
         (output (format
                  "\n%s\n"
                  (s-join
                   "\n"
                   (mapcar
                    (lambda (s)
                      (if (s-starts-with? ":" s)
                          s
                        (concat ": " s)))
                    (s-split
                     "\n"
                     (shell-command-to-string
                      (format "python -c \"%s\"" template))))))))
    ;; execute block as normal
    (org-babel-execute-src-block)
    ;; and add some output to the Results block
    (if (org-babel-where-is-src-block-result)
        (progn
          (goto-char (org-babel-where-is-src-block-result))
          (setq results (org-element-context))
          ;; delete results line
          (kill-line)
          ;;  delete the results
          (setf (buffer-substring (org-element-property :begin results)
                                  (org-element-property :post-affiliated results))
                "")
          ;; paste results line back
          (yank)
          ;; and the output from your code
          (insert output))
      (message "%s" output))))
check

Now, we use a named src-block so we can call M-x check in it, and check the answer.

from scipy.optimize import fsolve
import numpy as np

def objective(x):
    return np.exp(x) - 3

def solve():
    return fsolve(objective, 3)

print solve()
[ 1.09861229]
Correct!

I would like to be able to provide a solution function that would show a user my solution they were tested against. Python provides the inspect module that can do this. Here is how we get the code in Python.

import inspect
import test_solve

print inspect.getsource(test_solve.solve_solution)
def solve_solution():
    from scipy. optimize import fsolve
    import numpy as np

    def objective(x):
        return np.exp(x) - 3

    return fsolve(objective, 3)[0]

This makes it easy to wrap up a function in emacs that will show this from at src block. We just get the block name, and build the python code and execute it here.

(defun show-solution ()
  (interactive)
  (let* ((src-block (org-element-context))
         (name (org-element-property :name src-block))
         (template (format  "import inspect
import test_%s

print inspect.getsource(test_%s.%s_solution)" name name name)))
    (switch-to-buffer-other-window (get-buffer-create "solution"))
    (erase-buffer)
    (insert (shell-command-to-string
             (format "python -c \"%s\"" template)))
    (python-mode)))
show-solution

That summarizes the main features. It allows me to write a test module that has some name conventions to define a solution function, and a test function. Emacs can generate some boilerplate code for different problem names, and run the test to give the user some feedback. Most of the code in this post would not be directly visible to a user, it would be buried in a python module somewhere on the path, and in elisp files providing the glue. I am not sure how much obfuscation you can put in the python files, e.g. just providing byte-compiled code, so it is less easy to just read it. That is not as big a deal when it is just a study guide/feedback system.

From an authoring point of view, this seems pretty good to me. It is feasible I think to write an org-source document like this with tangling for the test modules, and an export to org that does not have the solutions in it. The only subtle point might be needing to alter Python paths to find the test modules if they aren't installed via something like pip.

I think this is pretty flexible, and could handle problems that take arguments, e.g. write a function that sorts a list. Here is a simple example of that. First we write the test_sort.py file with a solution, and some tests.

def sort_solution(LIST):
    return LIST.sort()

def test_sort(s):
    exec s in globals()
    if sort([3, 4, 2]) == [2, 3, 4]:
        print('passed test 1')
    if sort(['z', 'b']) == ['b', 'z']:
        print('passed test 2')
def sort(LIST):
    s = sorted(LIST)
    return s
passed test 1
passed test 2

Maybe it would make sense to use unittests, or nose or some other testing framework if it makes writing the tests easier. Another day.

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

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter
« Previous Page -- Next Page »