Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I am teaching students how to use Mathematica. I want to create a problem set that has cells with a certain style (probably Input or some new style that is a variant of Input) locked down so that the students can not see them at all or just see an unevaluatable placeholder instead. But I also want a cell within the notebook that permits someone with a password (like me) to make these hidden cells visible and evaluatable.

Extra credit: it would be cool if I could relock these cells so that they reverted to their hidden form.

Concession: Maybe the unlocking cell does not have to be in the notebook but could be a function in an external notebook that worked on the problem set notebook.

Right now I accomplish the basic idea by having two notebooks, one with the answers and one without them. But this is ugly and leads to synchronization errors as I change my mind.

Ideas welcome. Obviously, simpler is better.

share|improve this question
    
Are your students knowledgable enough to peek at the cell expression? –  rm -rf Jan 10 '13 at 18:44
2  
Your current approach of having two notebooks could be made robust by putting a button at the top of the master Q/A notebook that generates a question-only notebook when you need to distribute the questions. –  JxB Jan 10 '13 at 19:02
    
Yes, my students are probably smart enough to peek at the cell expression. Or at least I will give them credit for being that industrious. –  Seth Chandler Jan 10 '13 at 19:22
    
Yes, JxB, I can always Option-Select and then delete the Input cells or whatever Style I give to cells that Students Should Not See. But I still wonder if there is not a one-document solution. I suppose the issue is going to be whether we can come up with a one-document solution that is simple enough so that the document-management advantages outweigh the extra overhead. –  Seth Chandler Jan 10 '13 at 19:26
2  
With TaggingRules and Encode and so on one can probably do this, but it is quite some work I guess ... –  Rolf Mertig Jan 10 '13 at 19:58

3 Answers 3

I don't claim this will do everything you want, but this presentation by Jason Harris on Advanced Typesetting includes a program that will Encrypt code and allow it to be unlocked with a short and simple password.

Apparently it was used live during a workshop presentation to encourage those in attendance to attempt the problem(s) at hand without first seeing the solution(s). (Did anyone on here attend??)

Note: You may have to dismiss a message box when launching the notebook. Everything works fine for me on v9.

share|improve this answer
    
That's a nice presentation and looks like it should be sufficient for the OP's needs. –  rm -rf Jan 11 '13 at 2:29

This is a start that could be turned into a style and a package, and improved, etc.

First, some function to encrypt a string with a key. I'm not good at this, I only knew how to do it with Encode

encode[expr_, password_String] :=
 With[{fnameSource = FileNameJoin[{$TemporaryDirectory, "encrSource"}],
       fnameDest = FileNameJoin[{$TemporaryDirectory, "encrDest"}]},
  Block[{buff},
   Put[Unevaluated[buff = expr], fnameSource];
   Encode[fnameSource, fnameDest, password];
   With[{res = StringJoin@Import[fnameDest, {"Binary", "Character8"}]},
    Scan[DeleteFile, {fnameSource, fnameDest}];
    res
    ]
   ]]

To decode

decode[expr_String, password_String] := 
 With[{fname = FileNameJoin[{$TemporaryDirectory, "decr"}]},
      Export[fname, Characters@expr, {"Binary", "Character8"}];
      Block[{buff},
       Quiet@Check[Get[fname, password], buff = $Failed];
   With[{res = buff},
    DeleteFile@fname;
    res]
   ]]

The idea is simple: to provide a button in a CellFrameLabel, that encrypts or decrypts the cell's content

Let's overload our previous functions, letting them take a cell object

encode[co_CellObject] := 
 With[{pass = InputString["Please, enter the password"]},
  CurrentValue[co, {TaggingRules, "encoded"}] = True;
  NotebookWrite[co,
   Replace[NotebookRead[co], 
    Cell[data_, opts___] :> 
     Cell[BoxData@
       ToBoxes@Interpretation["Hidden cell", 
         Evaluate@encode[data, pass]], opts]]];
  SelectionMove[EvaluationNotebook[], After, Cell];
  ]

decode[co_CellObject] := 
 With[{pass = InputString["Please, enter the password"]},
  With[{newCell = Replace[
      NotebookRead[co],
      Cell[BoxData[int_], opts___] :> 
       Cell[decode[ToExpression@int, pass], opts]]},
   (CurrentValue[co, {TaggingRules, "encoded"}] = False;
   NotebookWrite[co, newCell];
   SelectionMove[EvaluationNotebook[], After, Cell]) /; 
    MatchQ[newCell, Cell[Except[$Failed], ___]]]
  ]

decode::fail = "Failed to decode cell";
decode[_CellObject] := (Message[decode::fail]; $Failed /; False)

These are the options an "encryptable cell" should have. Just a tagging rule with the state (encrypted or not encrypted), and the cell frame label specifications.

opts = Sequence[
  CellFrameLabels -> {{None, None}, {None, ToBoxes@Dynamic[
       CurrentValue[{TaggingRules, "encoded"}] /. {
          False :> {"Encode", encode},
          True :> {"Decode", decode}} /. {butName_, fun_} :>
         Button[butName, 
          fun[SelectionMove[EvaluationNotebook[], All, 
            EvaluationCell]; First@SelectedCells[]], 
          Method -> "Queued"]]}}, 
  TaggingRules -> {"encoded" -> False}];

So, for example, try

CellPrint@ExpressionCell[28 f[9], "Output", opts]

or

CellPrint@TextCell["The answer is 8", "Text", opts]

You can put this into a stylesheet, and the functions into a package.

share|improve this answer
    
It's probably nicer to use a palette or a docked cell than a style and CellFrameLabels. It should be quite easy to do from this code –  Rojo Jan 11 '13 at 7:14
    
I'm also going to give this idea a shot and will report back on how it goes. Your idea of putting the cell to be encoded into a notebook and then using the built-in Encode function is clever, though I wish Wolfram Research would have a more general Encode function that worked on things other than a notebook. Thanks for your work on this problem. –  Seth Chandler Jan 11 '13 at 13:34
    
No problem @SethJ.Chandler. I probably will end up putting this in a palette for myself too: select a cell or piece of text, click, password, and hidden. –  Rojo Jan 11 '13 at 13:41
    
@SethJ.Chandler I'll probably make the palette later and ping you to share it –  Rojo Jan 11 '13 at 14:48
    
@SethJ.Chandler What do you mean by more general. Right now you can encode .m files or any text file, e.g.:Export["test.txt","secrect","Text"];Encode["test.txt","test.enc",MachineID‌​->$MachineID];FilePrint@"test.enc" –  Rolf Mertig Jan 11 '13 at 15:39

Here's an idea following up on the master-notebook model.

The master notebook has two additional cell styles: AnswerInput and AnswerOutput. You can write the questions as you like, using ItemNumbered, Text, or whatever style except those two new styles. You put your answer code in an AnswerInput cell, and evaluate it. Set the output cell to style AnswerOutput.

Master Notebook

The master notebook has a button at the top, which you can copy for each assignment, that generates the question document. You distribute that notebook to the students, but only ever have to edit the master. Consequently the question notebook is automatically synchronized to the master notebook.

Question Notebook

The question notebook does not contain the Button, and your answers are replaced with a placeholder Input cell.

I'm inclined to do it this way because

  1. I don't have to remember and protect an additional password.
  2. Since there is no password prompt, students will not be distracted trying to guess the password.
  3. The notebook itself is not subject to attack for purposes of cheating, since the answers are not in the notebook.

Here is the code for the button. Evaluate it at the top of a new notebook, and double-click on the button cell frame at the right to hide the code. Note: you need to add "ButtonCode" to the CellTags option on this cell (from the Cell menu) to ensure the button is not included in the question notebook.

(* Click this button to generate a notebook without answers *)
Button["Create question notebook", 
 Module[{copy, cursorposition = NotebookSelection[]},
  (* Copy the current notebook, inheriting the stylesheet *)
  copy = CreateDocument[{}, 
    NotebookFileName -> 
     FileNameJoin[{NotebookDirectory[], "Questions.nb"}], 
    StyleDefinitions -> CurrentValue[EvaluationNotebook[], 
      StyleDefinitions]];
  NotebookWrite[copy, 
   (SelectionMove[EvaluationNotebook[], All, Notebook, 
     AutoScroll -> False]; NotebookRead[EvaluationNotebook[]])];
  (* Return cursor to its initial position *)
  SelectionMove[cursorposition, After, Cell];
  (* Remove all "AnswerOutput"-Style cells *)
  NotebookFind[copy, "AnswerOutput", All, CellStyle];
  NotebookDelete[copy];
  (* Replace all "AnswerInput"-Style cells with a placeholder Input cell, framed with a Gray border *)
  NotebookFind[copy, "AnswerInput", All, CellStyle];
  Scan[
   NotebookWrite[#, Cell[BoxData["(* Your solution *)\n"], 
      "Input", CellFrame -> {{1, 1}, {1, 1}}, 
      CellFrameColor -> Gray]] &, SelectedCells[copy]];
  (* Remove the button code *)
  NotebookFind[copy, "ButtonCode", All, CellTags];
  NotebookDelete[copy];
  (* Return cursor to its initial position *)
  SelectionMove[cursorposition, After, Cell];
 ], ImageSize -> All]

Add these two styles to the notebook's stylesheet:

Cell[StyleData["AnswerInput", StyleDefinitions -> StyleData["Input"]], GeneratedCellStyles->{"Output"->"AnswerOutput"}, MenuSortingValue->10000]

and

Cell[StyleData["AnswerOutput", StyleDefinitions -> StyleData["Output"]], MenuSortingValue->10000]

The question notebook will keep a copy of any Output cells from your master, so you can include Output in the questions themselves. When you evaluate AnswerInput cells, however, the outputs are styled AnswerOutput, and those are removed from the question notebook.

share|improve this answer
    
This looks very useful (although question 3 looks kind of challenging). I'll give it a try and comment here later on what I discover. It does have the virtue of simplicity even if it does lead to two notebooks. Thanks very much for your efforts. I am quite impressed. –  Seth Chandler Jan 11 '13 at 13:30
    
@SethJ.Chandler I've found out how to make the AnswerInput styles automatically evaluate to AnswerOutput. See updated stylesheet definitions. No more manual editing of the outputs. –  JxB Jan 12 '13 at 5:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.