├── README.md ├── LICENSE └── emacs-archive-tracker.el /README.md: -------------------------------------------------------------------------------- 1 | EAT (emacs-archive-tracker) 2 | ===================== 3 | 4 | EAT is a script in emacs-lisp to track some statistics about emacs package archives. 5 | Currently included archives are: 6 | 7 | ("gnu" . "http://elpa.gnu.org/packages/") 8 | ("marmalade" . "http://marmalade-repo.org/packages/") 9 | ("melpa" . "http://melpa.milkbox.net/packages/") 10 | 11 | If you know of any others please create an issue and let me know. 12 | 13 | Global Reports 14 | ==== 15 | 16 | To see all reports we generate, simply visit [our gh-page](http://malabarba.github.io/emacs-archive-tracker/). 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Artur Malabarba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /emacs-archive-tracker.el: -------------------------------------------------------------------------------- 1 | ;;; emacs-archive-tracker.el --- A script to track some statistics about emacs package archives. 2 | 3 | ;; Copyright (C) 2013 Artur Malabarba 4 | 5 | ;; Author: Artur Malabarba 6 | ;; URL: http://github.com/Bruce-Connor/emacs-archive-tracker 7 | ;; Version: 0.7 8 | ;; Keywords: 9 | ;; ShortName: eat 10 | ;; Separator: / 11 | 12 | ;;; Commentary: 13 | ;; 14 | ;; 15 | ;;; TODO watch --interval=7200 command 16 | 17 | ;;; License: 18 | ;; 19 | ;; This file is NOT part of GNU Emacs. 20 | ;; 21 | ;; The MIT License (MIT) 22 | ;; 23 | ;; Copyright (c) 2013 Artur Malabarba 24 | ;; 25 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy of 26 | ;; this software and associated documentation files (the "Software"), to deal in 27 | ;; the Software without restriction, including without limitation the rights to 28 | ;; use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 29 | ;; the Software, and to permit persons to whom the Software is furnished to do so, 30 | ;; subject to the following conditions: 31 | ;; 32 | ;; The above copyright notice and this permission notice shall be included in all 33 | ;; copies or substantial portions of the Software. 34 | ;; 35 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 36 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 37 | ;; FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 38 | ;; COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 39 | ;; IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 40 | ;; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 | 42 | ;;; Change Log: 43 | ;; 0.7 - 20130724 - data file. 44 | ;; 0.5 - 20130723 - Working version. 45 | ;; 0.1 - 20130723 - Created File. 46 | ;;; Code: 47 | 48 | (require 'cl-lib) 49 | (defconst eat/version "0.7" "Version of the emacs-archive-tracker.el package.") 50 | (defconst eat/version-int 3 "Version of the emacs-archive-tracker.el package, as an integer.") 51 | (defun eat/bug-report () 52 | "Opens github issues page in a web browser. Please send me any bugs you find, and please inclue your emacs and eat versions." 53 | (interactive) 54 | (browse-url "https://github.com/Bruce-Connor/emacs-archive-tracker/issues/new") 55 | (message "Your eat/version is: %s, and your emacs version is: %s.\nPlease include this in your report!" 56 | eat/version emacs-version)) 57 | 58 | (defcustom eat/sources '(("gnu" . "http://elpa.gnu.org/packages/") 59 | ("marmalade" . "http://marmalade-repo.org/packages/") 60 | ("melpa" . "http://melpa.milkbox.net/packages/")) 61 | "List of sources to be used." 62 | :type '(repeat (cons string string)) 63 | :group 'emacs-archive-tracker 64 | :package-version '(emacs-archive-tracker . "0.5")) 65 | 66 | (defcustom eat/pull-script "pull-website.sh" 67 | "Script that pulls form website before starting everything else." 68 | :type 'file 69 | :group 'emacs-archive-tracker 70 | :package-version '(emacs-archive-tracker . "0.5")) 71 | 72 | (defcustom eat/script "feed-website.sh" 73 | "Script that processes data and pushes to website." 74 | :type 'file 75 | :group 'emacs-archive-tracker 76 | :package-version '(emacs-archive-tracker . "0.5")) 77 | 78 | (defcustom eat/directory (expand-file-name "~/Git-Projects/emacs-archive-tracker/") 79 | "" 80 | :type 'directory 81 | :group 'emacs-archive-tracker 82 | :package-version '(emacs-archive-tracker . "0.1")) 83 | 84 | (defcustom eat/data-file (concat eat/directory "main-dish.dat") 85 | "File where we save the main graph data." 86 | :type 'file 87 | :group 'emacs-archive-tracker 88 | :package-version '(emacs-archive-tracker . "0.5")) 89 | 90 | (defcustom eat/log-file (concat eat/directory "eat.log") 91 | "File for logging." 92 | :type 'file 93 | :group 'emacs-archive-tracker 94 | :package-version '(emacs-archive-tracker . "0.1")) 95 | 96 | (defcustom eat/updated-file "updated.html" "" 97 | :type 'file 98 | :group 'emacs-archive-tracker 99 | :package-version '(emacs-archive-tracker . "0.7")) 100 | 101 | (defcustom eat/new-file "new.html" "" 102 | :type 'file 103 | :group 'emacs-archive-tracker 104 | :package-version '(emacs-archive-tracker . "0.7")) 105 | 106 | (defcustom eat/table-file (concat eat/directory "table-content.htmlt") 107 | "" 108 | :type 'string 109 | :group 'emacs-archive-tracker 110 | :package-version '(emacs-archive-tracker . "0.7")) 111 | 112 | (defvar eat/-new-day nil "Track if this is the first update of the day.") 113 | (defvar eat/-contents nil "") 114 | (defvar eat/-save-recent-to-global nil "") 115 | 116 | (append-to-file (concat 117 | (format-time-string "%n[%Y-%m-%d %T.%3N] ") 118 | "Script starting.") nil eat/log-file) 119 | 120 | (defun eat/-log (&rest rest) 121 | "Log to `eat/log-file'" 122 | (append-to-file (concat 123 | (format-time-string "%n [%Y-%m-%d %T.%3N] ") 124 | (eval (cons 'format rest))) nil eat/log-file)) 125 | 126 | (defun eat/-list-to-file (l f) 127 | "Save list L to file F in a relatively pretty format." 128 | (eat/-log "Writing %s..." f) 129 | (with-temp-file f 130 | (insert "'(") 131 | (let ((eval-expression-print-length nil) 132 | (eval-expression-print-level nil) 133 | (print-length nil) 134 | (print-level nil)) 135 | (dolist (cur l) 136 | (insert (format "\n %S" cur)))) 137 | (insert "\n )\n")) 138 | (eat/-log "Done.")) 139 | 140 | (defun eat/-list-to-table-- (l f) 141 | "Save list L to file F in html table format." 142 | (eat/-log "Writing %s..." f) 143 | (with-temp-file f 144 | (insert "'(") 145 | ;; 146 | ;; row 1, cell 1 147 | ;; row 1, cell 2 148 | ;; 149 | ;; 150 | ;; row 2, cell 1 151 | ;; row 2, cell 2 152 | ;; 153 | 154 | (let ((eval-expression-print-length nil) 155 | (eval-expression-print-level nil) 156 | (print-length nil) 157 | (print-level nil)) 158 | (dolist (cur l) 159 | (insert (format "\n %S" cur)))) 160 | (insert "\n )\n")) 161 | (eat/-log "Done.")) 162 | 163 | (defun eat/fetch-and-save-statistics (sources name) 164 | "Run `list-packages' with the given SOURCES, save it to a dated file inside directory NAME, and save its package count to variable eat/NAME-count." 165 | (let* ((package-archives sources) 166 | (dirname (concat eat/directory name)) 167 | (file (concat dirname "/" name (format-time-string "-%Y-%m-%d"))) 168 | (elfile (concat file ".el")) 169 | (selfile (concat file "-sorted.el")) 170 | (txtfile (concat file ".txt")) 171 | (weekfile (concat file "week-special.txt")) 172 | (datafile (concat eat/directory name "/" name ".dat")) 173 | (sc (intern (concat "eat/" name "-single-count"))) 174 | (tc (intern (concat "eat/" name "-tar-count"))) 175 | (c (intern (concat "eat/" name "-count"))) 176 | lastweek) 177 | (make-directory dirname :parents) 178 | ;; Get packages 179 | (eat/-log "Fetching list for %s." name) 180 | (package-refresh-contents) 181 | ;; Count them 182 | (set c 0) 183 | (set sc 0) 184 | (set tc 0) 185 | (dolist (cur package-archive-contents) 186 | (set c (1+ (eval c))) 187 | (let ((type (elt (cdr cur) 3))) 188 | (cond ;we use cond because case is not built-in 189 | ((equal type 'tar) (set tc (1+ (eval tc)))) 190 | ((equal type 'single) (set sc (1+ (eval sc)))) 191 | (t (eat/-log "[ERROR] Unexpected symbol: %s" 192 | (quote type)))))) 193 | ;; Print the counts 194 | (append-to-file (format "%s %s %s %s %s\n" 195 | (format-time-string "%s") 196 | (format-time-string "%Y-%m-%dT%R") 197 | (eval c) (eval sc) (eval tc)) 198 | nil datafile) 199 | ;; Print the packages 200 | ;; we only print them once a day 201 | (setq eat/-contents (cl-copy-list package-archive-contents)) 202 | (unless (file-readable-p elfile) 203 | (setq eat/-new-day t) 204 | (eat/-log "No elfile, writing one.") 205 | (eat/-list-to-file package-archive-contents elfile)) 206 | (unless (file-readable-p selfile) 207 | (eat/-log "No selfile, writing one.") 208 | (eat/-list-to-file (sort package-archive-contents 209 | (lambda (l g) (string< (car l) (car g)))) 210 | selfile)) 211 | (unless (file-readable-p txtfile) 212 | (eat/-log "No txtfile, writing one.") 213 | (let ((package-menu--new-package-list nil)) 214 | (with-temp-file txtfile 215 | (set-buffer-file-coding-system 'no-conversion) 216 | (package-menu-mode) 217 | (setq tabulated-list-format 218 | [("Package" 28 package-menu--name-predicate) 219 | ("Version" 18 nil) 220 | ("Status" 10 package-menu--status-predicate) 221 | ("Description" 0 nil)]) 222 | (tabulated-list-init-header) 223 | (package-menu--generate nil t)))) 224 | ;; Now let's check for new packages. 225 | ;; last week 226 | (eat/-compare-last name 4) 227 | ;; last month 228 | ;; (eat/-compare-last name 30) 229 | )) 230 | 231 | (defvar eat/ALL-count nil "") 232 | (defvar eat/ALL-single-count nil "") 233 | (defvar eat/ALL-tar-count nil "") 234 | (defvar eat/-previous-file nil "") 235 | 236 | (defun eat/-compare-last (name days) 237 | "" 238 | (let* ((vn (format "eat/-%s-ago-contents" days)) 239 | (vs (intern vn)) 240 | (dir (concat eat/directory name "/")) 241 | (nfile (format "%s%s/%s%s-new-since-%s.el" eat/directory name name (format-time-string "-%Y-%m-%d") days)) 242 | (ufile (format "%s%s/%s%s-updated-since-%s.el" eat/directory name name (format-time-string "-%Y-%m-%d") days)) 243 | updated new) 244 | (when t;eat/-new-day 245 | (setq eat/-previous-file 246 | (concat dir 247 | (replace-regexp-in-string 248 | "\n" "" 249 | (shell-command-to-string 250 | (format "cd %s%s && ls -1 | grep '[a-zA-Z]\\+-[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\.el' | sed -n '%sp'" 251 | eat/directory name days))))) 252 | (eat/-log "Previous file is %s" eat/-previous-file) 253 | (if (file-readable-p eat/-previous-file) 254 | (with-temp-buffer 255 | (insert-file-contents eat/-previous-file) 256 | (if (null (looking-at "^'($")) 257 | (eat/-log "[ERROR] Not a list in file %s" eat/-previous-file) 258 | (insert "(setq " vn " ") 259 | (forward-sexp 1) 260 | (insert ")") 261 | (forward-char 1) 262 | (if (null (and (looking-at "^$") (eobp))) 263 | (eat/-log "[ERROR] Extra garbage after list in file %s" eat/-previous-file) 264 | (eval-buffer) 265 | (setq updated (cl-copy-list eat/-contents)) 266 | (cl-delete-if (lambda (cur) (member cur (eval vs))) updated) 267 | (setq new (cl-remove-if (lambda (cur) 268 | (cl-member cur (eval vs) :test 269 | (lambda (o e) (string= (car o) (car e))))) 270 | updated)) 271 | (cl-delete-if (lambda (cur) (member cur new)) updated) 272 | (eat/-list-to-file new nfile) 273 | (eat/-list-to-file updated ufile)))) 274 | (eat/-log "No previous file readable. It was: %s" eat/-previous-file)) 275 | (when eat/-save-recent-to-global 276 | (eat/-list-to-table new (concat eat/directory eat/new-file)) 277 | (eat/-list-to-table updated (concat eat/directory eat/updated-file)))))) 278 | 279 | (defun eat/-list-to-table (l f) 280 | nil) 281 | 282 | ;; (defun eat/-compare-last (name days) "") 283 | (eat/-log "Running %s%s" eat/directory eat/pull-script) 284 | 285 | (defun eat/source-name-to-total-count (src) 286 | (format "%s " (eval (intern (concat "eat/" (car src) "-count"))))) 287 | 288 | (if (< 0 (shell-command 289 | (format "cd %s && ./%s" eat/directory eat/pull-script))) 290 | (with-current-buffer "*Shell Command Output*" 291 | (eat/-log "--- FAILED COMMAND!! --- Here's the output:\n%s" (buffer-string))) 292 | (eat/-log "Success.") 293 | 294 | (dolist (cur eat/sources) 295 | (eat/fetch-and-save-statistics (list cur) (car cur))) 296 | 297 | (let ((eat/-save-recent-to-global t)) 298 | (eat/fetch-and-save-statistics eat/sources "ALL")) 299 | 300 | (eat/-log "Saving global data in %s" eat/data-file) 301 | (append-to-file 302 | (eval (format "%s %s %s %s %s %s\n" 303 | (format-time-string "%s") 304 | (format-time-string "%Y-%m-%dT%R") 305 | eat/ALL-count 306 | eat/ALL-single-count 307 | eat/ALL-tar-count 308 | (mapconcat 'eat/source-name-to-total-count eat/sources ""))) 309 | nil eat/data-file) 310 | 311 | (with-temp-file eat/table-file 312 | (set-buffer-file-coding-system 'no-conversion) 313 | (insert "" 314 | (format "%s" eat/ALL-count) " " 315 | (format "%s" eat/ALL-single-count) " " 316 | (format "%s" eat/ALL-tar-count) " " 317 | (mapconcat 'eat/source-name-to-total-count eat/sources " ") "") 318 | (insert " \n \n \n")) 319 | 320 | ;; You stopped here. (581891) 321 | ;; (with-temp-file eat/new-package-list-file 322 | ;; (set-buffer-file-coding-system 'no-conversion) 323 | ;; (insert "" 324 | ;; (format "%s" eat/ALL-count) " " 325 | ;; (format "%s" eat/ALL-single-count) " " 326 | ;; (format "%s" eat/ALL-tar-count) " " 327 | ;; (mapconcat 'eat/source-name-to-total-count eat/sources " ") "") 328 | ;; (insert " \n \n \n")) 329 | 330 | (eat/-log "Running %s%s" eat/directory eat/script) 331 | (if (= 0 (shell-command 332 | (format "cd %s && ./%s" eat/directory eat/script))) 333 | (eat/-log "Success.") 334 | (set-buffer "*Shell Command Output*") 335 | (eat/-log "--- FAILED COMMAND!! --- Here's the output:\n%s" (buffer-string))) 336 | 337 | (setq eat/-new-day nil)) 338 | 339 | (provide 'emacs-archive-tracker) 340 | ;;; emacs-archive-tracker.el ends here. 341 | --------------------------------------------------------------------------------