Skip to content

Instantly share code, notes, and snippets.

@siddharthverma314
Created September 2, 2022 22:56
Show Gist options
  • Save siddharthverma314/ed0f0066c391e856e9a8cfd7aef46d24 to your computer and use it in GitHub Desktop.
Save siddharthverma314/ed0f0066c391e856e9a8cfd7aef46d24 to your computer and use it in GitHub Desktop.
Emacs code to list results from GradCafe. I used this to keep track of PhD admissions for CS in Fall 2022
;; -*- lexical-binding: t -*-
(use-package request)
(require 'request)
(require 'dom)
(require 'cl-lib)
(require 's)
(require 'ts)
(require 'dash)
(cl-defstruct gradcafe-result
type university date american-p gpa gre description)
(defvar gradcafe-universities '("Massachusetts Institute of Technology (MIT)"
"Stanford University"
"University of California, Berkeley"
"Carnegie Mellon University"
"Harvard University"
"University of Washington"
"Cornell (Ithaca)"
"Cornell Tech"
"Cornell University"
"Princeton University")
"Universities to fetch data from GradCafe")
(defun gradcafe--fetch-results (university program degree season per-page)
(with-temp-buffer
(insert
(request-response-data
(request "https://www.thegradcafe.com/survey/"
:type "GET"
:params `((per_page . ,per-page)
(institution . ,university)
(program . ,program)
(degree . ,degree)
(season . ,season))
:sync t)))
(libxml-parse-html-region (point-min) (point-max))))
(defun gradcafe--parse-result (xml university)
(let* ((additional-info (cl-loop
for node in (-> xml
(dom-child-by-tag 'div)
(dom-non-text-children))
when (equal 'span (dom-tag node))
collect (car (dom-children node))))
(match (cdr (s-match
(rx-let ((month (| "Jan" "Feb" "Mar" "Apr" "May" "Jun"
"Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))
(date (: (** 1 2 digit) " " month))
(my-blank (* (any whitespace ?\n))))
(rx my-blank
(group (| "Interview" "Accepted" "Rejected" "Wait listed"))
(? my-blank "on" my-blank (group date))
my-blank))
(car additional-info))))
(type (pcase (cl-first match)
("Interview" 'interview)
("Accepted" 'accept)
("Rejected" 'reject)
("Wait listed" 'waitlist)))
(date (ts-parse
(s-chop-prefix
"Added on "
(-> xml
(dom-child-by-tag 'p)
(dom-children)
(car)))))
(description (-> xml
(dom-child-by-tag 'h6)
(dom-child-by-tag 'span)
(dom-children)
(car)))
american
gre
gpa)
;; parse additional info
(cl-loop
for info in additional-info
when (s-equals? info "American")
do (setq american t)
when (s-starts-with? "GRE" info)
do (setq gre (string-to-number (s-chop-prefix "GRE " info)))
when (s-starts-with? "GPA" info)
do (setq gpa (string-to-number (s-chop-prefix "GPA " info))))
;; update date
(when-let* ((status-date-raw (cl-second match))
(status-date (ts-parse status-date-raw)))
(setq status-date (ts-apply :year (ts-year date) status-date))
;; (when (ts> status-date date)
;; (ts-decf (ts-year status-date)))
(setq date status-date))
(make-gradcafe-result :type type
:university university
:date date
:american-p american
:gpa gpa
:gre gre
:description description)))
(defun gradcafe--parse-results (xml university)
(cl-loop
for node in (dom-non-text-children (dom-by-id xml "results-container"))
when (and (equal 'div (dom-tag node))
(not (or (dom-by-id node "results-ad-placement-0")
(dom-by-id node "results-ad-placement-1"))))
collect (gradcafe--parse-result (dom-child-by-tag node 'div) university)))
(cl-defun gradcafe-fetch-results (university &key (program "Computer Science") (degree "PhD") (season "F22") (per-page 50))
"Return list of `gradcafe-result' fetched from the website"
(gradcafe--parse-results (gradcafe--fetch-results university program degree season per-page)
university))
(define-derived-mode gradcafe-results-mode tabulated-list-mode "gradcafe-results-mode"
"View gradcafe results in sorted order"
(define-key gradcafe-results-mode-map (kbd "RET") #'gradcafe-view-result))
(defun gradcafe-view-result (&optional result)
"Print result into a buffer and display"
(interactive)
(let ((result (or result (tabulated-list-get-id))))
(with-current-buffer (get-buffer-create "*gradcafe-result*")
(erase-buffer)
(with-slots (university type date american-p gre gpa description) result
(insert
(propertize (format "%s: %s\n" university type)
'face '(:weight bold :height 180))
(propertize (ts-format "%F\n" date) 'face '(:color blue))
(format "GPA: %s\tGRE: %s\tAmerican: %s\n" gpa gre (if american-p "yes" "no"))
"\n"
(or description "")))
(fill-paragraph)
(general-define-key :keymaps 'local "q" #'quit-window)
(switch-to-buffer-other-window (current-buffer)))))
(defun list-gradcafe-results ()
"View results in a tabular setting"
(interactive)
(let ((results (nreverse
(sort
(cl-loop
for university in gradcafe-universities
append (gradcafe-fetch-results university))
(lambda (x y) (ts<= (gradcafe-result-date x) (gradcafe-result-date y)))))))
(with-current-buffer (get-buffer-create "*gradcafe*")
(gradcafe-results-mode)
(setq-local tabulated-list-format [("University" 40 t) ("Type" 15 t) ("Date" 10 t)])
(setq-local tabulated-list-entries
(cl-loop
for result in results
collect (list result
(vector (gradcafe-result-university result)
(symbol-name (gradcafe-result-type result))
(ts-format "%Y-%m-%d" (gradcafe-result-date result))))))
(tabulated-list-init-header)
(tabulated-list-print)
(switch-to-buffer-other-window (current-buffer)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment