CAT-SOOP is a flexible, programmable learning management system based on the Python programming language. https://catsoop.mit.edu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

455 lines
14 KiB

  1. # This file is part of CAT-SOOP
  2. # Copyright (c) 2011-2020 by The CAT-SOOP Developers <catsoop-dev@mit.edu>
  3. #
  4. # This program is free software: you can redistribute it and/or modify it under
  5. # the terms of the GNU Affero General Public License as published by the Free
  6. # Software Foundation, either version 3 of the License, or (at your option) any
  7. # later version.
  8. #
  9. # This program is distributed in the hope that it will be useful, but WITHOUT
  10. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  11. # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  12. # details.
  13. #
  14. # You should have received a copy of the GNU Affero General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. """
  17. Initial context for page loads, including initializing 'special' variables
  18. Many of the variables in this file are special variables that affect the way
  19. the page is rendered (these special variables can be overwritten by early loads
  20. or late loads at lower levels).
  21. """
  22. import os
  23. import sys
  24. import logging
  25. import traceback
  26. from catsoop import __version__, __codename__
  27. LOGGER = logging.getLogger("cs")
  28. _nodoc = {
  29. "LOGGER",
  30. "config_loc",
  31. "default_config_location",
  32. "contents",
  33. "cs_all_pieces",
  34. "cs_all_thirdparty",
  35. "f",
  36. "i",
  37. "root",
  38. "cs_dummy_username",
  39. "datetime",
  40. }
  41. cs_version = __version__
  42. """
  43. CAT-SOOP's version number
  44. """
  45. cs_version_codename = (
  46. '"%s"' if "dev" not in __version__ else '"%s" development snapshot'
  47. ) % __codename__
  48. """
  49. The codename for this version
  50. """
  51. cs_fs_root = os.path.dirname(__file__)
  52. """
  53. The directory where CAT-SOOP's source is located (no trailing slash).
  54. """
  55. cs_data_root = r"/home/catsoop/data"
  56. """
  57. The directory where CAT-SOOP's data files are located (no trailing slash).
  58. """
  59. cs_url_root = "http://localhost:6010"
  60. """
  61. The URL root (without trailing slash). Going to this URL should lead the user
  62. to CAT-SOOP's information page.
  63. """
  64. cs_auth_type = "login"
  65. """
  66. Special: Which authentication type to use (`'login'` to use a form, `'cert'` to
  67. read client certificates, `'openid_connect'` to use OpenID Connect, or some
  68. other value for a course-specific or other custom authentication type).
  69. """
  70. # Default Page Content
  71. cs_title = "CAT-SOOP"
  72. """
  73. Special: The page title, to be displayed in the browser's title bar
  74. """
  75. cs_base_logo_text = (
  76. "\\ "
  77. "\n/ /\\__/\\ "
  78. "\n\\__=( o_O )="
  79. "\n(__________) "
  80. "\n |_ |_ |_ |_ "
  81. )
  82. """
  83. Special: Text representing the CAT-SOOP Logo
  84. """
  85. cs_main_page_text = ""
  86. """
  87. Special: Text to be added to the main (information) page when no course is
  88. chosen.
  89. """
  90. cs_base_color = "#0000CC"
  91. """
  92. Special: The base color to use to customize the main theme, hexadecimal format.
  93. """
  94. cs_light_color = None
  95. """
  96. Special: Light version of the color in the main theme, hexadeciaml format. If
  97. value is `None`, an appropriate color will be computed based on
  98. `cs_base_color`.
  99. """
  100. cs_process_theme = True
  101. """
  102. Special: Whether the theme should be "processed" by, e.g., evaluating Python
  103. code
  104. """
  105. cs_welcome_message = ""
  106. """
  107. Special: Welcome message displayed next to the title in base theme.
  108. """
  109. cs_header = "CAT-SOOP"
  110. """
  111. Special: The main header, displayed at the top-left of the page in the base
  112. theme.
  113. """
  114. cs_subheader = ""
  115. """
  116. Special: Sub-header, displayed below the main header
  117. """
  118. cs_footer = ""
  119. """
  120. Special: Footer, displayed in addition to the "powered by CAT-SOOP" link
  121. """
  122. cs_top_menu = ""
  123. """
  124. Special: Navigation menu.
  125. The navigation menu is specified as a string (containing raw HTML), or as a list of dictionaries.
  126. Each dictionary should contain two keys:
  127. * `'text'` maps to the text to be displayed, and
  128. * `'link'` maps to a URL, or to another list of dictionaries representing a
  129. submenu.
  130. In the case where a dictionary is specified, the appropriate HTML for use with
  131. the base theme will be generated.
  132. """
  133. cs_scripts = ""
  134. """
  135. Special: HTML to import additional scripts; included in the page's <head> tags
  136. """
  137. cs_side_menu = ""
  138. """
  139. Special: Additional menu space
  140. """
  141. cs_bottom_menu = ""
  142. """
  143. Special: Additional menu space
  144. """
  145. cs_content_header = (
  146. '<span class="cs_base_bold">C</span>AT-SOOP is an '
  147. '<span class="cs_base_bold">A</span>utomatic '
  148. '<span class="cs_base_bold">T</span>utor<br/> for '
  149. '<span class="cs_base_bold">S</span>ix-'
  150. '<span class="cs_base_bold">O</span>h-'
  151. '<span class="cs_base_bold">O</span>ne '
  152. '<span class="cs_base_bold">P</span>roblems'
  153. )
  154. """
  155. Special: The text to be displayed at the top of the "content" block.
  156. """
  157. cs_content = ""
  158. """
  159. Special: The content of the page
  160. """
  161. cs_footnotes = ""
  162. """
  163. Special: A string containing footnotes, if any. Automatically populated by the
  164. default handler.
  165. """
  166. cs_template = "BASE/templates/main.template"
  167. """
  168. Special: The template file to use to render the page
  169. """
  170. # Default Look and Feel
  171. cs_theme = "BASE/themes/base.css"
  172. """
  173. Special: A URL pointing to the page's CSS stylesheet
  174. """
  175. cs_icon_url = "data:image/gif;base64,R0lGODlhEAAQAKECAAAAAAAzZv///////yH5BAEKAAMALAAAAAAQABAAAAIpnI+pi+DjAHztSAkVnri/fRmb9WUmg6ZoxLUsmIVyKbdijIPfHqq+WgAAOw=="
  176. """
  177. Special: A URL pointing to the page's favicon
  178. """
  179. cs_loading_image = "data:image/gif;base64,R0lGODlhEAAQAPIGAMLCwkJCQgAAAGJiYoKCgpKSkv///wAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAGACwAAAAAEAAQAAADMmi63P4wyklrAyEAGoQInAdOmGYBw7AxwLoMGcG2rkHEQFHQLTsQOd2mB9ERCpTWzpEAACH5BAkKAAYALAAAAgAKAA4AAAMraAYRoNAEIUJUs97VHgTD4EVDQ2xEM2wgMV5AUbyKLKNEvoxA3P8sYNCQAAAh+QQJCgAGACwAAAAACgAOAAADLWi6EAFrBSGCAmQ0as1wROFABuEM0TUQ5FUU7fK+aRkWNYDFqV4bOl8v+BMuEgAh+QQJCgAGACwAAAAADgAKAAADKmi6QAMrrhECkaaVVl+FRiFuAwEEghAoYxGhqgI0oPxlNSbPOcb3PqAkAQAh+QQJCgAGACwCAAAADgAKAAADKWhqUAUrLuekApA+MiDD4BYExAVGwzgsmNR0lgWMXmwEghDYCq7zDFoCACH5BAkKAAYALAYAAAAKAA4AAAMqaADWros9GEuRUBE7jeUTYGEhMZANEQREN6xDJ54PsKJGIAhBp/OyWyMBACH5BAkKAAYALAYAAgAKAA4AAAMpaKoA+609Fie1C5Tipt7WRhRWw0ED0T1DEAyMq7mEEghCAKTdnZcySwIAIfkEBQoABgAsAgAGAA4ACgAAAytoumwALb4X2YR1URACVkBRYIEgBIw4KuUJDERIzGD3doMhfguBZyAYT5EAADs="
  180. """
  181. A URI pointing to an image to be used as a loading icon.
  182. """
  183. cs_check_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAABQVBMVEUAAAAGDQAAAAAAAAAAAAAAAAAAAACGqlBIfBNciiZGeg9Eeg5Feg5Gew9CdgpCdgxDehJvmDN1nj9plTVWhRtOgRlMfxZjkS5Geg9EeA5Geg5HfBBHew5GeQ5Cdg1CeAxDegtHgACVtmV+pURPgBp4oEBEeQx+o0p8oklFeg5YiCJIfRFEdQ5GehBHeg9CdwtEdQxEeg5Fdw5DeQ1Eegs2YQlCdwpCeAxDeQ0PHgBGdA9AdgkAAAAAAAARIgChwGKPs0qjwXKStUyFqD6CqDfJ3p+91Ji00XqlwmypyGihwGiauluWtlicwFKXulGTuEmMr0WOs0OKr0CGrDuEqjrR46zC2Ji+1ZC804640oK1z4CxzHywynqsx3enw3WYuWidvF+fwlqgxFePsFWSs1KIrE2Wu0uOr0qGqkeCpj50nDHRgoCJAAAAP3RSTlMABA0HEgoQ/ff17+jbxi8nDvz39vb29fTy7dPQoJeLVRUJ/f39+vn49/f118jAvrKWcW9jWllJQjkiIRwZFg8RajNxAAAA+klEQVQY06XQ1bLCMBAGYNJCHXfnuLtrFXc5gru+/wMQhhZouWRvMvNt/kx2dbvUC+V/3tY9OhZzX5i0HDqNxn/aRo2aXb/JXtR+p2HjfjUb5xmg1kfyL9v9d76q1UK1kqPaSWAFy1TQVs+zvCci4wd9fA8PE9HM5Rvkg5JlcJ68iuh8OFtkcb/ywrUtlUscUTfniaKQIlaj0AdsoZTuH2aEUsceXM/AuNNcmcsIZW7otWwsIuCacBVJqgycYdWPwx7rWJQKVu0y3n0OUZyemVUIAPp9S8wcIQRBDCgAsqIGBNM/eS/fPr/02KKx5IXDBixsfV2OwCYERebVjyGAvqhE3QAAAABJRU5ErkJggg=="
  184. """
  185. A URI pointing to an image to be used for the "check" (correct) image.
  186. """
  187. cs_cross_image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAA0lBMVEUAAAC/AAC/QEAAAADALS2qCwsAAAAAAAC7Jia3Hx+zGRm4ICCxFBTKQkLJPj68JycEAADMRUXMSEixFRXENjahBwe+KSmuEBDEMTEIAAALAAANAAAAAADGOTm+LCzCMjK1GxuzGRmuDw8AAAAsAADMR0fMSkrEMjLNQEDjbm7gYGDYUVHPRkbKPT3iZGTng4PmfHzld3fkcnLiZmbeW1vVVVXUTU3SSEjIODjpi4voiIjiamrfamrcZGTYW1vcV1faVVXST0/WTk7RTU3QRETlfn4RycE0AAAAKHRSTlMABAQEtrcvD/X19ba29fW2Kvb19fS2rKysJhsWCvX187a2tiER9vWsKh9QBQAAAP9JREFUGNOVzNdygkAYhuEsSChSVSxg1yQsvSliN+r935KL++N46jc7y8vDDF+fj+n3Gai/qqAbP60GQ+u3KlBjW5K3qlplaYB31QinW0Njqu9pGild4HiX4UjRNCXCGb6rlBlNTbJwFytKjMlD1eDnqDlKQzdMkoxcoyYiBN7Bbu6Sgzugte/z0ynfg9Y8nRzOnnc+TKboTWfjo/fvkXMcz9BLZWG1Ltar5yXI6KVX/1ZcBOFS3Pxr7T3RdxzHF2UZokdZCoLAEecIzUWHpEQZcdJmI3HorcDbbW7Jky2rAmWHtmkO9G8yXTdNix+ylHnbWjxdHywsm2cJw1i6Gh6M9B+39Tnr2wAAAABJRU5ErkJggg=="
  188. """
  189. A URI pointing to an image to be used for the "cross" (incorrect) image.
  190. """
  191. cs_course = None
  192. """
  193. The course associated with the given request (should not be manually set)
  194. """
  195. # Questions / Checker
  196. cs_question_type_defaults = {}
  197. """
  198. Special: A dictionary mapping question type names to dictionaries containing
  199. default values to use for that question type. These values are loaded in
  200. before the values in a &lt;question&gt; tag are evaluated.
  201. """
  202. cs_checker_websocket = "ws://localhost:6011"
  203. """
  204. Special: The location to which the browser should connect to the checker's
  205. "reporter" process.
  206. """
  207. cs_checker_server_port = 6011
  208. """
  209. Special: The local port on which the websocket server should run
  210. """
  211. cs_checker_global_timeout = 120
  212. """
  213. Special: The absolute maximum length (in seconds) that a checker should be
  214. allowed to run before being killed. This trumps any limits set by a particular
  215. question so that if there is, for example, an infinite loop in a checker's
  216. code, it will still be killed eventually.
  217. """
  218. cs_checker_parallel_checks = 1
  219. """
  220. Special: The number of checks the checker should run simultaneously.
  221. """
  222. # UWSGI Server
  223. cs_wsgi_server = "cheroot"
  224. """
  225. The WSGI server to use. Currently, must be 'cheroot' or 'uwsgi'
  226. """
  227. cs_wsgi_server_port = 6010
  228. """
  229. Special: The local port on which the WSGI server should run.
  230. """
  231. cs_wsgi_server_min_processes = 1
  232. """
  233. Special: The minimum number of worker processes the UWSGI server should have
  234. running
  235. """
  236. cs_wsgi_server_max_processes = 1
  237. """
  238. Special: The maximum number of worker processes the UWSGI server should have
  239. running
  240. """
  241. # user interface configuration flags
  242. cs_ui_config_flags = {
  243. "highlight_explanation_button": False,
  244. "auto_show_explanation_with_answer": False,
  245. }
  246. """
  247. Special: user interface configuration flags
  248. """
  249. # user information data configuration flags
  250. cs_user_config = {
  251. "section_variable": "section", # variable for class section
  252. "default_section_name": "default",
  253. }
  254. """
  255. Special: user information data configuration flags (e.g. used for groups)
  256. """
  257. # Log Encryption
  258. cs_log_compression = False
  259. """
  260. Special: Boolean indicating whether log entries should be compressed.
  261. """
  262. cs_log_encryption = False
  263. """
  264. Special: Boolean indicating whether log entries should be encrypted.
  265. """
  266. # File Upload Type
  267. cs_upload_management = "file"
  268. """
  269. Special: defines how CAT-SOOP should handle file uploads. Must be `'file'` or
  270. `'db'`.
  271. In `'file'` mode, CAT-SOOP will store the uploaded files on disk, under
  272. `<cs_data_root>/_logs/_uploads`.
  273. In `'db'` mode, CAT-SOOP will store the contents of the files directly in the
  274. CAT-SOOP logs.
  275. """
  276. cs_python_intepreter = "python3"
  277. """
  278. Path to python interpreter used for sandboxed python execution of checking code
  279. """
  280. cs_debug_level = os.environ.get("CATSOOP_DEBUG_LEVEL", "WARNING")
  281. try:
  282. cs_debug_level = int(cs_debug_level)
  283. except:
  284. pass
  285. cs_lti_debug_level = "WARNING"
  286. # Debugging Function
  287. import os
  288. from datetime import datetime
  289. cs_debug_log_location = "/tmp/catsoop.log"
  290. """
  291. The filename where the user debug log should be stored (via cs_debug)
  292. """
  293. def cs_debug(*values, tag=""):
  294. """
  295. Write values to cs_debug_log_location, with a timestamp and an optional tag.
  296. If cs_debug_log_location is None, do nothing.
  297. """
  298. if cs_debug_log_location is None:
  299. return
  300. with open(cs_debug_log_location, "a") as myfile:
  301. print(datetime.now().time(), tag, *values, file=myfile)
  302. _cs_config_errors = []
  303. # try to import configuration from config.py
  304. try:
  305. default_config_location = os.environ.get(
  306. "XDG_CONFIG_HOME", os.path.expanduser(os.path.join("~", ".config"))
  307. )
  308. config_loc = os.path.abspath(
  309. os.path.join(default_config_location, "catsoop", "config.py")
  310. )
  311. config_loc = os.environ.get("CATSOOP_CONFIG", config_loc)
  312. LOGGER.info("[base_context] using config file %s" % config_loc)
  313. with open(config_loc) as f:
  314. exec(f.read())
  315. except Exception as e:
  316. msg = "error in config.py: %s" % (e,)
  317. LOGGER.error("[base_context] %s" % msg)
  318. LOGGER.error("[base_context] traceback=%s" % traceback.format_exc())
  319. _cs_config_errors.append(msg)
  320. # Import all CAT-SOOP modules/subpackages
  321. cs_all_pieces = [
  322. "api",
  323. "auth",
  324. "base_context",
  325. "check",
  326. "cslog",
  327. "dispatch",
  328. "errors",
  329. "fernet",
  330. "groups",
  331. "language",
  332. "loader",
  333. "mail",
  334. "process",
  335. "session",
  336. "time",
  337. "thirdparty",
  338. "tutor",
  339. "user",
  340. "util",
  341. ]
  342. cs_all_thirdparty = ["data_uri"]
  343. for i in cs_all_pieces:
  344. if i != "base_context":
  345. exec("from . import %s" % i)
  346. exec("csm_%s = %s" % (i, i))
  347. for i in cs_all_thirdparty:
  348. exec("from .thirdparty import %s" % i)
  349. exec("csm_thirdparty.%s = %s" % (i, i))
  350. # Checks for valid Configuration
  351. # check for valid fs_root
  352. _fs_root_error = "cs_fs_root must be a directory containing the catsoop source code"
  353. if not os.path.isdir(cs_fs_root):
  354. LOGGER.error("[base_context] %s" % _fs_root_error)
  355. LOGGER.error("[base_context] cs_fs_root=%s" % cs_fs_root)
  356. _cs_config_errors.append(_fs_root_error)
  357. else:
  358. root = cs_fs_root
  359. if not os.path.isdir(root):
  360. LOGGER.error("[base_context] %s" % _fs_root_error)
  361. _cs_config_errors.append(_fs_root_error)
  362. else:
  363. contents = os.listdir(root)
  364. if not all(("%s.py" % i in contents or i in contents) for i in cs_all_pieces):
  365. _cs_config_errors.append(_fs_root_error)
  366. # check for valid data_root
  367. if not os.path.isdir(cs_data_root):
  368. _cs_config_errors.append("cs_data_root must be an existing directory")
  369. else:
  370. if not os.access(cs_data_root, os.W_OK):
  371. _cs_config_errors.append("the web server must be able to write to cs_data_root")