Where is the description of what the format for multimedia files is? A lot of my early assignments include images and I'd like to be able to look at student text inside the files for analysis and grading.
I can't speak for the file format, but if you want to see the text, it's probably easiest to do so by opening the file in DrRacket and looking at it.
Yeah. I want to programmatically look at the file.
The format is called WXME.
https://docs.racket-lang.org/gui/WXME_Decoding.html
It's extensible in the sense that snips (see snip%) can define how they are read and written.
So, you might need to look at the documentation/code of the snips that are in your file too.
The simplest way I know of to get nearly the plain-text content of a WXME file is to (1) create a text%
, (2) load the file into it, and (3) send the-text% get-text
to get its content. I say "nearly" because this will render any embedded snips as .
, which is not quite accurate and won't parse back in as valid student code, but is also likely good enough for text-processing.
(require racket/gui/base)
(define (file->text filename)
(define txt (new text%))
(send txt load-file filename)
(send txt get-text))
If you need to do more detailed processing, you'll need to actually walk the snip% tree, which is somewhat more tedious. Instead of sending get-text
, you'll send find-first-snip
(and send that next
to get its siblings), and then use is-a?
on all the various snip classes you want to handle. I've used the following (believe it or not, edited somewhat for brevity! helper functions not included)
;; display-all : (or/c snip? #f) output-port? -> #f
;; Walks the given snip tree and renders it as plaintext to the given output port.
(define (display-all snip out #:inline-numbers? [inline-numbers? #f])
(if (not snip)
out
(let ((snip-name (send (send snip get-snipclass) get-classname)))
(cond
[(is-a? snip string-snip%)
(display (send snip get-text 0 (send snip get-count))
out)]
[(is-a? snip comment-box:snip%)
(define comment (open-output-bytes))
(define contents (display-all (send (send snip get-editor)
find-first-snip)
comment))
(display (add-comment (get-output-string comment)) out)]
[(equal? snip-name
"(lib \"number-snip.ss\" \"drscheme\" \"private\")")
(define val (number->string (send snip get-number)))
(display (if inline-numbers? val (add-number val)) out)]
[(or (is-a? snip xml-snip<%>)
(equal? snip-name "(lib \"xml-snipclass.ss\" \"xml\")")
(equal? snip-name "(lib \"xml-snipclass.rkt\" \"xml\")")
(equal? snip-name "(lib \"xml.ss\" \"wxme\")"))
(define xml (open-output-bytes))
(define contents (display-all (send (send snip get-editor)
find-first-snip)
xml))
(display (add-xml (get-output-string xml)) out)]
[(or (equal? snip-name "(lib \"text-snipclass.ss\" \"xml\")")
(equal? snip-name "(lib \"text-snipclass.rkt\" \"xml\")")
(equal? snip-name "(lib \"text.ss\" \"wxme\")"))
(define text (open-output-bytes))
(define contents (display-all (send (send snip get-editor)
find-first-snip)
text))
(display (add-text (get-output-string text)) out)]
[(equal? snip-name (format "~s" '((lib "collapsed-snipclass.ss" "framework")
(lib "collapsed-snipclass-wxme.ss" "framework"))))
;; Display collapsed-snips in collapsed form isn't good for grading:
;; it hides code, and could hide badly long lines. So,
;; expand the snip to its hidden pieces, instead.
(for ([kid (in-list (send snip get-saved-snips))])
(display-all kid out))]
[(or (is-a? snip scheme-snip<%>)
(equal? snip-name "(lib \"scheme-snipclass.ss\" \"xml\")")
(equal? snip-name "(lib \"scheme-snipclass.rkt\" \"xml\")")
(equal? snip-name "(lib \"scheme.ss\" \"wxme\")"))
(define rkt (open-output-bytes))
(define contents (display-all (send (send snip get-editor)
find-first-snip)
rkt))
(if (send snip get-splice?)
(display (add-splice (get-output-string rkt)) out)
(display (add-racket (get-output-string rkt)) out))]
[(convertible? snip)
(define bm (new bitmap-dc% [bitmap (make-object bitmap% 1 1)]))
(define width (box 0))
(define height (box 0))
(send snip get-extent bm 0 0 width height)
(if (or (= (unbox width) 0) (= (unbox height) 0))
(display (add-empty-image "") out)
(with-handlers
([exn:fail:contract?
(λ(e)
(display
(add-comment
(string-append "Could not render this content;\n"
"please contact a professor."))
out))])
(display (add-image (convert snip 'png-bytes)) out)))]
[(syntax? snip)
(display (syntax->datum snip) out)]
[else
(display (format ">?>~a<?<" snip-name) out)
(display (format ">>>~a<<<" snip) out)]
)
(display-all (send snip next) out #:inline-numbers? inline-numbers?))))
(I'd love to know if there are ways to detect number, xml, collapsed and scheme snips without resorting to snip classnames! But this code has worked well for me for years.)