├── .gitignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── Makefile ├── calendar.rst ├── conf.py ├── index.rst ├── internals.rst ├── ofcourse-workflow.dia ├── ofcourse-workflow.svg └── start.rst ├── ofcourse-templates ├── about.html ├── blogs.html ├── calendar.html ├── home.html ├── hw │ ├── firstflight.html │ └── index.html ├── instructor.html ├── lectures │ ├── index.html │ └── w01c2.html ├── master.html ├── ohno.html ├── participant.html ├── profile.html ├── quiz │ └── quiz1.html ├── resources.html ├── syllabus.html └── weeklybreakdown.txt ├── ofcourse ├── .travis.yml ├── __init__.py ├── blueprints.py ├── cal.py ├── cli │ ├── __init__.py │ ├── openshift_utils.py │ └── util.py ├── course.ics ├── participants.py ├── render.py ├── site.py ├── static │ ├── books │ │ └── Open-Advice.pdf │ ├── css │ │ ├── bootstrap.min.css │ │ ├── material-wfont.min.css │ │ ├── material.min.css │ │ ├── pace.css │ │ ├── ripples.min.css │ │ └── site.css │ ├── decks │ │ └── hanginthere.jpg │ ├── fonts │ │ ├── LiberationSans-Regular-webfont.svg │ │ ├── LiberationSans-Regular-webfont.woff │ │ ├── Red Hat Liberation License.txt │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── hw │ │ ├── addyourselftocoursewebsite.html │ │ ├── createFASaccount.html │ │ ├── creategithubaccount.html │ │ ├── getyourfirstpullrequestaccepted.html │ │ └── issueapullrequest.html │ ├── img │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── apple-icon-precomposed.png │ │ ├── apple-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── favicon.png │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── grid-baseline-20px.png │ │ ├── less-logo-large.png │ │ ├── manifest.json │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ ├── ms-icon-70x70.png │ │ └── responsive-illustrations.png │ └── js │ │ ├── bootstrap.min.js │ │ ├── html5shiv.js │ │ ├── jquery.js │ │ ├── material.min.js │ │ ├── pace.min.js │ │ └── ripples.min.js ├── tests │ ├── __init__.py │ ├── __main__.py │ ├── api.py │ ├── gabbits │ │ └── status.yaml │ ├── test_new.py │ └── test_yaml.py ├── util.py ├── version.py └── yamls │ ├── assignments.yaml │ ├── fake_student.yaml │ ├── oer.yaml │ └── site.yaml ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.gz 3 | *.swp 4 | peerevals 5 | ofcourse/grades.yaml 6 | 7 | ### Python ### 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | lib/ 24 | lib64/ 25 | parts/ 26 | sdist/ 27 | var/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .coverage 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | 57 | # Sphinx documentation 58 | docs/_build/ 59 | 60 | # PyBuilder 61 | target/ 62 | 63 | # PBR 64 | .eggs/ 65 | AUTHORS 66 | ChangeLog 67 | 68 | # Virtualenvwrapper/VirtualFish 69 | .venv 70 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | install: pip install tox 3 | script: tox 4 | sudo: false 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 6 | 7 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 8 | 9 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 10 | 11 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 12 | 13 | This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | include LICENSE 3 | include requirements.txt 4 | include test-requirements.txt 5 | include tox.ini 6 | include ofcourse/yamls/site.yaml 7 | include ofcourse/yamls/schedule.yaml 8 | include ofcourse/yamls/fake_student.yaml 9 | recursive-include ofcourse-templates * 10 | recursive-include docs * 11 | recursive-include ofcourse/static * 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ofCourse - Open, Free Course(ware) 2 | 3 | [![test status](https://api.travis-ci.org/ryansb/ofCourse.svg)](https://travis-ci.org/ryansb/ofCourse) 4 | [![version](https://img.shields.io/pypi/v/ofcourse.svg)](https://pypi.python.org/pypi/ofcourse/) 5 | [![downloads](https://img.shields.io/pypi/dm/ofcourse.svg)](https://pypi.python.org/pypi/ofcourse/) 6 | 7 | ## Warning 8 | 9 | This is an enormous cleanup and rename of HFLOSSK, and things will break at 10 | least temporarily. Old history will disappear. Just be aware. - ryansb 11/21/14 11 | 12 | ## About 13 | 14 | This repository is an experiment to use Flask, Mako, and Bootstrap to make a 15 | website for courses on Free/Open Source Software at RIT. 16 | 17 | This courseware and its predecessors have been used by 4 previous professors, 18 | who've run the course 5 separate times. Those profs are: 19 | 20 | - Stephen Jacobs 21 | - Dave Shein (x2) 22 | - Ralph Bean 23 | - Justin Sherrill 24 | 25 | ## Docs 26 | 27 | Docs are available on [ReadTheDocs][rtfd] 28 | 29 | [![readthedocs](https://readthedocs.org/projects/ofcourse/badge/?version=latest)](http://ofcourse.readthedocs.org/en/latest) 30 | 31 | ## Tests 32 | 33 | All tests are run using tox. To run the tests 34 | 35 | $ virtualenv --no-site-packages -p python2 ofcourse_env 36 | $ . ofcourse_env/bin/activate 37 | $ pip install tox 38 | $ tox 39 | 40 | Tests check validity of all yaml, and the keys in any student yaml files. Tests 41 | also checks that code conforms to PEP8. 42 | 43 | ## License 44 | 45 | Copyright 2013 Remy DeCausemaker 46 | 47 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 48 | this file except in compliance with the License. You may obtain a copy of the 49 | License at http://www.apache.org/licenses/LICENSE-2.0 50 | 51 | Unless required by applicable law or agreed to in writing, software distributed 52 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 53 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 54 | specific language governing permissions and limitations under the License. 55 | 56 | [rtfd]: http://ofcourse.readthedocs.org/ 57 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ofcourse.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ofcourse.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/ofcourse" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ofcourse" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/calendar.rst: -------------------------------------------------------------------------------- 1 | .. Calendar documentation 2 | 3 | Adding events to the calendar 4 | ============================= 5 | 6 | This tutorial will help you prepare a course outline for ofCourse. 7 | 8 | About ofCourse scheduling 9 | ------------------------- 10 | 11 | ofCourse schedules are in the `iCalendar format `__. 12 | The files, `course.ics`, can be edited in any calendar editor, including: 13 | 14 | - Google Calendar 15 | - Apple Calendar 16 | - Lightning (for Mozilla Thunderbird and Seamonkey) 17 | 18 | Events in ofCourse 19 | ------------------ 20 | 21 | Classes 22 | +++++++ 23 | 24 | A class session is an event, optionally tagged with the "Class" category. 25 | 26 | The event title will show up as the class topic. 27 | 28 | The description gets parsed for lines that are formatted like so:: 29 | 30 | ASSIGNED: First Flight Assignment 31 | ASSIGNED: Bug Report (no link) 32 | DUE: Midterm Project Outline 33 | 34 | These lines will appear in the "Assigned" and "Due" columns of the syllabus. 35 | 36 | Hackathons 37 | ++++++++++ 38 | 39 | A hackathon is also an event, with the mandatory "Hackathon" tag. 40 | 41 | The event location and URL (as a URL attachment) will be displayed. 42 | 43 | Special Events 44 | ++++++++++++++ 45 | 46 | Special events (IRC meetings, field trips) are all ordinary calendar events, though with appropriate categories. 47 | 48 | The default special categories are: 49 | 50 | - `Special` 51 | - `Cancelled` 52 | - `Guest` - for guest lectures 53 | - `Hackathon` 54 | - `Vaycay` for vacations 55 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # ofcourse documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Nov 22 09:16:35 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.doctest', 34 | 'sphinx.ext.todo', 35 | ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix of source filenames. 41 | source_suffix = '.rst' 42 | 43 | # The encoding of source files. 44 | #source_encoding = 'utf-8-sig' 45 | 46 | # The master toctree document. 47 | master_doc = 'index' 48 | 49 | # General information about the project. 50 | project = u'ofcourse' 51 | copyright = u'2014, Ryan S. Brown, Remy DeCausemaker' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.1-alpha' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | #language = None 65 | 66 | # There are two options for replacing |today|: either, you set today to some 67 | # non-false value, then it is used: 68 | #today = '' 69 | # Else, today_fmt is used as the format for a strftime call. 70 | #today_fmt = '%B %d, %Y' 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | exclude_patterns = ['_build'] 75 | 76 | # The reST default role (used for this markup: `text`) to use for all 77 | # documents. 78 | #default_role = None 79 | 80 | # If true, '()' will be appended to :func: etc. cross-reference text. 81 | #add_function_parentheses = True 82 | 83 | # If true, the current module name will be prepended to all description 84 | # unit titles (such as .. function::). 85 | #add_module_names = True 86 | 87 | # If true, sectionauthor and moduleauthor directives will be shown in the 88 | # output. They are ignored by default. 89 | #show_authors = False 90 | 91 | # The name of the Pygments (syntax highlighting) style to use. 92 | pygments_style = 'sphinx' 93 | 94 | # A list of ignored prefixes for module index sorting. 95 | #modindex_common_prefix = [] 96 | 97 | # If true, keep warnings as "system message" paragraphs in the built documents. 98 | #keep_warnings = False 99 | 100 | 101 | # -- Options for HTML output ---------------------------------------------- 102 | 103 | # The theme to use for HTML and HTML Help pages. See the documentation for 104 | # a list of builtin themes. 105 | html_theme = 'default' 106 | 107 | # Theme options are theme-specific and customize the look and feel of a theme 108 | # further. For a list of options available for each theme, see the 109 | # documentation. 110 | #html_theme_options = {} 111 | 112 | # Add any paths that contain custom themes here, relative to this directory. 113 | #html_theme_path = [] 114 | 115 | # The name for this set of Sphinx documents. If None, it defaults to 116 | # " v documentation". 117 | #html_title = None 118 | 119 | # A shorter title for the navigation bar. Default is the same as html_title. 120 | #html_short_title = None 121 | 122 | # The name of an image file (relative to this directory) to place at the top 123 | # of the sidebar. 124 | #html_logo = None 125 | 126 | # The name of an image file (within the static path) to use as favicon of the 127 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 128 | # pixels large. 129 | #html_favicon = None 130 | 131 | # Add any paths that contain custom static files (such as style sheets) here, 132 | # relative to this directory. They are copied after the builtin static files, 133 | # so a file named "default.css" will overwrite the builtin "default.css". 134 | html_static_path = ['_static'] 135 | 136 | # Add any extra paths that contain custom files (such as robots.txt or 137 | # .htaccess) here, relative to this directory. These files are copied 138 | # directly to the root of the documentation. 139 | #html_extra_path = [] 140 | 141 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 142 | # using the given strftime format. 143 | #html_last_updated_fmt = '%b %d, %Y' 144 | 145 | # If true, SmartyPants will be used to convert quotes and dashes to 146 | # typographically correct entities. 147 | #html_use_smartypants = True 148 | 149 | # Custom sidebar templates, maps document names to template names. 150 | #html_sidebars = {} 151 | 152 | # Additional templates that should be rendered to pages, maps page names to 153 | # template names. 154 | #html_additional_pages = {} 155 | 156 | # If false, no module index is generated. 157 | #html_domain_indices = True 158 | 159 | # If false, no index is generated. 160 | #html_use_index = True 161 | 162 | # If true, the index is split into individual pages for each letter. 163 | #html_split_index = False 164 | 165 | # If true, links to the reST sources are added to the pages. 166 | #html_show_sourcelink = True 167 | 168 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 169 | #html_show_sphinx = True 170 | 171 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 172 | #html_show_copyright = True 173 | 174 | # If true, an OpenSearch description file will be output, and all pages will 175 | # contain a tag referring to it. The value of this option must be the 176 | # base URL from which the finished HTML is served. 177 | #html_use_opensearch = '' 178 | 179 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 180 | #html_file_suffix = None 181 | 182 | # Output file base name for HTML help builder. 183 | htmlhelp_basename = 'ofcoursedoc' 184 | 185 | 186 | # -- Options for LaTeX output --------------------------------------------- 187 | 188 | latex_elements = { 189 | # The paper size ('letterpaper' or 'a4paper'). 190 | #'papersize': 'letterpaper', 191 | 192 | # The font size ('10pt', '11pt' or '12pt'). 193 | #'pointsize': '10pt', 194 | 195 | # Additional stuff for the LaTeX preamble. 196 | #'preamble': '', 197 | } 198 | 199 | # Grouping the document tree into LaTeX files. List of tuples 200 | # (source start file, target name, title, 201 | # author, documentclass [howto, manual, or own class]). 202 | latex_documents = [ 203 | ('index', 'ofcourse.tex', u'ofcourse Documentation', 204 | u'Ryan S. Brown, Remy DeCausemaker', 'manual'), 205 | ] 206 | 207 | # The name of an image file (relative to this directory) to place at the top of 208 | # the title page. 209 | #latex_logo = None 210 | 211 | # For "manual" documents, if this is true, then toplevel headings are parts, 212 | # not chapters. 213 | #latex_use_parts = False 214 | 215 | # If true, show page references after internal links. 216 | #latex_show_pagerefs = False 217 | 218 | # If true, show URL addresses after external links. 219 | #latex_show_urls = False 220 | 221 | # Documents to append as an appendix to all manuals. 222 | #latex_appendices = [] 223 | 224 | # If false, no module index is generated. 225 | #latex_domain_indices = True 226 | 227 | 228 | # -- Options for manual page output --------------------------------------- 229 | 230 | # One entry per manual page. List of tuples 231 | # (source start file, name, description, authors, manual section). 232 | man_pages = [ 233 | ('index', 'ofcourse', u'ofcourse Documentation', 234 | [u'Ryan S. Brown, Remy DeCausemaker'], 1) 235 | ] 236 | 237 | # If true, show URL addresses after external links. 238 | #man_show_urls = False 239 | 240 | 241 | # -- Options for Texinfo output ------------------------------------------- 242 | 243 | # Grouping the document tree into Texinfo files. List of tuples 244 | # (source start file, target name, title, author, 245 | # dir menu entry, description, category) 246 | texinfo_documents = [ 247 | ('index', 'ofcourse', u'ofcourse Documentation', 248 | u'Ryan S. Brown, Remy DeCausemaker', 'ofcourse', 'One line description of project.', 249 | 'Miscellaneous'), 250 | ] 251 | 252 | # Documents to append as an appendix to all manuals. 253 | #texinfo_appendices = [] 254 | 255 | # If false, no module index is generated. 256 | #texinfo_domain_indices = True 257 | 258 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 259 | #texinfo_show_urls = 'footnote' 260 | 261 | # If true, do not generate a @detailmenu in the "Top" node's menu. 262 | #texinfo_no_detailmenu = False 263 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. ofcourse documentation master file, created by 2 | sphinx-quickstart on Sat Nov 22 09:16:35 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to ofcourse's documentation! 7 | ==================================== 8 | 9 | :doc:`start` 10 | 11 | :doc:`calendar` 12 | 13 | :doc:`internals` 14 | 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/internals.rst: -------------------------------------------------------------------------------- 1 | .. A tour of the ofcourse internals 2 | 3 | Under the Hood 4 | ============== 5 | 6 | The Web App 7 | ----------- 8 | 9 | `Flask`_ powers the web site, rendering all the page templates and handling 10 | requests. 11 | 12 | The reason for using Flask instead of generating the site statically is for the 13 | blog scraper. OfCourse will automatically crawl the RSS/Atom feeds on student 14 | blogs to determine how many posts they've made and whether they're meeting the 15 | assigned post quota. 16 | 17 | .. _Flask: http://flask.pocoo.org/ 18 | 19 | The CLI Tool 20 | ------------ 21 | 22 | The CLI tool is a `Click`_ application that uses `dulwich`_ and the OpenShift 23 | API to deploy your course seamlessly. 24 | 25 | .. _Click: http://click.pocoo.org/ 26 | .. _dulwich: https://www.samba.org/~jelmer/dulwich/ 27 | -------------------------------------------------------------------------------- /docs/ofcourse-workflow.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/docs/ofcourse-workflow.dia -------------------------------------------------------------------------------- /docs/start.rst: -------------------------------------------------------------------------------- 1 | .. Getting started docs 2 | 3 | Get started with ofcourse 4 | ========================= 5 | 6 | This tutorial will help you install the ofcourse tools, create a 7 | site, and deploy your site on OpenShift. 8 | 9 | Installing ofcourse 10 | ------------------- 11 | 12 | Before you can do anything with this (run the webserver locally, or any of the 13 | scripts) you'll need to setup and activate a python `virtualenv 14 | `_. Run the following at the command 15 | prompt... 16 | 17 | On Linux/Mac OS X 18 | +++++++++++++++++ 19 | 20 | If you don't have virtualenv installed yet, try:: 21 | 22 | $ sudo pip install virtualenv virtualenvwrapper 23 | 24 | If you're using a distro like Fedora or Ubuntu, you should try this instead:: 25 | 26 | Fedora: 27 | $ sudo yum install python-virtualenv 28 | 29 | Ubuntu/Debian: 30 | $ sudo apt-get install python-virtualenv 31 | 32 | Once you have virtualenv installed, you should be able to run:: 33 | 34 | $ virtualenv --no-site-packages -p python2 ofcourse_environment 35 | $ source ofcourse_environment/bin/activate 36 | $ pip install ofcourse 37 | 38 | To actively develop ofcourse, try this instead:: 39 | 40 | $ git clone https://github.com/ryansb/ofCourse ofcourse 41 | $ cd ofcourse 42 | $ virtualenv --no-site-packages -p python2 ofcourse_environment 43 | $ source ofcourse_environment/bin/activate 44 | $ python setup.py develop 45 | 46 | 47 | On Windows 48 | ++++++++++ 49 | 50 | At the windows command prompt:: 51 | 52 | $ virtualenv --no-site-packages -p python2 ofcourse_environment 53 | $ ofcourse_environment/Scripts/activate.bat 54 | 55 | In msysGit or git-bash:: 56 | 57 | $ git clone https://github.com/ryansb/ofCourse.git 58 | 59 | Back in the windows command prompt:: 60 | 61 | $ cd ofCourse 62 | $ python setup.py develop 63 | 64 | On Both Platforms 65 | +++++++++++++++++ 66 | 67 | After completing these instructions you should be able to see the CLI tool 68 | documentation by typing into your prompt:: 69 | 70 | $ ofcourse 71 | 72 | 73 | Creating a New Course 74 | --------------------- 75 | 76 | Before you can follow these instructions, you'll need to setup and activate a 77 | python `virtualenv `_. Run the 78 | following at the command prompt... 79 | 80 | On Linux/Mac OS X 81 | +++++++++++++++++ 82 | 83 | If you don't have virtualenv installed yet, try:: 84 | 85 | $ sudo pip install virtualenv virtualenvwrapper 86 | 87 | If you're using a distro like Fedora or Ubuntu, you should try this instead:: 88 | 89 | Fedora: 90 | $ sudo yum install python-virtualenv 91 | 92 | Ubuntu/Debian: 93 | $ sudo apt-get install python-virtualenv 94 | 95 | Once you have virtualenv installed, you should be able to run:: 96 | 97 | $ cd code 98 | $ virtualenv --no-site-packages -p python2 ofcourse_environment 99 | $ . ./ofcourse_environment/bin/activate 100 | $ pip install ofcourse 101 | $ mkdir YOUR_COURSENAME 102 | $ cd YOUR_COURSENAME 103 | $ ofcourse new 104 | 105 | Once you've done that you'll probably want to edit some of the 106 | pre-generated files for the course. That way the website will 107 | reflect your course's information. Check your course out locally with:: 108 | 109 | $ ofcourse run 110 | 111 | And go to http://localhost:5000 in your browser to see what you have. 112 | 113 | After you're satisfied with your content, create a GitHub repository and add 114 | your files.:: 115 | 116 | $ git init 117 | $ git add . 118 | $ git commit -m 'initial commit' 119 | $ git remote add origin git@github.com:YOUR_USERNAME/YOUR_COURSENAME.git 120 | $ git push origin master 121 | 122 | And now your course is published on GitHub! 123 | 124 | On Windows 125 | ++++++++++ 126 | 127 | At the windows command prompt:: 128 | 129 | $ virtualenv --no-site-packages -p python2 ofcourse_environment 130 | $ ofcourse_environment/Scripts/activate.bat 131 | $ pip install ofcourse 132 | 133 | Make the repository on GitHub, then use msysGit or git-bash:: 134 | 135 | $ git clone git@github.com:YOUR_USERNAME/YOUR_COURSENAME-content.git 136 | 137 | Back in the windows command prompt:: 138 | 139 | $ cd YOUR_COURSENAME-content 140 | $ ofcourse new 141 | 142 | Once you've done that you'll probably want to edit some of the 143 | pre-generated files for the course. That way the website will 144 | reflect your course's information. Check your course out locally with:: 145 | 146 | $ ofcourse run 147 | 148 | When you're ready, add the files and push them to GitHub with git-bash or 149 | msysGit:: 150 | 151 | $ git add . 152 | $ git commit -m 'Add ofcourse templates' 153 | $ git push origin master 154 | 155 | And now your course is published on GitHub! 156 | 157 | Deploying to OpenShift 158 | ++++++++++++++++++++++ 159 | 160 | Deploying to OpenShift is fairly simple once you have your local instance up and 161 | running. Before deploying you need to make sure that you have an openshift 162 | account and a few extra things configured. 163 | 164 | If you don't yet have an account feel free to go to openshifts account creation 165 | page which can be found `here `__. 166 | 167 | Once you have an account created, you then need to go to your 168 | `settings page `_ and set 169 | the domain name for the app that you will be using. This creates a url which 170 | will look similar to the following where {{domain-name}} is substituted with 171 | what you enter. 172 | 173 | http://{{application-name}}-{{domain-name}}.rhcloud.com 174 | 175 | After you have your domain name set, the last think you need to do is submit 176 | ssh key which will allow you to push your newly created code to OpenShift's 177 | servers. Github provides a fantastic guide on setting up your SSH keys 178 | `here `__, if you have 179 | already created your SSH key, simply paste them into the text box 180 | `here `__. 181 | 182 | Lastly you'll need to choose an app name. Lets say for this example we will be 183 | calling our app `SuperAwesomeApp`. Simply run the following:: 184 | 185 | ofcourse openshift --app SuperAwesomeApp 186 | 187 | Follow the instructions on the screen and your app should be deployed now! 188 | -------------------------------------------------------------------------------- /ofcourse-templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block jumbotron %} 4 |

Hello, world!

5 |

This is a wonderful experiment using Twitter Boostrap, Flask, and Jinja2 to make awesome.

6 |

Learn more »

7 | {% endblock %} 8 | {% block body %} 9 |
10 |
11 |

Twitter Bootstrap

12 |

"Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development."

13 |

View details »

14 |
15 |
16 |

Flask

17 |

"Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions."

18 |

View details »

19 |
20 |
21 |

Jinja2

22 |

"Jinja2 is a modern and designer-friendly templating language for 23 | Python, modelled after Django’s templates. It is fast, widely used 24 | and secure with the optional sandboxed template execution 25 | environment."

26 |

View details »

27 |
28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /ofcourse-templates/blogs.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% block head %} 3 | 4 | 5 | 6 | 40 | {% endblock %} 41 | {% macro student_block(student) %} 42 |
43 |
44 | 45 |
46 |

{{student.irc}}

47 |
48 |
49 |
50 | 51 | {{student.irc}}'s Avatar 52 | 53 |
54 | Blog 55 | 56 |
57 |
    58 | {% for forge_link in studentforges %} 59 |
  • {{forge_link}}
  • 60 | {% endfor %} 61 | 62 | {% set hw = student.hw|default({}) %} 63 | {% if student.isActive %} 64 | {% for key in hw_keys %} 65 | {% if key in hw %} 66 |
  • {{key}}
  • 67 | {% else %} 68 |
  • {{key}}?
  • 69 | {% endif %} 70 | {% endfor %} 71 | {% else %} 72 | {% for key, loc in hw.items() %} 73 |
  • {{key}}
  • 74 | {% endfor %} 75 | {% endif %} 76 | 77 | {# 78 | # This block used for quick grading reference ;) 79 | {% if student.get('name') %} 80 |
  • {{student.name}}
  • 81 | {% endif %} 82 | #} 83 |
84 |
85 |
86 |
87 | {% endmacro %} 88 | {% block jumbotron %} 89 |

Participants

90 |

Should have {{target_number|int}} blog post(s)

91 | {% endblock %} 92 | {% block body %} 93 |
94 | {% for student in student_data %} 95 | {{ student_block(student) }} 96 | {% if loop.index % 3 == 0 %} 97 |
98 |
99 | {% endif %} 100 | {% endfor %} 101 |
102 | {% endblock %} 103 | -------------------------------------------------------------------------------- /ofcourse-templates/calendar.html: -------------------------------------------------------------------------------- 1 | {% macro assignment_cell(values) %} 2 | 3 | {% for _, name, link in values %} 4 |

5 | {% if link %} 6 | {{name|safe}} 7 | {% else %} 8 | {{name}} 9 | {% endif %} 10 |

11 | {% endfor %} 12 | 13 | {% endmacro %} 14 | 15 | {% macro title_cell(ev) %} 16 | {% set cats = normalize_categories(ev) %} 17 |

18 | {% if 'attach' in ev %} 19 | 20 | {{ev['summary']}} 21 | 22 | {% else %} 23 | {{ev['summary']}} 24 | {% endif %} 25 |

26 | {% endmacro %} 27 | 28 | {% macro class_period(n, ev) %} 29 | {{n}} 30 | {{ev.decoded('dtstart').strftime("%x")}} 31 | {{title_cell(ev)}} 32 | {% set assignments = assignment_data(ev.get('description','')) %} 33 | {{ assignment_cell(items_assigned(assignments)) }} 34 | {{ assignment_cell(items_due(assignments)) }} 35 | {% endmacro %} 36 | 37 | {% macro hackathon(ev) %} 38 | *** 39 | 40 | {{ev.decoded('dtstart').strftime("%x %X")}} - {{ev.decoded('dtend').strftime("%x %X")}} 41 | 42 | {{title_cell(ev)}} 43 | {{ev.decoded('location')|default("")}} 44 | 45 | {% endmacro %} 46 | 47 | {% macro calendar(cal) %} 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {% for week in calendar_weeks(cal) %} 66 | {% set week_loop = loop %} 67 | {% set class_count = 0 %} 68 | {% for event in week %} 69 | 70 | {% if loop.first %} 71 | 72 | {% endif %} 73 | {% if 'hackathon' in normalize_categories(event)%} 74 | {{hackathon(event)}} 75 | {% else %} 76 | {% set class_count = class_count + 1 %} 77 | {{class_period(class_count, event)}} 78 | {% endif %} 79 | 80 | {% endfor %} 81 | {% endfor %} 82 | 83 |
WeekDayTopicAssignedDue
{{week_loop.index}}
84 | {% endmacro %} 85 | -------------------------------------------------------------------------------- /ofcourse-templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% block jumbotron %} 3 |

{{ course.name }}

4 |

{{ course.desc }}

5 | {% endblock %} 6 | {% block body %} 7 |
8 |
9 |

Twitter Bootstrap

10 |

"Sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development."

11 |

View details »

12 |
13 |
14 |

Flask

15 |

"Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions."

16 |

View details »

17 |
18 |
19 |

Jinja2

20 |

"Jinja2 is a modern and designer-friendly templating language for 21 | Python, modelled after Django’s templates. It is fast, widely used 22 | and secure with the optional sandboxed template execution 23 | environment."

24 |

View details »

25 |
26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /ofcourse-templates/hw/firstflight.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block jumbotron %} 4 |

First Flight

5 | {% endblock %} 6 | 7 | {% block body %} 8 |

The purpose of this homework assignment is to introduce 9 | students to their first FLOSS practices. Read it in full, there 10 | are a number of graded deliverables.

11 | 12 |

The due-date is listed in the Syllabus.

13 | 14 |

IRC

15 | 16 |

IRC is one of the primary means of communication for a FOSS 17 | community, particularly for informal communication.

18 | 19 |

There is a course IRC channel on irc.freenode.net. The channel 20 | is #rit-foss. Communicating regularly in IRC factors into the 21 | FOSS Dev Practices component of your final grade.

22 | 23 |
24 |

Tasks:

25 | 26 |
    27 |
  • Download and install an IRC client on your development machine. 28 | 45 |
  • 46 |
  • Choose a nick and register yourself with 48 | the NickServ.
  • 49 |
  • Connect to #rit-foss on irc.freenode.net and introduce yourself. 50 |
      51 |
    • The instructor’s nick is decause.
    • 52 |
    53 |
  • 54 |
55 |
56 | 57 |

58 | It is a good practice to “hang out” in IRC channels of projects that you use 59 | and especially of projects that you contribute to. Here you can find early 60 | alerts regarding any upcoming major changes or security vulnerabilities. It is 61 | also the easiest (lowest overhead) method for getting your questions answered. 62 |

63 | 64 |
65 |

66 | Note 67 |

68 | 69 |

70 | Only for the brave – if you want to be completely awesome, you can setup a 71 | proxy node so you are always logged in. People can leave you messages this way. 72 |

73 | 74 |

75 | If you want to be completely completely awesome, you can setup BitlBee so you can tweet from your IRC client. 76 |

77 | 78 | 79 |

Mailman

80 | 81 |

82 | Discussion mailing lists are a more formal mechanism of communication for FOSS 83 | projects. More formal than IRC, less formal than bug trackers. Discussion 84 | mailing lists are often used to ask questions, announce upcoming releases and 85 | beta tests, and to debate redesigns and refactors. The advantage here is that 86 | mailing lists are typically archived and indexed by Google; discussions that 87 | should be preserved for posterity should occur here. Upstream projects usually 88 | have an existing mailing list where messages of these sort are to be posted. 89 |

90 | 91 | 92 |

Blogging

93 | 94 |

Setup a blog if you don’t have one. Much like mailing lists, blogs are 95 | archived, indexed by Google, and therefore preserved for posterity. When 96 | you encounter a technical challenge, typically you google for a solution 97 | and you typically find that solution in a blog post of some developer who 98 | has run into a similar situation. Blogging about your attempts, successes 99 | and failures (and writing tutorials!) is a best practice for increasing the 100 | general body of searchable knowledge available, for increasing the Wisdom of the Ancients.

101 | 102 |

103 | Blogs around a topic are also typically aggregated by a planet (an RSS feed 104 | aggregator). This way, all developers blogging about Project X can have their 105 | blog posts fast-tracked to a readership subscribed to Planet X. For instance, 106 | here’s a link to Planet Python. 107 |

108 | 109 |

110 | The Planet for the course may be hosted at some point in the future at 111 | http://yacht.rit.edu/planet. 113 |

114 | 115 | 116 |

117 | You must create a blog (if you don’t have one already) and write at least one 118 | post per week about your progress, attempts, successes, failures, reflections, 119 | and/or all of the above. 120 |

121 | 122 |
123 |

Tasks:

124 | 125 |
    126 |
  1. 127 | Create a blog if you don’t already have one. There are lots of free 128 | services available. You might try http://wordpress.com or 129 | http://blogspot.com, or 131 | even http://foss.rit.edu. 133 |
  2. 134 |
  3. Write an introductory post, detailing the process you went through 135 | to complete the FirstFlight assignment.
  4. 136 |
137 |
138 | 139 |
140 |

141 | Note 142 |

143 | 144 |

145 | 146 | Attention Wordpress Users! 147 | 148 | By default Wordpress will limit the number of posts that are listed in the 149 | RSS feed to 10. This will create an issue roughly halfway through the term when 150 | you are supposed to have more than 10 blog posts. In order to fix this issue 151 | you must take the following steps. 152 |

    153 |
  1. 154 | Log in to your Wordpress Administration Site at https://WORDPRESS_BLOG_NAME.wordpress.com/wp-admin/options-reading.php 155 |
  2. 156 |
  3. 157 | Go to Settings 158 |
  4. 159 |
  5. 160 | Go to Reading 161 |
  6. 162 |
  7. 163 | Set "Syndication feeds show the most recent" 164 |
  8. 165 |
166 |

167 |
168 | 169 |

Github

170 | 171 |

172 | Code forges are service sites around which FOSS development orbits, some of 173 | the more popular sites are github, bitbucket, sourceforge, and launchpad. 174 |

175 | 176 |

177 | For your own enlightenment, review the following comparisons of the different 178 | forges: 179 |

180 | 181 | 189 | 190 |

191 | You’ll need to create your own account on github.com. All development for this 192 | course should be tracked on that forge. Github is, after all, the most popular 193 | forge. 194 |

195 |
196 |

Tasks:

197 |
    198 |
  1. Create a Github account if you don’t already have one.
  2. 199 |
200 |
201 |
202 |

Upload an Avatar

203 |

This is optional (but appreciated). Humans identify with faces more than they do nicknames (surprise!), so go ahead and associate an avatar that'll automatically show up in GitHub, the course web site, and other places around the web.

204 |

Gravatar

205 |

Gravatar is now owned by Automattic, who also make WordPress. Go to their site and create a WordPress account (or use one you have already) and make sure your RIT email is in there.

206 |

Libravatar

207 |

Libravatar is a free equivalent to Gravatar and has the advantage of not requiring a WordPress.com account. Head over to their New Account page and link up your email address with a picture.

208 |
209 |
210 |

Patch the Course Project

211 |

Check out the source repository for this course; it’s hosted at 212 | {{course.repo}}.

213 |

Inside the repository, we’ll keep an index of all the students in the course and metadata about them (you!).

214 |
215 |

Tasks:

216 |
    217 |
  • Load up the git cheatsheet at Zack Rusin's blog and keep it nearby.
  • 218 |
  • Work through this git tutorial if you don’t have any experience with git.
  • 219 |
  • Fork the repository (link to github help on this).
  • 220 |
  • Clone a local copy.
  • 221 |
  • Add a file in the /scripts/people/$YEAR/$TERM folder titled $YOUR_IRC_NICK.yaml. Perhaps obviously, it is 222 | a YAML file. You can use the rjbpop.yaml file as an example. You will want to make sure 223 | that you have $TERM in all lowercase. For example a student in HFOSS fall of 2014 would have their YAML file in the 224 | /scripts/people/2014/fall folder. 225 | 226 | 227 |
    228 | BE WARNED: Your .yaml file must match the format *exactly* (meaning it is case and whitespace sensitive.) 229 |
    230 |
    231 | CRUFTY: There is a people.yaml file in that directory. It is a legacy hangover from older code. Do not bother editing it. It will actually make merges more difficult. 232 | The .yaml file contains a name field, which will be used to refer to you publicly. Feel free to use a nickname or just your first name if you prefer. 233 |
  • 234 | 235 |
  • Once you've confirmed your .yaml file matches exactly, commit and push your changes to github, and issue a pull request.
  • 236 |
  • Once the patch is accepted upstream and pushed to production, this 237 | should add your blog feed to the Participants page.)
  • 238 |
239 |
240 |
241 | {% endblock %} 242 | -------------------------------------------------------------------------------- /ofcourse-templates/hw/index.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block jumbotron %} 4 |

HW

5 |

"Work From Home" gets better later, promise.

6 | {% endblock %} 7 | {% block body %} 8 |
9 |
10 |
    11 |
  • Tasks

  • 12 | {% for hw, loc in assignments|dictsort %} 13 |
  • {{hw}}
  • 14 | {% endfor %} 15 |
16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /ofcourse-templates/instructor.html: -------------------------------------------------------------------------------- 1 | {% extends "profile.html" %} 2 | {% block jumbotron %} 3 |

Instructor

4 |

Failing early and often, so you won't have to

5 | {% endblock %} 6 | {% block body %} 7 | {{ profile(instructor, instructor.get('avatar', "")) }} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /ofcourse-templates/lectures/index.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block jumbotron %} 4 |

LN

5 |

Lecture Notes: Agenda-y type things to keep the train on the tracks

6 | {% endblock %} 7 | {% block body %} 8 |
9 |
10 | 16 |
17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /ofcourse-templates/lectures/w01c2.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | 3 | {% block jumbotron %} 4 |

W1C2

5 |

Week 1 - Class 2

6 | {% endblock %} 7 | 8 | {% block body %} 9 |
10 |

Agenda:

11 | 12 |
    13 |
  1. 14 |

    Welcome

    15 | 16 |
      17 |
    • /whois decause
    • 18 |
    19 |
  2. 20 | 21 |
  3. 22 |

    RollCall

    23 |
      24 |
    • Name

    • 25 |
    • Year/Major

    • 26 |
    • Weapon Of Choice

    • 27 |
    • Favorite Game

    • 28 |
    29 |
  4. 30 | 31 |
  5. 32 |

    Review Hackathon Schedule

    33 | 38 |
  6. 39 | 40 |
  7. 41 |

    Assignment

    42 | 45 |
  8. 46 | 47 |
48 |
49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /ofcourse-templates/master.html: -------------------------------------------------------------------------------- 1 | 2 | {% macro title() %} 3 | {{course.name}}@{{course.place}} 4 | {% endmacro %} 5 | {% macro glyphicon(glyph) %} 6 | 7 | {% endmacro %} 8 | 9 | 10 | 11 | {% block title %}{{title()}}{% endblock %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | {% block head %} 30 | {% endblock %} 31 | 32 | 33 | 34 | 35 | 59 | 60 |
61 |
62 |
63 |
64 |
    65 |
  • {{glyphicon('calendar')}} {{course.start}} — {{course.end}}
  • 66 |
  • {{glyphicon('bell')}} {{course.times}}
  • 67 |
  • {{glyphicon('shopping-cart')}} {{course.course}}
  • 68 |
  • {{glyphicon('map-marker')}} {{course.location}}
  • 69 |
  • {{glyphicon('envelope')}} 70 | {{instructor.email}} 71 |
  • 72 |
73 |
74 | {% block doc_toc %} 75 | {% endblock %} 76 |
77 |
78 |
79 | {% block jumbotron %} 80 | {% endblock %} 81 |
82 | {% block body %} 83 | {% endblock %} 84 |
85 |
86 | 87 |
88 | 89 |
90 |
91 | 92 |
93 | 94 | 96 | 97 | 98 | 99 | 100 | 101 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ofcourse-templates/ohno.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% block jumbotron %} 3 |

Somthing is Borked

4 |

Try reloading this page again later...

5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /ofcourse-templates/participant.html: -------------------------------------------------------------------------------- 1 | {% extends "profile.html" %} 2 | {% block jumbotron %} 3 |

{{participant_data.irc}}

4 |

{{participant_data.name}}

5 | {% endblock %} 6 | {% block body %} 7 | {{ profile(participant_data, gravatar(participant_data, 'rit_dce', '@rit.edu')) }} 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /ofcourse-templates/profile.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% block head %} 3 | 4 | 5 | 6 | {% endblock %} 7 | {% macro profile(data, avatar) %} 8 |
9 |
10 | {% if avatar %} 11 | {{data.name}}'s avatar 12 | {% endif %} 13 |

Blog

14 |

15 | {% if data.get('coderwall' ) %} 16 |

17 | {% endif %} 18 | 19 | {% if data.get('github') %} 20 |

GitHub

21 | 22 | {% endif %} 23 | 24 | {% if data.get('openhub') %} 25 | 26 | OpenHub profile for {{data.openhub}} 27 | 28 | {% endif %} 29 | 30 | {% if data.forges|default([])|length %} 31 |

Forges

32 |
    33 | {% for forge_name in data.forges %} 34 |
  • {{forge_name}}
  • 35 | {% endfor %} 36 |
37 | {% endif %} 38 | 39 | 40 | {% if data.links|default([])|length %} 41 |

Elsewhere

42 |
    43 | {% for key, link in instructor.links.items() %} 44 |
  • {{key}}
  • 45 | {% endfor %} 46 |
47 | {% endif %} 48 |

49 |
50 |
51 |

52 | {% if 'bio' in data %} 53 |

Bio

54 | {{data.bio}} 55 | {% endif %} 56 | 57 | {% if 'twitter' in data %} 58 |

Twitterverse

59 | 60 | {{- raw }} 61 | 62 | {{ endraw -}} 63 | {% endif %} 64 |

65 |
66 |
67 | {% endmacro %} 68 | -------------------------------------------------------------------------------- /ofcourse-templates/quiz/quiz1.html: -------------------------------------------------------------------------------- 1 | {% extends "../master.html" %} 2 | 3 | {% block jumbotron %} 4 |

TOSW

5 |

The Success of Open Source & The Open Source Way

6 | 7 | 8 | {% block body %} 9 |

10 | In Chapter 3 of Stephen Weber's The Success of Open Source, there was 11 | a listing of eight "General Principles" contained within the chapter. Last 12 | week, we discussed in class the five pillars of "The Open Source Way." 14 |

15 | 16 | 17 |
    18 |
  1. What are the titles of each Pillar?
  2. 19 |
  3. What are the titles of each General Principle?
  4. 20 |
  5. What are the similarities between Weber's eight principles, and the five pillars?
  6. 21 |
  7. What are the differences?
  8. 22 |
  9. Bonus: Whose "keen analysis" did Weber "Draw Heavily" upon?"
  10. 23 |
  11. Double-Bonus: 24 | What was the title of this "keen analysis." 25 | (Hint: Weber hinted at the title when he was talking about Cathedrals earlier in the chapter.)
  12. 26 |
  13. Triple-Bonus: 27 | Where can this keen analysis be found? (Answer in URL form, i.e. http://placeofkeenanalysisis.com/analysis)
  14. 28 |
29 | 30 |
31 |

Q: How do I quiz?

32 |

A: 3 Steps:

33 |
    34 |
  1. Create a blog post that includes each question, with the answer to each question below it, on your HFOSS Blog you set up in First Flight.
  2. 35 |
  3. Add a line to the bottom of your .yaml file you edited in first flight, with the link to your post.
  4. 36 |
  5. Send decause a pull request. Once your pull-request is accepted, you have completed the quiz.
  6. 37 |
38 |
39 | -------------------------------------------------------------------------------- /ofcourse-templates/resources.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% block jumbotron %} 3 |

OER FTW

4 |

Open Educational Resources (For the Win)

5 | {% endblock %} 6 | {% block body %} 7 |
8 | {% for res_type, res_list in resources.items() if res_type != 'links' %} 9 |
10 |

{{res_type}}

11 |
    12 | {% for res in res_list %} 13 |
  • {{res}}
  • 14 | {% endfor %} 15 | {% if res_type.lower() in resources.links %} 16 | {% for res_name, res in resources.links[res_type.lower()].items() %} 17 |
  • {{res_name|replace("_", " ")}}
  • 18 | {% endfor %} 19 | {% endif %} 20 |
21 |
22 | {% endfor %} 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /ofcourse-templates/syllabus.html: -------------------------------------------------------------------------------- 1 | {% extends "master.html" %} 2 | {% import "calendar.html" as calendar with context %} 3 | 4 | {% block title %} 5 | Syllabus — The RIT HFOSS Development Course documentation 6 | {% endblock %} 7 | 8 | {% block doc_toc %} 9 |
10 | 20 |
21 | {% endblock %} 22 | 23 | {% block jumbotron %} 24 |

Syllabus

25 | {% endblock %} 26 | 27 | {% block body %} 28 |
29 | 30 |

{{course.desc}}

31 |
32 |
33 |
    34 |
  • Instructor - {{instructor.name}} <{{instructor.email}}>
  • 35 |
  • Office: {{instructor.office.location}}
  • 36 |
  • Office Hours: {{instructor.office.hours}}
  • 37 | {% if assistant: %} 38 |
  • Teaching Assistant - {{assistant.name}} <{{assistant.email}}>
  • 39 | {% endif %} 40 |
  • IRC - irc.freenode.net, #rit-foss
  • 41 | 42 |
43 | The source for this syllabus can be found at 44 | {{course.repo}} 45 | 46 |
47 |
48 |
49 |
50 | 51 |

Text Books

52 |

There are a number of textbooks we’ll be referencing throughout the semester. You can 53 | find these books/texts/articles here on the resources page

54 |
55 |
56 | 57 |

What You’ll Do

58 |

This course will introduce students to the Free and Open Source Software (FOSS) 59 | and Open Content movements, to the open source development process, 60 | and to the open questions of the efficacy of technology in the classroom.

61 |

Students will learn FOSS process and Tools with class projects that 62 | support the One Laptop Per Child community by creating content and 63 | software for free distribution to students and teachers around the world. 64 | The OLPC project is driven by a world-wide community–one that students 65 | in HFOSS will become part of.

66 |

For this course students will be expected to attend and make final 67 | presentations to the RIT and Rochester FOSS communities via the regular 68 | Rochester Pythonistas meet-ups and FOSSBox hack-a-thons when possible. 69 | Students will also become members of the Sugar and OLPC international 70 | communities. Local FOSS community members may join us in class sessions as 71 | well. Treat them as you would another instructor, but they’re also your 72 | peers in moving this innovative project forward.

73 |
74 |
75 | 76 |

The spirit of the course

77 |

While still a course where you will receive a letter grade, the spirit of the 78 | course is intended to be both open and fun.

79 |

An open course – students will have access to the ‘document source’ for the 80 | syllabus. While you are reading the syllabus right now, 81 | as a student of the class you have a right to 82 | fork the upstream repository, make modifications, 83 | and submit patches for review. Barring a troll festival, this can create a fun, 84 | dynamic environment in which the course curriculum can develop by the very same 85 | mechanism being taught during the semester (community-driven).

86 |
87 |
88 | 89 |

Licensing

90 |

All code developed by students in the course must be licensed (by the student) under any one of the 91 | licenses approved by the Open Source Initiative.

92 |

Code that you write is your code, with which you can do what you will; 93 | true. However, if you’re unwilling to license code you write for an Free/Open Source 94 | course with a Free/Open Source license, you may be in the wrong course.

95 |
96 |
97 | 98 |

Schedule

99 | {{calendar.calendar(load_calendar("course.ics"))}} 100 |
101 | 102 |
103 | 104 |

Attendance

105 |

Attendance is required for this course. Students are allotted 2 excused absences per semester.

106 |

Subsequent absences will result in a 10% reduction of your final letter grade for each class missed.

107 |
108 |
109 | 110 |

Grading

111 |

Assignments are due at 4:59pm of the day they are marked as due, to be useful in class.

112 |

Late submissions will be deducted 10% per day they are late.

113 |
114 |

Your final grade for the semester will be derived from the following weights.

115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | {% macro component(name, cls, value) %} 128 | 129 | 130 | 131 | {% endmacro %} 132 | 133 | {{ component("In-Class Participation", "label-warning", "10%") }} 134 | {{ component("Quizzes", "label-warning", "10%") }} 135 | {{ component("Literature Reviews", "label-warning", "10%") }} 136 | {{ component("Team Peer Assessment", "label-info", "15%") }} 137 | {{ component("Completed Project", "label-info", "15%") }} 138 | {{ component("Final Presentation", "label-success", "20%") }} 139 | {{ component("FOSS Dev Practices (Blog posts, commits, tickets, IRC)", "label-success", "20%") }} 140 | 141 |
ComponentWeight
{{name}}{{value}}
142 | 143 |
144 |

Blog updates – students are required to keep a blog to which they post updates 145 | about their investigations, progress, success, and pitfalls. This blog can be 146 | hosted anywhere, but must be added to the course participant page (there are instructions on how to do this 147 | in Homework - First Flight).

148 |
149 |
    150 |
  • You must make at least one blog post per week to receive full credit. 151 | This is in addition to any assignments that are posted to your blog for 152 | that week. A week ends on Sunday at 11:59pm.
  • 153 |
  • You must participate regularly in the course’s IRC channel: asking and answering questions.
  • 154 |
  • Contributions to the course curriculum, syllabus, and rubric are factored in here as well.
  • 155 |
156 |
157 |

Blogging is good for you and good for the FLOSS community at large.

158 |

The details for the final can be found at Final.

159 |
160 |
161 | 162 |

Lightning Talks - Extra Credit

163 |

Every Wednesday for the first portion of class, any student has the opportunity 164 | to give a lightning talk on a 165 | topic of their chosing. Your lightning talk must be less than 5 minutes in 166 | length and must be at least remotely related to the course material.

167 |

You will receive +1 extra credit points towards your final grade for every 168 | lightning talk you give. Only the first 2 lightning talks offered will be allowed during a given 170 | class. Talks will be chosen from among those offered by students on a FIFO 172 | basis.

173 |
174 | {% endblock %} 175 | -------------------------------------------------------------------------------- /ofcourse-templates/weeklybreakdown.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 1 18 | 26 a Welcome, intro, Syllabus, RTFM 19 | 28 a First Flight - get ready for lmacken 20 | 2 21 | 2 a Guest Lecture: lmacken - Red Hat Software Engineer 22 | 4 b Intro to Git and Github 23 | 3 24 | 9 a LIT: What is Open Source? 25 | 11 b LIT: Rhetoric, Flamewars, and Trolling 26 | 4 27 | 16 a EDU: Pedagogy and Grokkery 28 | 18 b EDU: Curriculum Exploration 29 | Bonus: Software Freedom Day 30 | 5 31 | 23 a Commarch: Git-by-a-bus and CCF: Callaway Coefficient of Fail 32 | 25 b Commarch: Gource (Guest Lecture zanarama) 33 | 6 34 | 30 a Project Pitches 35 | 2 b Team Formation 36 | 7 37 | 7 a OLPC Distribution. Smoke Tests. Vote on Wed Class 38 | 9 b 39 | Bonus: Weekend Hackathon at SU. 40 | 8 41 | 15 a XXX Cancelled: RIT closing? Tues ROC.py Meetup 42 | 16 b XXX Cancelled: Prof. Flys to Google 43 | 9 44 | 21 a Guest Lecture: threebean - Red Hat 45 | 23 b TBD 46 | 10 47 | 28 a Vote on Wed Class. Vote on Monday Class. 48 | 30 b 49 | Bonus: BarcampROC and Pre-BarcampROC Hackathon 50 | 11 51 | 4 a XXX No Class. Election Night Hackathon Tues 11/5. 52 | 6 b 53 | 12 54 | 11 a Feature Freeze 55 | 13 b User Testing 56 | 13 57 | 18 a ROC.py 11/19 - ROC.py Demo/Testing 58 | 20 b Prof. Ovedotter Alm -- Intro to Natural Language Processing 59 | XX 60 | 28 a Thanksgiving Break 61 | 30 b Thanksgiving Break 62 | 14 63 | 2 a Docs/Release 64 | 4 b Docs/Release 65 | 15 66 | 9 a Docs/Release 67 | 11 b Docs/Release 68 | 16 69 | TBD Final Presentations 70 |
WeekDayTopicAssignedDue
71 | -------------------------------------------------------------------------------- /ofcourse/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | install: pip install ofcourse 3 | script: ofcourse validate 4 | -------------------------------------------------------------------------------- /ofcourse/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/__init__.py -------------------------------------------------------------------------------- /ofcourse/blueprints.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import os 3 | 4 | from flask import Blueprint 5 | 6 | from ofcourse.render import render_template 7 | from ofcourse.util import app_path 8 | 9 | 10 | homework = Blueprint('homework', __name__, 11 | template_folder=app_path('templates')) 12 | lectures = Blueprint('lectures', __name__, 13 | template_folder=app_path('templates')) 14 | quizzes = Blueprint('quizzes', __name__, 15 | template_folder=app_path('templates')) 16 | 17 | 18 | @homework.route('/', defaults={'page': 'index'}) 19 | @homework.route('/') 20 | def display_homework(page): 21 | if page == 'index': 22 | # Old way of generating list (for backwards compatibility) 23 | hws = os.listdir(app_path('static', 'hw')) 24 | hws.extend(os.listdir(app_path('templates', 'hw'))) 25 | hws = [hw for hw in sorted(hws) if not hw == "index.mak"] 26 | # New, cleaner-in-the-template way to do it 27 | hwd = [(hw, os.path.join('/static', 'hw')) 28 | for hw in os.listdir(app_path('static', 'hw'))] 29 | hwd.extend([(os.path.splitext(hw)[0], '/hw') 30 | for hw in os.listdir(app_path('templates', 'hw'))]) 31 | hwd = {os.path.basename(hw): os.path.join(loc, hw) 32 | for hw, loc in hwd 33 | if os.path.splitext(os.path.basename(hw))[0] != "index"} 34 | else: 35 | hws = None 36 | hwd = None 37 | 38 | return render_template(os.path.join("hw", page), 39 | hws=hws, os=os, assignments=hwd) 40 | 41 | 42 | @lectures.route('/', defaults={'page': 'index'}) 43 | @lectures.route('/') 44 | def display_lecture(page): 45 | if page == 'index': 46 | lecture_list = os.listdir(app_path('templates', 'lectures')) 47 | # Old way of generating list (for backwards compatibility) 48 | lectures = [note for note in sorted(lecture_list) 49 | if note != "index.mak"] 50 | # New, cleaner-in-the-template way to do it 51 | lecture_notes = [ 52 | (note, os.path.splitext(os.path.join("/lectures", note))[0]) 53 | for note in sorted(lecture_list) 54 | if os.path.splitext(note)[0] != "index"] 55 | else: 56 | lecture_notes = None 57 | lectures = None 58 | 59 | return render_template(os.path.join('lectures', page), 60 | lecture_notes=lecture_notes, lectures=lectures) 61 | 62 | 63 | @quizzes.route('/') 64 | def show_quiz(quiz_num): 65 | return render_template(os.path.join('quiz', quiz_num)) 66 | -------------------------------------------------------------------------------- /ofcourse/cal.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Calendar management for ofCourse 3 | Authors: Matt Soucy 4 | ''' 5 | 6 | from __future__ import print_function 7 | from itertools import groupby 8 | import re 9 | 10 | from icalendar import Calendar, Event, vText 11 | 12 | 13 | def load_calendar(fn): 14 | ' Load a calendar file and parse it into a Calendar structure ' 15 | with open(fn) as infile: 16 | return Calendar.from_ical(infile.read()) 17 | 18 | 19 | def event_start_time(ev): 20 | ''' 21 | Get the start time of an event 22 | Handle "all-day" events 23 | ''' 24 | from datetime import datetime 25 | return datetime.fromordinal(ev.decoded('dtstart').toordinal()) 26 | 27 | 28 | def sorted_events(cal): 29 | ' Organize the relevant events from the calendar ' 30 | return sorted([ev for ev in cal.subcomponents if type(ev) == Event], 31 | key=event_start_time) 32 | 33 | 34 | def normalize_categories(ev): 35 | ' Always return an array of categories ' 36 | if isinstance(ev['categories'], vText): 37 | return [str(ev['categories']).lower()] 38 | else: 39 | return [str(cat).lower() for cat in ev['categories']] 40 | 41 | 42 | def calendar_weeks(cal): 43 | ' Organize calendar event by week ' 44 | def week_num(ev): 45 | ' Simplest way without lots of string (un-)formatting ' 46 | return ev.decoded('dtstart').isocalendar()[1] 47 | return [list(wk[1]) for wk in groupby(sorted_events(cal), week_num)] 48 | 49 | 50 | assignment_re = re.compile(r"^(ASSIGNED|DUE): (.*)\w*(?:<(.*)>)") 51 | 52 | 53 | def assignment_data(desc): 54 | ' Parse the description for assignments ' 55 | ret = [] 56 | for line in desc.split("\n"): 57 | evdata = assignment_re.match(line) 58 | if evdata: 59 | ret.append(evdata.groups('')) 60 | return ret 61 | 62 | 63 | def items_assigned(items): 64 | ' Only return assignments that are newly assigned ' 65 | return [it for it in items if it[0] == 'ASSIGNED'] 66 | 67 | 68 | def items_due(items): 69 | ' Only return assignments that are due ' 70 | return [it for it in items if it[0] == 'DUE'] 71 | -------------------------------------------------------------------------------- /ofcourse/cli/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Ryan Brown 3 | License: Apache 2.0 4 | """ 5 | 6 | import os 7 | 8 | import yaml 9 | import click 10 | from distutils import dir_util 11 | from distutils import file_util 12 | 13 | from ofcourse.cli.util import year, season 14 | from ofcourse.cli.openshift_utils import (generate_token, 15 | get_api, 16 | get_app, 17 | is_dirty, 18 | new_app, 19 | push, 20 | ) 21 | from ofcourse.version import __version__ 22 | from ofcourse.tests.test_yaml import TestAllYaml 23 | import unittest 24 | 25 | 26 | @click.group() 27 | def cli(): 28 | pass 29 | 30 | 31 | @cli.command() 32 | def version(): 33 | click.echo("You are using ofcourse version {}".format(__version__)) 34 | click.echo("Get more information at " 35 | "https://github.com/ryansb/ofCourse") 36 | 37 | 38 | @cli.command(short_help="Run your course locally, and view it on " 39 | "http://localhost:5000") 40 | def run(): 41 | from ofcourse.site import app 42 | app.run( 43 | debug=True, 44 | threaded=True, 45 | ) 46 | 47 | 48 | @cli.command(short_help="Make a new course site from scratch") 49 | def new(): 50 | # \u2714 is a check mark 51 | # \u2717 is an x 52 | # TODO: include default README with instructions for starting your course 53 | click.echo(u'\u2714 Glorious README') 54 | 55 | source_dir = os.path.split(__file__)[0].replace('cli', '') 56 | 57 | static_dir = os.path.join(os.getcwd(), 'static') 58 | dir_util.copy_tree(source_dir + 'static', static_dir, update=True) 59 | 60 | click.echo(u'\u2714 CSS/Javascript for browser art') 61 | 62 | templates_dir = os.path.join(os.getcwd(), 'templates') 63 | dir_util.copy_tree(os.path.join(source_dir, '..', 'ofcourse-templates'), 64 | templates_dir, update=True) 65 | 66 | click.echo(u'\u2714 Starter Mako templates for great good') 67 | 68 | yamls_dir = os.path.join(source_dir, 'yamls') 69 | 70 | people_dir = os.path.join(os.getcwd(), 'people', year(), season()) 71 | if not os.path.isdir(people_dir): 72 | os.makedirs(people_dir) 73 | 74 | file_util.copy_file(os.path.join(yamls_dir, 'fake_student.yaml'), 75 | people_dir, update=True) 76 | 77 | file_util.copy_file(os.path.join(yamls_dir, 'assignments.yaml'), 78 | os.getcwd(), update=True) 79 | file_util.copy_file(os.path.join(yamls_dir, 'site.yaml'), 80 | os.getcwd(), update=True) 81 | file_util.copy_file(os.path.join(yamls_dir, 'oer.yaml'), 82 | os.getcwd(), update=True) 83 | 84 | file_util.copy_file(os.path.join(source_dir, 'course.ics'), 85 | os.getcwd(), update=True) 86 | file_util.copy_file(os.path.join(source_dir, ".travis.yml"), 87 | os.getcwd(), update=True) 88 | 89 | click.echo(u'\u2714 Starter yaml files for data driven education') 90 | 91 | 92 | @cli.command(short_help="Synchronize templates from ofcourse to your content" + 93 | " repository") 94 | def sync(): 95 | click.confirm('This will add (and potentially overwrite) files in your' + 96 | ' course repository. The changes will not be permanent and' + 97 | ' you can use git to undo anything you would rather not' + 98 | ' have. Would you like to continue?', abort=True) 99 | module_dir = os.path.split(__file__)[0].replace('cli', '') 100 | 101 | templates_src = os.path.join( 102 | module_dir, 103 | '..', 104 | 'ofcourse-templates', 105 | ) 106 | 107 | templates_dest = os.path.join(os.getcwd(), 'templates') 108 | 109 | if not os.path.isdir(templates_dest): 110 | click.echo(u'\u2717 Uhoh, {} is not a directory...') 111 | click.echo(u'If you are trying to start a new course, try' + 112 | u' `ofcourse new`') 113 | click.echo(u'If you are updating an existing course, make' + 114 | u' sure you are in the right directory') 115 | click.exit(1) 116 | 117 | dir_util.copy_tree(templates_src, templates_dest, update=True) 118 | 119 | click.echo(u'\u2714 Your templates have been updated!') 120 | 121 | static_dest = os.path.join(os.getcwd(), 'static') 122 | 123 | dir_util.copy_tree(os.path.join(module_dir, 'static'), 124 | static_dest, update=True) 125 | 126 | click.echo(u'\u2714 Your static content (CSS/JS) has been updated!') 127 | 128 | click.echo(u'Make sure to add the new files to your git repository') 129 | click.echo(u'Tip: To see what\'s new, use `git add -p` to stage changes' + 130 | u' individually, and `git status` to check for new files') 131 | 132 | 133 | @cli.command(short_help="Validates ofcourse website using " 134 | "all of the tests currently built into ofcourse " 135 | "Currently these tests validate YAML files") 136 | @click.pass_context 137 | def validate(ctx): 138 | click.echo("Validating ofCourse Participant YAML Files") 139 | 140 | suite = unittest.TestSuite() 141 | suite.addTest(TestAllYaml("test_recursive_yaml")) 142 | suite.addTest(TestAllYaml("test_student_yaml")) 143 | 144 | runner = unittest.TextTestRunner() 145 | ctx.exit(len(runner.run(suite).failures)) 146 | 147 | 148 | @cli.command(short_help="Push this to openshift. Requires " 149 | "http://openshift.com account. Will check for " 150 | "course.openshift.git_url as well as CLI flag --remote") 151 | @click.option("--verbose", help="Show more info", is_flag=True) 152 | @click.option('--app', help="Openshift app name (e.g. hfoss)") 153 | @click.option('--user', help="Openshift username (usually your email)") 154 | @click.option('--domain', help="Openshift domainname") 155 | def openshift(verbose, app, user, domain): 156 | appname = app 157 | site_yaml = os.path.join(os.getcwd(), 'site.yaml') 158 | 159 | if is_dirty(): 160 | click.confirm( 161 | "You have uncommitted changes. Changes that aren't " 162 | "committed won't be pushed to openshift.\n" 163 | "Do you want to continue?", abort=True 164 | ) 165 | conf = os.path.join(os.getenv("HOME"), ".ofcourse.token") 166 | token = None 167 | try: 168 | with open(conf, 'r') as cfg: 169 | token = cfg.read().strip() 170 | api = get_api(token) 171 | # The idea here is that we test out the token by listing 172 | # apps, and if there's a failure we just fall through to 173 | # asking for uname/pass 174 | api.app_list(domain_name=domain) 175 | except Exception: 176 | if not user: 177 | user = click.prompt("Openshift username") 178 | password = click.prompt("Openshift password", hide_input=True) 179 | token = generate_token(user, password) 180 | with open(conf, 'w') as cfg: 181 | cfg.write(token) 182 | 183 | api = get_api(token) 184 | if not domain: 185 | domain = api.domain_get()[1] 186 | 187 | if (not appname) and os.path.isfile(site_yaml): 188 | with open(site_yaml, 'r') as site: 189 | s = yaml.safe_load(site) 190 | appname = s.get("course", {} 191 | ).get("openshift", {} 192 | ).get("app_name", None) 193 | 194 | try: 195 | get_app(appname, api, domain) 196 | except: 197 | if click.confirm("The app {} could not be found, should I create it" 198 | " automatically?".format(appname)): 199 | new_app(appname, api, domain) 200 | 201 | if verbose: 202 | click.echo("Pushing files to openshift app {}".format(appname)) 203 | 204 | app_url = push(appname, api, domain) 205 | click.echo("Your app is now on Openshift at {}".format(app_url)) 206 | -------------------------------------------------------------------------------- /ofcourse/cli/openshift_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Ryan Brown 3 | License: Apache 2.0 4 | """ 5 | 6 | import logging 7 | import os 8 | import pkg_resources 9 | import re 10 | import six 11 | import socket 12 | import uuid 13 | 14 | import requests 15 | import time 16 | import oshift 17 | from six.moves import StringIO 18 | import dulwich.porcelain as git 19 | 20 | 21 | class NotFound(BaseException): 22 | pass 23 | 24 | 25 | openshift_files = { 26 | "setup.py": { 27 | "contents": """from setuptools import setup 28 | setup(name='thecourse', 29 | version='1.0', 30 | description='courseware on openshift', 31 | author='Dr. Professor', 32 | author_email='dr@professor.com', 33 | url='http://www.python.org/sigs/distutils-sig/', 34 | install_requires=['ofcourse>={version}'], 35 | )""".format(version=pkg_resources.get_distribution('ofcourse').version), 36 | }, 37 | "wsgi.py": { 38 | "contents": """#!/usr/bin/python 39 | # IMPORTANT: Please do not make changes to this file unless you know what 40 | # you're doing. Thank you. 41 | 42 | import os 43 | 44 | virtenv = os.environ['OPENSHIFT_PYTHON_DIR'] + '/virtenv/' 45 | virtualenv = os.path.join(virtenv, 'bin/activate_this.py') 46 | try: 47 | execfile(virtualenv, dict(__file__=virtualenv)) 48 | except IOError: 49 | pass 50 | 51 | import ofcourse.site 52 | 53 | application = ofcourse.site.app""", 54 | }, 55 | } 56 | 57 | 58 | class TempBranch(object): 59 | def __init__(self, name, repo, delete=True): 60 | self.branch = 'refs/heads/{}'.format(name) 61 | self.delete = delete 62 | self.repo = repo 63 | 64 | # save the starting branch so we know where to go back to 65 | self.start = self.repo.refs.read_ref('HEAD').replace('ref: ', '') 66 | 67 | def __enter__(self): 68 | self.repo.refs.add_if_new(self.branch, self.repo.head()) 69 | 70 | self.repo.refs.set_symbolic_ref('HEAD', self.branch) 71 | 72 | def __exit__(self, exc_type, value, tb): 73 | if value is None: 74 | self.repo.refs.set_symbolic_ref('HEAD', self.start) 75 | # lol, only reset --hard is supported 76 | if self.delete: 77 | self.repo.refs.remove_if_equals(self.branch, None) 78 | else: 79 | six.reraise(exc_type, value, tb) 80 | 81 | git.reset(self.repo, "hard") 82 | 83 | 84 | def push(name, api, domain): 85 | repo = git.Repo(os.getcwd()) 86 | branch = "temp-{}".format(str(uuid.uuid4())[:8]) 87 | set_deploy_branch(name, branch, api, domain) 88 | 89 | remote = git_url(name, api, domain) 90 | 91 | if is_dirty(): 92 | print("Nuking changes.") 93 | git.reset(repo, "hard") 94 | 95 | with TempBranch(branch, repo, delete=True): 96 | for fname, file_info in openshift_files.items(): 97 | with open(fname, 'w') as f: 98 | f.write(file_info.get("contents", "")) 99 | repo.stage(fname) 100 | repo.do_commit("Commit openshift files") 101 | push_out = StringIO() 102 | push_err = StringIO() 103 | print("Pushing to openshift (may take a few minutes)") 104 | git.push(repo, remote, "refs/heads/{}".format(branch), 105 | outstream=push_out, errstream=push_err) 106 | 107 | push_out.seek(0) 108 | out = push_out.read() 109 | if not re.match(r'^Push to .* successful.', out): 110 | print("There was a failure while pushing") 111 | print("---BEGIN STDERR---") 112 | push_err.seek(0) 113 | print(push_err.read()) 114 | print("---BEGIN STDOUT---") 115 | print(out) 116 | print("There was a failure while pushing") 117 | git.rm(repo, openshift_files.keys()) 118 | map(os.remove, openshift_files.keys()) 119 | 120 | return get_app(name, api, domain)['app_url'] 121 | 122 | 123 | def is_clean(): 124 | return not is_dirty() 125 | 126 | 127 | def is_dirty(): 128 | """Check for uncommitted changes. True if dirty.""" 129 | repo = git.Repo(os.getcwd()) 130 | s = git.status(repo) 131 | return any(s.staged.values() + [s.unstaged]) 132 | 133 | 134 | def get_api(token): 135 | oshift.log.setLevel(logging.FATAL) 136 | return oshift.Openshift("openshift.redhat.com", token=token) 137 | 138 | 139 | def generate_token(uname, passwd): 140 | session = requests.post( 141 | "https://openshift.redhat.com/broker/rest/user/authorizations", 142 | auth=requests.auth.HTTPBasicAuth(uname, passwd), 143 | params={ 144 | 'scope': 'session', 145 | 'note': 'ofCourse CLI auth token', 146 | }, 147 | headers={'Accept': 'application/json'}, 148 | ) 149 | if session.status_code != 201: 150 | raise Exception("Uhoh {} response={}".format(session.status_code, 151 | session.text)) 152 | return session.json().get("data", {}).get("token", "") 153 | 154 | 155 | def new_app(name, api, domain, wait_until_available=True): 156 | try: 157 | get_app(name, api, domain) 158 | return 159 | except: 160 | pass 161 | # Ok, the app doesn't exist 162 | api.app_create(name, ['python-2.7'], domain_name=domain) 163 | if not wait_until_available: 164 | return 165 | while True: 166 | try: 167 | app = get_app(name, api, domain) 168 | socket.getaddrinfo(requests.utils.urlparse( 169 | app['app_url']).netloc, 80) 170 | break 171 | except NotFound: 172 | print("Waiting for new app...") 173 | time.sleep(5) 174 | except socket.gaierror as e: 175 | if e.errno != -2: 176 | raise e 177 | print("Waiting for new app...") 178 | time.sleep(5) 179 | 180 | 181 | def get_app(name, api, domain): 182 | apps = [a for a in api.app_list(domain_name=domain)[1] 183 | if a.get("name", "") == name] 184 | if apps: 185 | return apps[0] 186 | raise NotFound("Could not find app {}".format(name)) 187 | 188 | 189 | def git_url(name, api, domain): 190 | app = get_app(name, api, domain) 191 | remote = app['git_url'] 192 | # change SSH URL 193 | # from "ssh://user@host/dir/repo.git" 194 | # to "user@host:dir/repo.git" 195 | return remote.replace("ssh://", "").replace("/", ":", 1) 196 | 197 | 198 | def set_deploy_branch(name, branch, api, domain): 199 | app = get_app(name, api, domain) 200 | if app['deployment_branch'] != branch: 201 | api.app_action('UPDATE', name, domain_name=domain, 202 | deployment_branch=branch) 203 | -------------------------------------------------------------------------------- /ofcourse/cli/util.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | def season(): 5 | if datetime.date.today().month > 6: 6 | return "fall" 7 | return "spring" 8 | 9 | 10 | def year(): 11 | return str(datetime.date.today().year) 12 | -------------------------------------------------------------------------------- /ofcourse/course.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN 3 | VERSION:2.0 4 | BEGIN:VTIMEZONE 5 | TZID:America/New_York 6 | BEGIN:DAYLIGHT 7 | TZOFFSETFROM:-0500 8 | TZOFFSETTO:-0400 9 | TZNAME:EDT 10 | DTSTART:19700308T020000 11 | RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 12 | END:DAYLIGHT 13 | BEGIN:STANDARD 14 | TZOFFSETFROM:-0400 15 | TZOFFSETTO:-0500 16 | TZNAME:EST 17 | DTSTART:19701101T020000 18 | RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 19 | END:STANDARD 20 | END:VTIMEZONE 21 | BEGIN:VEVENT 22 | CREATED:20150930T205515Z 23 | LAST-MODIFIED:20150930T205705Z 24 | DTSTAMP:20150930T205705Z 25 | UID:c6f3f329-fb18-4617-8cd8-01bba4ccc235 26 | SUMMARY:Commarch clean-up\; TBD 27 | CATEGORIES:Class 28 | DTSTART;TZID=America/New_York:20150929T183000 29 | DTEND;TZID=America/New_York:20150929T194500 30 | TRANSP:OPAQUE 31 | DESCRIPTION:DUE: Homework - Team Proposal 33 | END:VEVENT 34 | BEGIN:VEVENT 35 | CREATED:20151001T012736Z 36 | LAST-MODIFIED:20151001T012821Z 37 | DTSTAMP:20151001T012821Z 38 | UID:7b31d51e-f5f6-4e15-9240-c87fb39a0b13 39 | SUMMARY:Welcome\, intro\, Syllabus\, RTFM 40 | CATEGORIES:Class 41 | DTSTART;TZID=America/New_York:20150825T183000 42 | DTEND;TZID=America/New_York:20150825T194500 43 | TRANSP:OPAQUE 44 | DESCRIPTION:ASSIGNED: litreview1: What is Open Source? by Steve Weber 47 | END:VEVENT 48 | BEGIN:VEVENT 49 | CREATED:20151001T012824Z 50 | LAST-MODIFIED:20151001T012904Z 51 | DTSTAMP:20151001T012904Z 52 | UID:556b4516-6d6d-4b10-97eb-afc5a3e34e51 53 | SUMMARY:First Flight 54 | ATTACH:http://hfoss-ritigm.rhcloud.com/lectures/w01c2 55 | CATEGORIES:Class 56 | DTSTART;TZID=America/New_York:20150827T183000 57 | DTEND;TZID=America/New_York:20150827T194500 58 | TRANSP:OPAQUE 59 | DESCRIPTION:ASSIGNED: Homework - First Flight 61 | END:VEVENT 62 | BEGIN:VEVENT 63 | CREATED:20151001T013224Z 64 | LAST-MODIFIED:20151001T013358Z 65 | DTSTAMP:20151001T013358Z 66 | UID:dc2e0c94-04bf-4a19-bb4d-6cd589298a69 67 | SUMMARY:5 Pillars of Opensource.com\, $7 Unix history\, In the Beginning.. 68 | .\, Ethics of Unpaid Labor 69 | CATEGORIES:Class 70 | DTSTART;TZID=America/New_York:20150910T183000 71 | DTEND;TZID=America/New_York:20150910T194500 72 | TRANSP:OPAQUE 73 | DESCRIPTION:ASSIGNED: Homework - Bugfix 75 | X-MOZ-GENERATION:1 76 | END:VEVENT 77 | BEGIN:VEVENT 78 | CREATED:20151001T013135Z 79 | LAST-MODIFIED:20151001T013404Z 80 | DTSTAMP:20151001T013404Z 81 | UID:e87f06af-6b4a-4c12-b15b-4c2c7e149b7f 82 | SUMMARY:litreview Discussion: What is Open Source? 83 | CATEGORIES:Class 84 | DTSTART;TZID=America/New_York:20150908T183000 85 | DTEND;TZID=America/New_York:20150908T194500 86 | TRANSP:OPAQUE 87 | DESCRIPTION:DUE: litreview1: What is Open Source? by Steve Weber 89 | 90 | SEQUENCE:1 91 | X-MOZ-GENERATION:2 92 | END:VEVENT 93 | BEGIN:VEVENT 94 | CREATED:20151001T012954Z 95 | LAST-MODIFIED:20151001T013409Z 96 | DTSTAMP:20151001T013409Z 97 | UID:518e6bba-2ba2-450a-926a-57e74be11a0b 98 | SUMMARY:IP and Licensing\; Writing for HFOSS - litreviews and blog posts 99 | CATEGORIES:Class 100 | DTSTART;TZID=America/New_York:20150903T183000 101 | DTEND;TZID=America/New_York:20150903T194500 102 | TRANSP:OPAQUE 103 | DESCRIPTION:ASSIGNED: grok litreview.txt \nDUE: grok litreview.txt \nDUE: Homework - First Flight 107 | X-MOZ-GENERATION:1 108 | END:VEVENT 109 | BEGIN:VEVENT 110 | CREATED:20151001T012915Z 111 | LAST-MODIFIED:20151001T013412Z 112 | DTSTAMP:20151001T013412Z 113 | UID:6fed9095-66d9-4487-9c72-62a7beefeac3 114 | SUMMARY:Formal Introduction to Git and Github 115 | ATTACH:http://hfoss-ritigm.rhcloud.com/static/decks/g-b4-gh-txt.odp 116 | CATEGORIES:Class 117 | DTSTART;TZID=America/New_York:20150901T183000 118 | DTEND;TZID=America/New_York:20150901T194500 119 | TRANSP:OPAQUE 120 | X-MOZ-GENERATION:1 121 | END:VEVENT 122 | BEGIN:VEVENT 123 | CREATED:20151001T013320Z 124 | LAST-MODIFIED:20151001T013437Z 125 | DTSTAMP:20151001T013437Z 126 | UID:a49ad899-57ed-4d9e-ab5d-f16eaa759240 127 | SUMMARY:litreview Discussion: Ethics of Unpaid Labor & the OSS Community 128 | CATEGORIES:Class 129 | DTSTART;TZID=America/New_York:20150915T183000 130 | DTEND;TZID=America/New_York:20150915T194500 131 | TRANSP:OPAQUE 132 | DESCRIPTION:DUE: litreview2: Ethics of Unpaid Labor & the OSS Community by 133 | Ashe Dryden 135 | X-MOZ-GENERATION:2 136 | END:VEVENT 137 | BEGIN:VEVENT 138 | CREATED:20151001T013442Z 139 | LAST-MODIFIED:20151001T013545Z 140 | DTSTAMP:20151001T013545Z 141 | UID:3e768a6c-7ebd-4002-a895-4047cefdf9a1 142 | SUMMARY:Quiz\; OLPC Distribution & Smoke testing 143 | CATEGORIES:Class 144 | DTSTART;TZID=America/New_York:20150917T183000 145 | DTEND;TZID=America/New_York:20150917T194500 146 | TRANSP:OPAQUE 147 | DESCRIPTION:ASSIGNED: Smoke Test \nDUE: Homework - Bugfix 150 | X-MOZ-GENERATION:1 151 | END:VEVENT 152 | BEGIN:VEVENT 153 | CREATED:20151001T013553Z 154 | LAST-MODIFIED:20151001T013642Z 155 | DTSTAMP:20151001T013642Z 156 | UID:3a5951bf-ce04-4d77-bb21-4da4cca9b35b 157 | SUMMARY:Software Freedom Day 158 | ATTACH:http://www.softwarefreedomday.org/ 159 | CATEGORIES:Hackathon 160 | DTSTART;TZID=America/New_York:20150919T120000 161 | DTEND;TZID=America/New_York:20150920T130000 162 | TRANSP:OPAQUE 163 | LOCATION:Student Innovation Center\, RIT\, Rochester\, NY 164 | END:VEVENT 165 | BEGIN:VEVENT 166 | CREATED:20151001T013657Z 167 | LAST-MODIFIED:20151001T013805Z 168 | DTSTAMP:20151001T013805Z 169 | UID:cf632677-fe17-43bd-a549-702e5d6ae916 170 | SUMMARY:Quiz comments\; COMMARCH: Git-by-a-bus and CCF: Callaway Coefficie 171 | nt of Fail. 172 | CATEGORIES:Class 173 | DTSTART;TZID=America/New_York:20150922T183000 174 | DTEND;TZID=America/New_York:20150922T194500 175 | TRANSP:OPAQUE 176 | DESCRIPTION:ASSIGNED: Homework - Team Proposal \nASSIGNED: Commarch Report 179 | END:VEVENT 180 | BEGIN:VEVENT 181 | CREATED:20151001T013811Z 182 | LAST-MODIFIED:20151001T013846Z 183 | DTSTAMP:20151001T013846Z 184 | UID:72bad1de-9192-4784-bfca-b30651e63311 185 | SUMMARY:COMMARCH: Reporting and analysis using GBAB et al. 186 | CATEGORIES:Class 187 | DTSTART;TZID=America/New_York:20150924T183000 188 | DTEND;TZID=America/New_York:20150924T194500 189 | TRANSP:OPAQUE 190 | DESCRIPTION:DUE: Smoke Test 192 | SEQUENCE:1 193 | X-MOZ-GENERATION:1 194 | END:VEVENT 195 | BEGIN:VEVENT 196 | CREATED:20150930T210513Z 197 | LAST-MODIFIED:20151001T013945Z 198 | DTSTAMP:20151001T013945Z 199 | UID:35e5fe40-1943-45b0-8f96-f0d28743a798 200 | SUMMARY:Local Hack Day 201 | ATTACH:http://localhackday.mlh.io/ 202 | CATEGORIES:Hackathon 203 | DTSTART;TZID=America/New_York:20151010T090000 204 | DTEND;TZID=America/New_York:20151010T210000 205 | TRANSP:OPAQUE 206 | LOCATION:MAGIC Center\, RIT\, Rochester\, NY 207 | SEQUENCE:1 208 | X-MOZ-GENERATION:1 209 | END:VEVENT 210 | BEGIN:VEVENT 211 | CREATED:20151001T014152Z 212 | LAST-MODIFIED:20151001T014241Z 213 | DTSTAMP:20151001T014241Z 214 | UID:1fbbb255-86a0-4b2e-af6e-4e22c29f88d7 215 | SUMMARY:RocPy 216 | ATTACH:http://www.meetup.com/Rochester-Python-Meetup/ 217 | CATEGORIES:Special 218 | DTSTART;TZID=America/New_York:20151020T190000 219 | DTEND;TZID=America/New_York:20151020T200000 220 | TRANSP:OPAQUE 221 | X-MOZ-GENERATION:3 222 | END:VEVENT 223 | BEGIN:VEVENT 224 | CREATED:20151001T014101Z 225 | LAST-MODIFIED:20151001T014249Z 226 | DTSTAMP:20151001T014249Z 227 | UID:91fc407a-f3aa-49e8-93fb-f54b617688e5 228 | SUMMARY:EDU: Curriculum Discussion 229 | CATEGORIES:Class 230 | DTSTART;TZID=America/New_York:20151015T183000 231 | DTEND;TZID=America/New_York:20151015T194500 232 | TRANSP:OPAQUE 233 | DESCRIPTION:DUE: NY & MA 4th Grade Curriculum 235 | X-MOZ-GENERATION:2 236 | END:VEVENT 237 | BEGIN:VEVENT 238 | CREATED:20151001T014022Z 239 | LAST-MODIFIED:20151001T014253Z 240 | DTSTAMP:20151001T014253Z 241 | UID:2b630978-adad-4581-8fd0-cb94fdc705fe 242 | SUMMARY:NO CLASS (Monday Schedule) 243 | CATEGORIES:Cancelled 244 | DTSTART;TZID=America/New_York:20151013T183000 245 | DTEND;TZID=America/New_York:20151013T194500 246 | TRANSP:OPAQUE 247 | X-MOZ-GENERATION:3 248 | SEQUENCE:1 249 | END:VEVENT 250 | BEGIN:VEVENT 251 | CREATED:20151001T014309Z 252 | LAST-MODIFIED:20151001T014328Z 253 | DTSTAMP:20151001T014328Z 254 | UID:997521e9-ecf1-4dde-b5ae-096317f3889d 255 | SUMMARY:TBD 256 | CATEGORIES:Class 257 | DTSTART;TZID=America/New_York:20151022T183000 258 | DTEND;TZID=America/New_York:20151022T194500 259 | TRANSP:OPAQUE 260 | END:VEVENT 261 | BEGIN:VEVENT 262 | CREATED:20151001T014400Z 263 | LAST-MODIFIED:20151001T014451Z 264 | DTSTAMP:20151001T014451Z 265 | UID:405f54e7-f6c2-4a4d-b855-f14f4627fe24 266 | SUMMARY:Hack Upstate 267 | ATTACH:http://hackupstate.com/ 268 | CATEGORIES:Hackathon 269 | DTSTART;TZID=America/New_York:20151003T110000 270 | DTEND;TZID=America/New_York:20151004T143000 271 | TRANSP:OPAQUE 272 | LOCATION:Tech Garden\, Syracuse\, NY 273 | END:VEVENT 274 | BEGIN:VEVENT 275 | CREATED:20150930T205932Z 276 | LAST-MODIFIED:20151001T014526Z 277 | DTSTAMP:20151001T014526Z 278 | UID:146674e2-ecf8-4c3e-8f4b-7ff008d5a4f9 279 | SUMMARY:Interlock Rochester 280 | CATEGORIES:Meetup 281 | DTSTART;TZID=America/New_York:20151103T183000 282 | DTEND;TZID=America/New_York:20151103T193000 283 | TRANSP:OPAQUE 284 | X-MOZ-GENERATION:1 285 | END:VEVENT 286 | BEGIN:VEVENT 287 | CREATED:20151001T014538Z 288 | LAST-MODIFIED:20151001T014607Z 289 | DTSTAMP:20151001T014607Z 290 | UID:cefa5d38-2b87-4ce5-aa6a-d805caf2fbb4 291 | SUMMARY:Election Night Hackathon 292 | CATEGORIES:Hackathon 293 | DTSTART;TZID=America/New_York:20151103T200000 294 | DTEND;TZID=America/New_York:20151103T220000 295 | TRANSP:OPAQUE 296 | LOCATION:Student Innovation Center\, RIT\, Rochester\, NY 297 | END:VEVENT 298 | BEGIN:VEVENT 299 | CREATED:20151001T014309Z 300 | LAST-MODIFIED:20151001T014624Z 301 | DTSTAMP:20151001T014624Z 302 | UID:e377a03c-a614-4e2d-bad8-6b23be61897e 303 | SUMMARY:TBD 304 | CATEGORIES:Class 305 | DTSTART;TZID=America/New_York:20151112T183000 306 | DTEND;TZID=America/New_York:20151112T194500 307 | TRANSP:OPAQUE 308 | END:VEVENT 309 | BEGIN:VEVENT 310 | CREATED:20151001T014152Z 311 | LAST-MODIFIED:20151001T014653Z 312 | DTSTAMP:20151001T014653Z 313 | UID:5d748bd9-0192-42d3-9706-fa6e35fe2491 314 | SUMMARY:RocPy 315 | ATTACH:http://www.meetup.com/Rochester-Python-Meetup/ 316 | CATEGORIES:Special 317 | DTSTART;TZID=America/New_York:20151117T190000 318 | DTEND;TZID=America/New_York:20151117T200000 319 | TRANSP:OPAQUE 320 | X-MOZ-GENERATION:3 321 | END:VEVENT 322 | BEGIN:VEVENT 323 | CREATED:20151001T014309Z 324 | LAST-MODIFIED:20151001T014701Z 325 | DTSTAMP:20151001T014701Z 326 | UID:1bbaf684-e735-4c63-bc8b-de52dfbe732d 327 | SUMMARY:TBD 328 | CATEGORIES:Class 329 | DTSTART;TZID=America/New_York:20151119T183000 330 | DTEND;TZID=America/New_York:20151119T194500 331 | TRANSP:OPAQUE 332 | END:VEVENT 333 | BEGIN:VEVENT 334 | CREATED:20151001T014718Z 335 | LAST-MODIFIED:20151001T014808Z 336 | DTSTAMP:20151001T014808Z 337 | UID:85ae2208-ec05-49a0-856b-deaa10e43e7f 338 | SUMMARY:Rochester Mini Maker Faire 339 | ATTACH:http://makerfairerochester.com/ 340 | CATEGORIES:Hackathon 341 | DTSTART;TZID=America/New_York:20151121T090000 342 | DTEND;TZID=America/New_York:20151121T170000 343 | TRANSP:OPAQUE 344 | X-MOZ-GENERATION:4 345 | SEQUENCE:2 346 | LOCATION:Rochester Riverside Convention Center\, Rochester\, NY 347 | END:VEVENT 348 | BEGIN:VEVENT 349 | CREATED:20151001T014309Z 350 | LAST-MODIFIED:20151001T014828Z 351 | DTSTAMP:20151001T014828Z 352 | UID:7e2436d6-b2bd-4f58-9691-75fd15ae4632 353 | SUMMARY:IRC Class 354 | CATEGORIES:Class 355 | CATEGORIES:IRC 356 | CATEGORIES:Special 357 | DTSTART;TZID=America/New_York:20151124T183000 358 | DTEND;TZID=America/New_York:20151124T194500 359 | TRANSP:OPAQUE 360 | X-MOZ-GENERATION:1 361 | END:VEVENT 362 | BEGIN:VEVENT 363 | CREATED:20151001T014831Z 364 | LAST-MODIFIED:20151001T014851Z 365 | DTSTAMP:20151001T014851Z 366 | UID:0ef32e0f-b09d-4712-82ee-be4cec1f048b 367 | SUMMARY:NO CLASS (Thanksgiving) 368 | CATEGORIES:Cancelled 369 | DTSTART;TZID=America/New_York:20151126T183000 370 | DTEND;TZID=America/New_York:20151126T194500 371 | TRANSP:OPAQUE 372 | END:VEVENT 373 | BEGIN:VEVENT 374 | CREATED:20150930T205932Z 375 | LAST-MODIFIED:20151001T014900Z 376 | DTSTAMP:20151001T014900Z 377 | UID:2d794975-b8a2-4307-930d-ed63d5f86a8d 378 | SUMMARY:Interlock Rochester 379 | CATEGORIES:Meetup 380 | DTSTART;TZID=America/New_York:20151201T183000 381 | DTEND;TZID=America/New_York:20151201T193000 382 | TRANSP:OPAQUE 383 | X-MOZ-GENERATION:1 384 | END:VEVENT 385 | BEGIN:VEVENT 386 | CREATED:20151001T014309Z 387 | LAST-MODIFIED:20151001T014907Z 388 | DTSTAMP:20151001T014907Z 389 | UID:0137d346-8af2-4a52-a239-c99036f52120 390 | SUMMARY:TBD 391 | CATEGORIES:Class 392 | DTSTART;TZID=America/New_York:20151203T183000 393 | DTEND;TZID=America/New_York:20151203T194500 394 | TRANSP:OPAQUE 395 | END:VEVENT 396 | BEGIN:VEVENT 397 | CREATED:20151001T014309Z 398 | LAST-MODIFIED:20151001T014912Z 399 | DTSTAMP:20151001T014912Z 400 | UID:8fb84606-c9d0-4358-8877-48d1f8eccb0b 401 | SUMMARY:TBD 402 | CATEGORIES:Class 403 | DTSTART;TZID=America/New_York:20151208T183000 404 | DTEND;TZID=America/New_York:20151208T194500 405 | TRANSP:OPAQUE 406 | END:VEVENT 407 | BEGIN:VEVENT 408 | CREATED:20151001T014309Z 409 | LAST-MODIFIED:20151001T014923Z 410 | DTSTAMP:20151001T014923Z 411 | UID:ad0468d1-5b3e-45ca-ac2f-28bbceef1eb0 412 | SUMMARY:Last Class 413 | CATEGORIES:Class 414 | CATEGORIES:Special 415 | DTSTART;TZID=America/New_York:20151210T183000 416 | DTEND;TZID=America/New_York:20151210T194500 417 | TRANSP:OPAQUE 418 | X-MOZ-GENERATION:1 419 | END:VEVENT 420 | BEGIN:VEVENT 421 | CREATED:20151001T014309Z 422 | LAST-MODIFIED:20151110T235108Z 423 | DTSTAMP:20151110T235108Z 424 | UID:80d31efb-fad7-4511-b96a-a7b4b4269389 425 | SUMMARY:Quiz 2 426 | CATEGORIES:Class 427 | DTSTART;TZID=America/New_York:20151029T183000 428 | DTEND;TZID=America/New_York:20151029T194500 429 | TRANSP:OPAQUE 430 | DESCRIPTION:ASSIGNED: Team Proposal 2 431 | X-MOZ-GENERATION:1 432 | END:VEVENT 433 | BEGIN:VEVENT 434 | CREATED:20150930T210148Z 435 | LAST-MODIFIED:20151110T235147Z 436 | DTSTAMP:20151110T235147Z 437 | UID:09fcbb5d-0ced-4d43-99bb-c9c7d53824b7 438 | SUMMARY:Commarch Reports 439 | CATEGORIES:Class 440 | CATEGORIES:Guest 441 | DTSTART;TZID=America/New_York:20151008T183000 442 | DTEND;TZID=America/New_York:20151008T193000 443 | TRANSP:OPAQUE 444 | DESCRIPTION:ASSIGNED: NY & MA 4th Grade Curriculum 446 | X-MOZ-GENERATION:2 447 | END:VEVENT 448 | BEGIN:VEVENT 449 | CREATED:20150930T205932Z 450 | LAST-MODIFIED:20151110T235237Z 451 | DTSTAMP:20151110T235237Z 452 | UID:7ad9a885-cc1e-47b7-a309-1bf4aaa76c32 453 | SUMMARY:Interlock Rochester 454 | CATEGORIES:Meetup 455 | DTSTART;TZID=America/New_York:20151006T183000 456 | DTEND;TZID=America/New_York:20151006T193000 457 | TRANSP:OPAQUE 458 | X-MOZ-GENERATION:3 459 | DESCRIPTION:ASSIGNED: Watch FSF 30th Anniversary Video \nDUE: Commarch Report 462 | END:VEVENT 463 | BEGIN:VEVENT 464 | CREATED:20150930T205808Z 465 | LAST-MODIFIED:20151110T235254Z 466 | DTSTAMP:20151110T235254Z 467 | UID:dn9bjqtgun6t8d8dmdmphucvn8@google.com 468 | STATUS:CONFIRMED 469 | ORGANIZER;CN=Matt Soucy:mailto:msoucy@csh.rit.edu 470 | CATEGORIES:Class 471 | DTSTART;TZID=America/New_York:20151001T183000 472 | DTEND;TZID=America/New_York:20151001T194500 473 | URL:https://www.google.com/calendar/event?eid=ZG45YmpxdGd1bjZ0OGQ4ZG1kbXBo 474 | dWN2bjggZ2NhNGVhOTR2bTZiazVwNnBiOWFxdTNhbG9AZw 475 | SEQUENCE:0 476 | TRANSP:OPAQUE 477 | X-MOZ-GENERATION:1 478 | END:VEVENT 479 | BEGIN:VEVENT 480 | CREATED:20151001T014309Z 481 | LAST-MODIFIED:20151110T235309Z 482 | DTSTAMP:20151110T235309Z 483 | UID:82685f41-f400-4dae-aa94-c01d8e3fc5c9 484 | SUMMARY:Creative Commons & Free Culture 485 | CATEGORIES:Class 486 | DTSTART;TZID=America/New_York:20151027T183000 487 | DTEND;TZID=America/New_York:20151027T194500 488 | TRANSP:OPAQUE 489 | DESCRIPTION:DUE: Team formation 490 | X-MOZ-GENERATION:2 491 | END:VEVENT 492 | BEGIN:VEVENT 493 | CREATED:20151110T235331Z 494 | LAST-MODIFIED:20151110T235409Z 495 | DTSTAMP:20151110T235409Z 496 | UID:8ee26bf0-ab7b-4d44-82ca-20c5d7de7bfc 497 | SUMMARY:RITLug - Filesystems 498 | ATTACH:http://ritlug.github.io/ 499 | CATEGORIES:Meetup 500 | DTSTART;TZID=America/New_York:20151030T160000 501 | DTEND;TZID=America/New_York:20151030T180000 502 | TRANSP:OPAQUE 503 | END:VEVENT 504 | BEGIN:VEVENT 505 | CREATED:20151110T235509Z 506 | LAST-MODIFIED:20151110T235546Z 507 | DTSTAMP:20151110T235546Z 508 | UID:ac209ec9-c9db-4de7-9361-1b4593a87955 509 | SUMMARY:Free Software & Open Source Symposium 510 | ATTACH:http://fsoss.senecac.on.ca/2015/about/overview 511 | CATEGORIES:Meetup 512 | DTSTART;VALUE=DATE:20151029 513 | DTEND;VALUE=DATE:20151031 514 | TRANSP:TRANSPARENT 515 | LOCATION:Seneca@York\, Toronto 516 | END:VEVENT 517 | BEGIN:VEVENT 518 | CREATED:20151001T014309Z 519 | LAST-MODIFIED:20151110T235611Z 520 | DTSTAMP:20151110T235611Z 521 | UID:95a4c202-11f7-4d55-ae16-6c2767282472 522 | SUMMARY:TBD 523 | CATEGORIES:Class 524 | DTSTART;TZID=America/New_York:20151105T183000 525 | DTEND;TZID=America/New_York:20151105T194500 526 | TRANSP:OPAQUE 527 | X-MOZ-GENERATION:3 528 | DESCRIPTION:DUE: Team Proposal 2 529 | END:VEVENT 530 | BEGIN:VEVENT 531 | CREATED:20151001T014309Z 532 | LAST-MODIFIED:20151110T235632Z 533 | DTSTAMP:20151110T235632Z 534 | UID:d1c236e6-1c37-4807-97c3-898cca6ad9fb 535 | SUMMARY:TBD 536 | CATEGORIES:Class 537 | CATEGORIES:IRC 538 | CATEGORIES:Special 539 | DTSTART;TZID=America/New_York:20151110T183000 540 | DTEND;TZID=America/New_York:20151110T194500 541 | TRANSP:OPAQUE 542 | X-MOZ-GENERATION:2 543 | END:VEVENT 544 | END:VCALENDAR 545 | -------------------------------------------------------------------------------- /ofcourse/participants.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from datetime import datetime, date, timedelta 4 | from six.moves.urllib.parse import urlparse 5 | import yaml 6 | 7 | from flask import Blueprint, redirect 8 | 9 | import ofcourse 10 | from .util import app_path, get_hw_keys 11 | from .render import render_template 12 | 13 | participants_bp = Blueprint('participants_bp', 14 | __name__, 15 | template_folder=app_path('templates')) 16 | 17 | 18 | currentYear = str(date.today().year) 19 | currentTerm = "fall" if date.today().month > 7 else "spring" 20 | 21 | 22 | @participants_bp.route('/') 23 | def participants_blank(): 24 | """ 25 | This is the default landing 26 | for the participants listing page. 27 | It will list all of the participants 28 | in the current term for HFOSS 29 | """ 30 | return participants_year_term(currentYear, currentTerm) 31 | 32 | 33 | @participants_bp.route('/') 34 | def participants_year(year_or_nick): 35 | """ 36 | This will get all the participants 37 | within a given year 38 | """ 39 | p_url = find_participant(year_or_nick) 40 | if p_url is not None: 41 | # render individual page 42 | return redirect(p_url) 43 | # otherwise render as a year 44 | return participants(year_or_nick + '/') 45 | 46 | 47 | @participants_bp.route('//') 48 | def participants_year_term(year, term): 49 | """ 50 | This will get all the participants 51 | within a given year and term 52 | """ 53 | return participants(year + '/' + term + '/') 54 | 55 | 56 | @participants_bp.route('/all') 57 | def participants_all(): 58 | return participants('') 59 | """ 60 | This will get all the participants 61 | who have taken HFOSS 62 | """ 63 | 64 | 65 | def participants(root_dir): 66 | """ 67 | Render the participants page, 68 | which shows a directory of all 69 | the students with their forge 70 | links, blog posts, assignment 71 | links, and etc. 72 | 73 | """ 74 | 75 | yaml_dir = app_path('people', root_dir) 76 | 77 | student_data = [] 78 | for dirpath, dirnames, files in os.walk(yaml_dir): 79 | dirpath = dirpath.rstrip("/") 80 | for fname in sorted(files): 81 | if fname.endswith('.yaml'): 82 | with open(dirpath + '/' + fname) as students: 83 | contents = yaml.safe_load(students) 84 | contents['yaml'] = dirpath + '/' + fname 85 | year_term_data = dirpath.split('/') 86 | contents['participant_page'] = "{y}/{t}/{u}".format( 87 | y=year_term_data[-2], 88 | t=year_term_data[-1], 89 | u=os.path.splitext(fname)[0] 90 | ) 91 | 92 | for forge in contents['forges']: 93 | url = urlparse(forge) 94 | if "github.com" in url.netloc: 95 | contents['github'] = url.path[1:] 96 | 97 | contents['isActive'] = (currentYear in year_term_data and 98 | currentTerm in year_term_data) 99 | 100 | student_data.append(contents) 101 | 102 | assignments = get_hw_keys() 103 | elapsed = (datetime.today() - ofcourse.site.COURSE_START).total_seconds() 104 | target_number = int(elapsed / timedelta(weeks=1).total_seconds() + 1 + 105 | len(assignments)) 106 | 107 | return render_template( 108 | 'blogs', 109 | student_data=student_data, 110 | gravatar=ofcourse.site.gravatar, 111 | target_number=target_number, 112 | hw_keys=assignments 113 | ) 114 | 115 | 116 | def find_participant(nick): 117 | yaml_dir = app_path('people') 118 | 119 | for dirpath, dirnames, files in os.walk(yaml_dir): 120 | for fname in files: 121 | if (fname.lower().startswith(nick.lower()) and 122 | fname.endswith('.yaml')): 123 | participant = os.path.join( 124 | dirpath, 125 | fname 126 | ).replace(yaml_dir, '') 127 | participant = participant.replace('.yaml', '') 128 | return 'participants' + participant 129 | -------------------------------------------------------------------------------- /ofcourse/render.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Matt Soucy 3 | Remy D 4 | Ralph Bean 5 | Sam Lucidi 6 | License: Apache 2.0 7 | 8 | """ 9 | 10 | from functools import partial 11 | 12 | # flask dependencies 13 | import flask 14 | 15 | 16 | # Renderers {{{ 17 | # Renderer template {{{ 18 | def make_renderer(render, suffixes, exception, 19 | init=None, config={}, params={}): 20 | class Renderer(object): 21 | def __init__(self, app): 22 | ''' Perform required setup ''' 23 | if init: 24 | init(app) 25 | app.config.update(config) 26 | 27 | def __call__(self, template, **kwargs_raw): 28 | ''' Attempt to render ''' 29 | kwargs = kwargs_raw.copy() 30 | kwargs.update(params) 31 | for suffix in suffixes: 32 | try: 33 | return render(template + suffix, **kwargs) 34 | except exception: 35 | pass 36 | return Renderer 37 | # }}} 38 | 39 | _renderer_classes = [] 40 | 41 | # Jinja2 files (.html) {{{ 42 | try: 43 | import jinja2 44 | _renderer_classes.append(make_renderer( 45 | flask.render_template, (".html", ".htm"), jinja2.TemplateNotFound)) 46 | except ImportError: 47 | # Somehow, jinja isn't supported 48 | pass 49 | # }}} 50 | 51 | # Mako files (.mak) {{{ 52 | try: 53 | import flask.ext.mako as mako 54 | from mako.exceptions import TopLevelLookupException 55 | _renderer_classes.append(make_renderer( 56 | mako.render_template, (".mak", ".mako"), TopLevelLookupException, 57 | init=mako.MakoTemplates, 58 | params={'name': 'mako'}, 59 | config={'MAKO_TRANSLATE_EXCEPTIONS': False})) 60 | except ImportError: 61 | # Mako extensions don't exist 62 | pass 63 | # }}} 64 | # }}} 65 | 66 | # Filters {{{ 67 | _filters = [] 68 | 69 | # Markdown {{{ 70 | try: 71 | import markdown 72 | _filters.append( 73 | ("markdown", partial(markdown.markdown, extensions=("extra",)))) 74 | except: 75 | pass 76 | # }}} 77 | 78 | # reStructuredText {{{ 79 | try: 80 | from docutils.core import publish_parts 81 | _filters.append(( 82 | "rst", 83 | lambda txt: publish_parts(txt, writer_name='html')['fragment'])) 84 | except: 85 | pass 86 | # }}} 87 | # }}} 88 | 89 | _renderers = [] 90 | 91 | 92 | def render_init(app): 93 | ''' 94 | Set up all renderers and filters for a provided class 95 | ''' 96 | global _renderers 97 | _renderers = [] 98 | for cls in _renderer_classes: 99 | _renderers.append(cls(app)) 100 | for name, filt in _filters: 101 | app.jinja_env.filters[name] = filt 102 | 103 | 104 | def render_template(template, **kwargs_raw): 105 | ''' 106 | Try several template render methods 107 | 108 | Default to Jinja2 templates, fallback to mako 109 | ''' 110 | for render in _renderers: 111 | ret = render(template, **kwargs_raw) 112 | if ret is not None: 113 | return ret 114 | 115 | # vim: fdm=marker:et:ts=4:sw=4 116 | -------------------------------------------------------------------------------- /ofcourse/site.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Remy D 3 | Ralph Bean 4 | Sam Lucidi 5 | License: Apache 2.0 6 | 7 | """ 8 | 9 | from __future__ import division 10 | 11 | import os 12 | import yaml 13 | import hashlib 14 | from datetime import datetime 15 | 16 | # flask dependencies 17 | from flask import Flask 18 | from flask import jsonify 19 | from werkzeug.exceptions import NotFound 20 | 21 | # ofcourse 22 | from .render import render_init, render_template 23 | from .cal import ( 24 | load_calendar, normalize_categories, calendar_weeks, 25 | assignment_data, items_assigned, items_due) 26 | from ofcourse.util import count_posts, app_path 27 | from ofcourse.blueprints import homework, lectures, quizzes 28 | from ofcourse.participants import participants_bp 29 | 30 | app = Flask(__name__) 31 | app.static_folder = app_path("static") 32 | app.templates_folder = app_path("templates") 33 | app.people_folder = app_path("people") 34 | render_init(app) 35 | 36 | 37 | # Automatically include site config 38 | @app.context_processor 39 | def inject_yaml(): 40 | with open(app_path('site.yaml')) as site_yaml: 41 | site_config = yaml.safe_load(site_yaml) 42 | 43 | course_url = "http://localhost:5000/" 44 | 45 | if site_config['course'].get('public_url', None) is not None: 46 | course_url = site_config['course']['public_url'] 47 | elif os.environ.get('OPENSHIFT_GEAR_DNS', None) is not None: 48 | course_url = "https://" + os.environ['OPENSHIFT_GEAR_DNS'] 49 | 50 | site_config['course']['public_url'] = course_url 51 | return site_config 52 | 53 | config = inject_yaml() 54 | COURSE_START = datetime.combine(config['course']['start'], datetime.min.time()) 55 | COURSE_END = datetime.combine(config['course']['end'], datetime.max.time()) 56 | 57 | BLOG_START = datetime.combine(config['course'].get( 58 | 'blog_start', COURSE_START), datetime.min.time()) 59 | BLOG_END = datetime.combine(config['course'].get( 60 | 'blog_end', datetime.now()), datetime.max.time()) 61 | 62 | 63 | def gravatar(person_data, fallback_key, fallback_suffix): 64 | """ 65 | Get a gravatar for an email address. 66 | 67 | Defaults to libravatar with a gravatar fallback 68 | Uses the "avatar" key if it exists, or defaults to school email. 69 | """ 70 | email = person_data.get("avatar", 71 | person_data[fallback_key] + fallback_suffix) 72 | email = email.encode('utf8').lower() 73 | slug = hashlib.md5(email).hexdigest() 74 | libravatarURL = "https://seccdn.libravatar.org/avatar/" 75 | gravatarURL = "https://secure.gravatar.com/avatar/" 76 | return libravatarURL + slug + "?d=" + gravatarURL + slug 77 | 78 | 79 | @app.route('/', defaults=dict(page='home')) 80 | @app.route('/') 81 | def simple_page(page): 82 | """ 83 | Render a simple page. Looks for a template file 84 | with the name of the page parameter that was passed in. 85 | By default, this just shows the homepage. 86 | 87 | """ 88 | 89 | return render_template(page) 90 | 91 | 92 | @app.route('/syllabus') 93 | def syllabus(): 94 | """ 95 | Render the syllabus page. 96 | 97 | """ 98 | 99 | return render_template( 100 | 'syllabus', 101 | load_calendar=load_calendar, 102 | normalize_categories=normalize_categories, 103 | calendar_weeks=calendar_weeks, 104 | assignment_data=assignment_data, 105 | items_assigned=items_assigned, 106 | items_due=items_due) 107 | 108 | 109 | @app.route('/blog///') 110 | def blog_posts(year, term, username): 111 | """ 112 | Count number of posts on a student's 113 | blog. 114 | 115 | """ 116 | 117 | student_data = None 118 | 119 | fname = os.path.join(app.people_folder, year, term, username) + ".yaml" 120 | 121 | print("Getting blog count for: " + fname) 122 | with open(fname) as student: 123 | contents = yaml.safe_load(student) 124 | student_data = contents 125 | 126 | num_posts = 0 127 | if 'feed' in student_data: 128 | print("Checking %s's blog feed." % username) 129 | num_posts = count_posts(student_data['feed'], BLOG_START, BLOG_END) 130 | else: 131 | print("No feed listed for %s!" % username) 132 | raise NotFound() 133 | 134 | return jsonify(number=num_posts) 135 | 136 | 137 | @app.route('/blogs///') 138 | @app.route('/participants///') 139 | @app.route('/checkblogs///') 140 | def participant_page(year, term, username): 141 | """ 142 | Render a page that shows some stats about the selected participant 143 | """ 144 | 145 | participant_data = {} 146 | yaml_dir = app_path('people') 147 | participant_yaml = os.path.join(yaml_dir, year, term, username + '.yaml') 148 | with open(participant_yaml) as participant_file: 149 | participant_data = yaml.safe_load(participant_file) 150 | 151 | return render_template( 152 | 'participant', 153 | participant_data=participant_data, 154 | gravatar=gravatar 155 | ) 156 | 157 | 158 | @app.route('/oer') 159 | @app.route('/resources') 160 | def resources(): 161 | res = dict() 162 | oer_links = [] 163 | oer_yaml = app_path("oer.yaml") 164 | with open(oer_yaml) as oer_data: 165 | oer_links = yaml.safe_load(oer_data) 166 | 167 | res['links'] = {} 168 | res['Decks'] = [] 169 | res['Books'] = [] 170 | res['Videos'] = [] 171 | 172 | if os.path.exists(app_path('static', 'decks')): 173 | res['Decks'] = os.listdir(app_path('static', 'decks')) 174 | if 'decks' in oer_links: 175 | res['links']['decks'] = oer_links['decks'] 176 | 177 | if os.path.exists(app_path('static', 'books')): 178 | res['Books'] = os.listdir(app_path('static', 'books')) 179 | if 'books' in oer_links: 180 | res['links']['books'] = oer_links['books'] 181 | 182 | if os.path.exists(app_path('static', 'videos')): 183 | res['Videos'] = os.listdir(app_path('static', 'videos')) 184 | if 'videos' in oer_links: 185 | res['links']['videos'] = oer_links['videos'] 186 | 187 | return render_template('resources', resources=res) 188 | 189 | 190 | app.register_blueprint(homework, url_prefix='/assignments') 191 | app.register_blueprint(homework, url_prefix='/hw') 192 | app.register_blueprint(lectures, url_prefix='/lectures') 193 | app.register_blueprint(quizzes, url_prefix='/quizzes') 194 | app.register_blueprint(quizzes, url_prefix='/quiz') 195 | app.register_blueprint(participants_bp, url_prefix='/participants') 196 | app.register_blueprint(participants_bp, url_prefix='/blogs') 197 | app.register_blueprint(participants_bp, url_prefix='/checkblogs') 198 | -------------------------------------------------------------------------------- /ofcourse/static/books/Open-Advice.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/books/Open-Advice.pdf -------------------------------------------------------------------------------- /ofcourse/static/css/pace.css: -------------------------------------------------------------------------------- 1 | .pace { 2 | -webkit-pointer-events: none; 3 | pointer-events: none; 4 | -webkit-user-select: none; 5 | -moz-user-select: none; 6 | user-select: none; 7 | } 8 | 9 | .pace-inactive { 10 | display: none; 11 | } 12 | 13 | .pace .pace-progress { 14 | background-color: #29d; 15 | position: fixed; 16 | z-index: 2000; 17 | top: 0; 18 | left: 0; 19 | height: 12px; 20 | overflow: hidden; 21 | 22 | -webkit-transition: width 1s; 23 | -moz-transition: width 1s; 24 | -o-transition: width 1s; 25 | transition: width 1s; 26 | } 27 | 28 | .pace .pace-progress-inner { 29 | position: absolute; 30 | top: 0; 31 | left: 0; 32 | right: -32px; 33 | bottom: 0; 34 | 35 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.2)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.2)), color-stop(0.75, rgba(255, 255, 255, 0.2)), color-stop(0.75, transparent), to(transparent)); 36 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 37 | background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 38 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 39 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 40 | -webkit-background-size: 32px 32px; 41 | -moz-background-size: 32px 32px; 42 | -o-background-size: 32px 32px; 43 | background-size: 32px 32px; 44 | 45 | -webkit-animation: pace-stripe-animation 500ms linear infinite; 46 | -moz-animation: pace-stripe-animation 500ms linear infinite; 47 | -ms-animation: pace-stripe-animation 500ms linear infinite; 48 | -o-animation: pace-stripe-animation 500ms linear infinite; 49 | animation: pace-stripe-animation 500ms linear infinite; 50 | } 51 | 52 | @-webkit-keyframes pace-stripe-animation { 53 | 0% { -webkit-transform: none; transform: none; } 54 | 100% { -webkit-transform: translate(-32px, 0); transform: translate(-32px, 0); } 55 | } 56 | @-moz-keyframes pace-stripe-animation { 57 | 0% { -moz-transform: none; transform: none; } 58 | 100% { -moz-transform: translate(-32px, 0); transform: translate(-32px, 0); } 59 | } 60 | @-o-keyframes pace-stripe-animation { 61 | 0% { -o-transform: none; transform: none; } 62 | 100% { -o-transform: translate(-32px, 0); transform: translate(-32px, 0); } 63 | } 64 | @-ms-keyframes pace-stripe-animation { 65 | 0% { -ms-transform: none; transform: none; } 66 | 100% { -ms-transform: translate(-32px, 0); transform: translate(-32px, 0); } 67 | } 68 | @keyframes pace-stripe-animation { 69 | 0% { transform: none; transform: none; } 70 | 100% { transform: translate(-32px, 0); transform: translate(-32px, 0); } 71 | } 72 | -------------------------------------------------------------------------------- /ofcourse/static/css/ripples.min.css: -------------------------------------------------------------------------------- 1 | .withripple{position:relative}.ripple-wrapper{position:absolute;top:0;left:0;z-index:1;width:100%;height:100%;overflow:hidden;border-radius:inherit;pointer-events:none}.ripple{position:absolute;width:20px;height:20px;margin-left:-10px;margin-top:-10px;border-radius:100%;background-color:rgba(0,0,0,.05);-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);-webkit-transform-origin:50%;-ms-transform-origin:50%;transform-origin:50%;opacity:0;pointer-events:none}.ripple.ripple-on{transition:opacity .15s ease-in 0s,-webkit-transform .5s cubic-bezier(.4,0,.2,1) .1s;transition:opacity .15s ease-in 0s,transform .5s cubic-bezier(.4,0,.2,1) .1s;opacity:.1}.ripple.ripple-out{transition:opacity .1s linear 0s!important;opacity:0} 2 | /*# sourceMappingURL=ripples.min.css.map */ -------------------------------------------------------------------------------- /ofcourse/static/css/site.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Liberation Sans'; 3 | src: url('../fonts/LiberationSans-Regular-webfont.eot'); 4 | src: url('../fonts/LiberationSans-Regular-webfont.eot?iefix') format('eot'), 5 | url('../fonts/LiberationSans-Regular-webfont.woff') format('woff'), 6 | url('../fonts/LiberationSans-Regular-webfont.ttf') format('truetype'), 7 | url('../fonts/LiberationSans-Regular-webfont.svg#webfont') format('svg'); 8 | } 9 | 10 | body { 11 | padding-top: 60px; 12 | padding-bottom: 40px; 13 | } 14 | 15 | .alert { 16 | color: #333; 17 | } 18 | 19 | div.alert.alert-info, div.alert.alert-success, div.alert.alert-warning, div.alert.alert-failure { 20 | color: #FFF; 21 | } 22 | 23 | a, .alert code>a, .alert a { 24 | color: #303f9f; 25 | } 26 | 27 | a:hover { 28 | color: #3f51b5; 29 | } 30 | 31 | body .well, .container .well, .container-fluid .well, body .jumbotron, .container .jumbotron, .container-fluid .jumbotron, body .well-default, .container .well-default, .container-fluid .well-default, body .jumbotron-default, .container .jumbotron-default, .container-fluid .jumbotron-default { 32 | box-shadow: none; 33 | background-color: none; 34 | } 35 | 36 | body, div, p { 37 | font-family: 'Liberation Sans', Arial, sans-serif; 38 | } 39 | 40 | h1,h2,h3,h4,h5,h6, .jumbotron p { 41 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 42 | font-weight: bold; 43 | } 44 | 45 | .padded { 46 | margin-bottom: 10px; 47 | padding: 10px; 48 | } 49 | 50 | .headerlink { 51 | padding-top: 50px; 52 | } 53 | 54 | .navbar .navbar-toggle .icon-bar { 55 | color: white; 56 | } 57 | 58 | @media(max-width:1200px){ 59 | .navbar .navbar-nav > li > a { 60 | padding-top: 10px; 61 | padding-bottom: 10px; 62 | } 63 | } 64 | 65 | /* Don't overlap affix below md size*/ 66 | @media(max-width:991px){ 67 | .affix { 68 | position: relative; 69 | width: auto; 70 | } 71 | } 72 | 73 | .container { 74 | padding-top: 0; 75 | } 76 | 77 | @media(min-width: 1200px) { 78 | .container { 79 | padding-top: 15px; 80 | } 81 | } 82 | 83 | .item { 84 | text-align: right; 85 | } 86 | 87 | .center { 88 | text-align: center; 89 | } 90 | 91 | .panel-info .panel-heading { 92 | background-color: #3F51B5; 93 | } 94 | 95 | /* create new stacking context 1 above the auto level */ 96 | .raise { 97 | position: relative; 98 | z-index: 1; 99 | } 100 | 101 | .panel-info .panel-heading { 102 | color: #3F51B5; 103 | } 104 | 105 | .panel-title { 106 | color: #FFF; 107 | padding: 5px 0; 108 | } 109 | 110 | .panel-info a:hover { 111 | text-decoration: none; 112 | } 113 | 114 | .avatar-link { 115 | border-radius: 100%; 116 | box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.23), 0px 10px 30px rgba(0, 0, 0, 0.19); 117 | height: 70px; 118 | width: 70px; 119 | } 120 | 121 | /* showblogs-specific */ 122 | .uglymug { 123 | border-radius: 64px; 124 | -webkit-border-radius: 64px; 125 | -moz-border-radius: 64px; 126 | height: 70px; 127 | } 128 | 129 | .forge-list { 130 | padding-left: 0; 131 | list-style: none; 132 | } 133 | 134 | .blog { 135 | margin-top: 10px; 136 | font-size: x-large; 137 | } 138 | 139 | .cardlist { 140 | margin-top: 10px; 141 | } 142 | 143 | .shadowcard { 144 | box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5); 145 | } 146 | 147 | .topic { 148 | margin: 0; 149 | } 150 | 151 | /* Colors! */ 152 | .special { 153 | background: yellow; 154 | } 155 | 156 | .cancelled { 157 | background: red; 158 | } 159 | 160 | .guest { 161 | background: lightgreen; 162 | } 163 | 164 | .hackathon { 165 | background: orange; 166 | } 167 | 168 | .vaycay { 169 | background: darkgrey; 170 | } 171 | 172 | .label-warning { 173 | background: #ffc722; 174 | } 175 | -------------------------------------------------------------------------------- /ofcourse/static/decks/hanginthere.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/decks/hanginthere.jpg -------------------------------------------------------------------------------- /ofcourse/static/fonts/LiberationSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/fonts/LiberationSans-Regular-webfont.woff -------------------------------------------------------------------------------- /ofcourse/static/fonts/Red Hat Liberation License.txt: -------------------------------------------------------------------------------- 1 | LICENSE AGREEMENT AND LIMITED PRODUCT WARRANTY LIBERATION FONT SOFTWARE 2 | This agreement governs the use of the Software and any updates to the 3 | Software, regardless of the delivery mechanism. Subject to the following 4 | terms, Red Hat, Inc. ("Red Hat") grants to the user ("Client") a license to 5 | this collective work pursuant to the GNU General Public License v.2 with the 6 | exceptions set forth below and such other terms as our set forth in this End 7 | User License Agreement. 8 | 1. The Software and License Exception. LIBERATION font software (the 9 | "Software") consists of TrueType-OpenType formatted font software for 10 | rendering LIBERATION typefaces in sans serif, serif, and monospaced character 11 | styles. You are licensed to use, modify, copy, and distribute the Software 12 | pursuant to the GNU General Public License v.2 with the following exceptions: 13 | 1) As a special exception, if you create a document which uses this font, and 14 | embed this font or unaltered portions of this font into the document, this 15 | font does not by itself cause the resulting document to be covered by the GNU 16 | General Public License.  This exception does not however invalidate any other 17 | reasons why the document might be covered by the GNU General Public License.  18 | If you modify this font, you may extend this exception to your version of the 19 | font, but you are not obligated to do so. If you do not wish to do so, delete 20 | this exception statement from your version. 21 | 22 | 2) As a further exception, any distribution of the object code of the Software 23 | in a physical product must provide you the right to access and modify the 24 | source code for the Software and to reinstall that modified version of the 25 | Software in object code form on the same physical product on which you 26 | received it. 27 | 2. Intellectual Property Rights. The Software and each of its components, 28 | including the source code, documentation, appearance, structure and 29 | organization are owned by Red Hat and others and are protected under copyright 30 | and other laws. Title to the Software and any component, or to any copy, 31 | modification, or merged portion shall remain with the aforementioned, subject 32 | to the applicable license. The "LIBERATION" trademark is a trademark of Red 33 | Hat, Inc. in the U.S. and other countries. This agreement does not permit 34 | Client to distribute modified versions of the Software using Red Hat's 35 | trademarks. If Client makes a redistribution of a modified version of the 36 | Software, then Client must modify the files names to remove any reference to 37 | the Red Hat trademarks and must not use the Red Hat trademarks in any way to 38 | reference or promote the modified Software. 39 | 3. Limited Warranty. To the maximum extent permitted under applicable law, the 40 | Software is provided and licensed "as is" without warranty of any kind, 41 | expressed or implied, including the implied warranties of merchantability, 42 | non-infringement or fitness for a particular purpose. Red Hat does not warrant 43 | that the functions contained in the Software will meet Client's requirements 44 | or that the operation of the Software will be entirely error free or appear 45 | precisely as described in the accompanying documentation. 46 | 4. Limitation of Remedies and Liability. To the maximum extent permitted by 47 | applicable law, Red Hat or any Red Hat authorized dealer will not be liable to 48 | Client for any incidental or consequential damages, including lost profits or 49 | lost savings arising out of the use or inability to use the Software, even if 50 | Red Hat or such dealer has been advised of the possibility of such damages. 51 | 5. Export Control. As required by U.S. law, Client represents and warrants 52 | that it: (a) understands that the Software is subject to export controls under 53 | the U.S. Commerce Department's Export Administration Regulations ("EAR"); (b) 54 | is not located in a prohibited destination country under the EAR or U.S. 55 | sanctions regulations (currently Cuba, Iran, Iraq, Libya, North Korea, Sudan 56 | and Syria); (c) will not export, re-export, or transfer the Software to any 57 | prohibited destination, entity, or individual without the necessary export 58 | license(s) or authorizations(s) from the U.S. Government; (d) will not use or 59 | transfer the Software for use in any sensitive nuclear, chemical or biological 60 | weapons, or missile technology end-uses unless authorized by the U.S. 61 | Government by regulation or specific license; (e) understands and agrees that 62 | if it is in the United States and exports or transfers the Software to 63 | eligible end users, it will, as required by EAR Section 740.17(e), submit 64 | semi-annual reports to the Commerce Department's Bureau of Industry & Security 65 | (BIS), which include the name and address (including country) of each 66 | transferee; and (f) understands that countries other than the United States 67 | may restrict the import, use, or export of encryption products and that it 68 | shall be solely responsible for compliance with any such import, use, or 69 | export restrictions. 70 | 6. General. If any provision of this agreement is held to be unenforceable, 71 | that shall not affect the enforceability of the remaining provisions. This 72 | agreement shall be governed by the laws of the State of North Carolina and of 73 | the United States, without regard to any conflict of laws provisions, except 74 | that the United Nations Convention on the International Sale of Goods shall 75 | not apply. 76 | Copyright © 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark 77 | of Red Hat, Inc. -------------------------------------------------------------------------------- /ofcourse/static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /ofcourse/static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /ofcourse/static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /ofcourse/static/hw/addyourselftocoursewebsite.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | HFOSS@RIT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 33 | 34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |

Add Yourself To The Course Website

42 |
43 |
44 |

Check out the source repository for this course; it's hosted at 45 | https://github.com/decause/hflossk.

46 |

Inside the repository, we'll keep an index of all the students in the course and metadata about them (you!).

47 |
48 |

Tasks:

49 |
    50 |
  • Load up the git cheatsheet at Zack 52 | Rusin's blog and keep it nearby.
  • 53 |
  • Work through this git tutorial if 55 | you don't have any experience with git.
  • 56 |
  • Fork the 57 | repository (link to github help on 59 | this).
  • 60 |
  • Clone a local copy.
  • 61 |
  • Add a file in the /scripts/people folder titled $YOUR_IRC_NICK.yaml. Perhaps obviously, it is 62 | a YAML file. You can use the rjbpop.yaml file as an example. 63 | 64 | 65 |
    66 | BE WARNED: Your .yaml file must match the format *exactly* (meaning it is case and whitespace sensitive.) 67 |
    68 |
    69 | CRUFTY: There is a people.yaml file in that directory. It is a legacy hangover from older code. Do not bother editing it. It will actually make merges more difficult. 70 |
  • 71 | 72 |
  • Once you've confirmed your .yaml file matches exactly, commit and push your changes to github, and issue a pull request.
  • 73 |
  • Once the patch is accepted upstream and pushed to production, this 74 | should add your blog feed to the Participants page.)
  • 75 |
76 |
77 |
78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 |
87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /ofcourse/static/hw/createFASaccount.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/hw/createFASaccount.html -------------------------------------------------------------------------------- /ofcourse/static/hw/creategithubaccount.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | HFOSS@RIT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 33 | 34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |

Create A Github Account

42 |
43 |
44 |

45 | You'll need to create your own account on github.com. All development for this 46 | course should be tracked on that forge. Github is, after all, the most popular 47 | forge. 48 |

49 |
50 |

Tasks:

51 |
    52 |
  1. Create a Github account if you don't already have one.
  2. 53 |
54 |
55 |
56 |
57 |
58 | 59 |
60 | 61 |
62 |
63 | 64 |
65 | 66 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /ofcourse/static/hw/getyourfirstpullrequestaccepted.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | HFOSS@RIT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 33 | 34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |

Get Your First Pull Request Accepted

42 |
43 |
44 |

Check out the source repository for this course; it's hosted at 45 | https://github.com/decause/hflossk.

46 |

Inside the repository, we'll keep an index of all the students in the course and metadata about them (you!).

47 |
48 |

Tasks:

49 |
    50 |
  • Load up the git cheatsheet at Zack 52 | Rusin's blog and keep it nearby.
  • 53 |
  • Work through this git tutorial if 55 | you don't have any experience with git.
  • 56 |
  • Fork the 57 | repository (link to github help on 59 | this).
  • 60 |
  • Clone a local copy.
  • 61 |
  • Add a file in the /scripts/people folder titled $YOUR_IRC_NICK.yaml. Perhaps obviously, it is 62 | a YAML file. You can use the rjbpop.yaml file as an example. 63 | 64 | 65 |
    66 | BE WARNED: Your .yaml file must match the format *exactly* (meaning it is case and whitespace sensitive.) 67 |
    68 |
    69 | CRUFTY: There is a people.yaml file in that directory. It is a legacy hangover from older code. Do not bother editing it. It will actually make merges more difficult. 70 |
  • 71 | 72 |
  • Once you've confirmed your .yaml file matches exactly, commit and push your changes to github, and issue a pull request.
  • 73 |
  • Once the patch is accepted upstream and pushed to production, this 74 | should add your blog feed to the Participants page.)
  • 75 |
76 |
77 |
78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 |
87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /ofcourse/static/hw/issueapullrequest.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | HFOSS@RIT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 33 | 34 | 35 | 36 |
37 |
38 |
39 | 40 |
41 |

Get Your First Pull Request Accepted

42 |
43 |
44 |

Check out the source repository for this course; it's hosted at 45 | https://github.com/decause/hflossk.

46 |

Inside the repository, we'll keep an index of all the students in the course and metadata about them (you!).

47 |
48 |

Tasks:

49 |
    50 |
  • Load up the git cheatsheet at Zack 52 | Rusin's blog and keep it nearby.
  • 53 |
  • Work through this git tutorial if 55 | you don't have any experience with git.
  • 56 |
  • Fork the 57 | repository (link to github help on 59 | this).
  • 60 |
  • Clone a local copy.
  • 61 |
  • Add a file in the /scripts/people folder titled $YOUR_IRC_NICK.yaml. Perhaps obviously, it is 62 | a YAML file. You can use the rjbpop.yaml file as an example. 63 | 64 | 65 |
    66 | BE WARNED: Your .yaml file must match the format *exactly* (meaning it is case and whitespace sensitive.) 67 |
    68 |
    69 | CRUFTY: There is a people.yaml file in that directory. It is a legacy hangover from older code. Do not bother editing it. It will actually make merges more difficult. 70 |
  • 71 | 72 |
  • Once you've confirmed your .yaml file matches exactly, commit and push your changes to github, and issue a pull request.
  • 73 |
  • Once the patch is accepted upstream and pushed to production, this 74 | should add your blog feed to the Participants page.)
  • 75 |
76 |
77 |
78 |
79 |
80 | 81 |
82 | 83 |
84 |
85 | 86 |
87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-144x144.png -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-192x192.png -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-36x36.png -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-48x48.png -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-72x72.png -------------------------------------------------------------------------------- /ofcourse/static/img/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/android-icon-96x96.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-114x114.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-120x120.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-144x144.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-152x152.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-180x180.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-57x57.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-60x60.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-72x72.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-76x76.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon-precomposed.png -------------------------------------------------------------------------------- /ofcourse/static/img/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/apple-icon.png -------------------------------------------------------------------------------- /ofcourse/static/img/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /ofcourse/static/img/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/favicon-16x16.png -------------------------------------------------------------------------------- /ofcourse/static/img/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/favicon-96x96.png -------------------------------------------------------------------------------- /ofcourse/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/favicon.ico -------------------------------------------------------------------------------- /ofcourse/static/img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/favicon.png -------------------------------------------------------------------------------- /ofcourse/static/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /ofcourse/static/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /ofcourse/static/img/grid-baseline-20px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/grid-baseline-20px.png -------------------------------------------------------------------------------- /ofcourse/static/img/less-logo-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/less-logo-large.png -------------------------------------------------------------------------------- /ofcourse/static/img/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /ofcourse/static/img/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/ms-icon-144x144.png -------------------------------------------------------------------------------- /ofcourse/static/img/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/ms-icon-150x150.png -------------------------------------------------------------------------------- /ofcourse/static/img/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/ms-icon-310x310.png -------------------------------------------------------------------------------- /ofcourse/static/img/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/ms-icon-70x70.png -------------------------------------------------------------------------------- /ofcourse/static/img/responsive-illustrations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/static/img/responsive-illustrations.png -------------------------------------------------------------------------------- /ofcourse/static/js/html5shiv.js: -------------------------------------------------------------------------------- 1 | /* 2 | HTML5 Shiv v3.6.2pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | (function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); 5 | a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/\w+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; 6 | c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| 7 | "undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",version:"3.6.2pre",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f);if(g)return a.createDocumentFragment(); 8 | for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d0?!a.ctrlKey&&!a.metaKey&&!a.altKey&&8!=a.which:!1}a.expr[":"].notmdproc=function(b){return a(b).data("mdproc")?!1:!0},a.material={options:{input:!0,ripples:!0,checkbox:!0,togglebutton:!0,radio:!0,arrive:!0,autofill:!1,withRipples:[".btn:not(.btn-link)",".card-image",".navbar a:not(.withoutripple)",".dropdown-menu a",".nav-tabs a:not(.withoutripple)",".withripple"].join(","),inputElements:"input.form-control, textarea.form-control, select.form-control",checkboxElements:".checkbox > label > input[type=checkbox]",togglebuttonElements:".togglebutton > label > input[type=checkbox]",radioElements:".radio > label > input[type=radio]"},checkbox:function(b){a(b?b:this.options.checkboxElements).filter(":notmdproc").data("mdproc",!0).after("")},togglebutton:function(b){a(b?b:this.options.togglebuttonElements).filter(":notmdproc").data("mdproc",!0).after("")},radio:function(b){a(b?b:this.options.radioElements).filter(":notmdproc").data("mdproc",!0).after("")},input:function(c){a(c?c:this.options.inputElements).filter(":notmdproc").data("mdproc",!0).each(function(){var b=a(this);if(a(this).attr("data-hint")||b.hasClass("floating-label")){if(b.wrap("
"),b.after(""),b.hasClass("floating-label")){var c=b.attr("placeholder");b.attr("placeholder",null).removeClass("floating-label"),b.after("
"+c+"
")}if(b.attr("data-hint")&&b.after("
"+b.attr("data-hint")+"
"),(null===b.val()||"undefined"==b.val()||""===b.val())&&b.addClass("empty"),b.parent().next().is("[type=file]")){b.parent().addClass("fileinput");var d=b.parent().next().detach();b.after(d)}}}),a(document).on("change",".checkbox input[type=checkbox]",function(){a(this).blur()}).on("keydown paste",".form-control",function(c){b(c)&&a(this).removeClass("empty")}).on("keyup change",".form-control",function(){var b=a(this);""===b.val()&&b[0].checkValidity()?b.addClass("empty"):b.removeClass("empty")}).on("focus",".form-control-wrapper.fileinput",function(){a(this).find("input").addClass("focus")}).on("blur",".form-control-wrapper.fileinput",function(){a(this).find("input").removeClass("focus")}).on("change",".form-control-wrapper.fileinput [type=file]",function(){var b="";a.each(a(this)[0].files,function(a,c){b+=c.name+", "}),b=b.substring(0,b.length-2),b?a(this).prev().removeClass("empty"):a(this).prev().addClass("empty"),a(this).prev().val(b)})},ripples:function(b){a(b?b:this.options.withRipples).ripples()},autofill:function(){var b=setInterval(function(){a("input[type!=checkbox]").each(function(){a(this).val()&&a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100);setTimeout(function(){clearInterval(b)},1e4);var c;a(document).on("focus","input",function(){var b=a(this).parents("form").find("input").not("[type=file]");c=setInterval(function(){b.each(function(){a(this).val()!==a(this).attr("value")&&a(this).trigger("change")})},100)}).on("blur","input",function(){clearInterval(c)})},init:function(){a.fn.ripples&&this.options.ripples&&this.ripples(),this.options.input&&this.input(),this.options.checkbox&&this.checkbox(),this.options.togglebutton&&this.togglebutton(),this.options.radio&&this.radio(),this.options.autofill&&this.autofill(),document.arrive&&this.options.arrive&&(a.fn.ripples&&this.options.ripples&&a(document).arrive(this.options.withRipples,function(){a.material.ripples(a(this))}),this.options.input&&a(document).arrive(this.options.inputElements,function(){a.material.input(a(this))}),this.options.checkbox&&a(document).arrive(this.options.checkboxElements,function(){a.material.checkbox(a(this))}),this.options.radio&&a(document).arrive(this.options.radioElements,function(){a.material.radio(a(this))}),this.options.togglebutton&&a(document).arrive(this.options.togglebuttonElements,function(){a.material.togglebutton(a(this))}))}}}(jQuery); 2 | //# sourceMappingURL=material.min.js.map 3 | -------------------------------------------------------------------------------- /ofcourse/static/js/pace.min.js: -------------------------------------------------------------------------------- 1 | /*! pace 0.5.1 */ 2 | (function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W=[].slice,X={}.hasOwnProperty,Y=function(a,b){function c(){this.constructor=a}for(var d in b)X.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},Z=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};for(t={catchupTime:500,initialRate:.03,minTime:500,ghostTime:500,maxProgressPerFrame:10,easeFactor:1.25,startOnPageLoad:!0,restartOnPushState:!0,restartOnRequestAfter:500,target:"body",elements:{checkInterval:100,selectors:["body"]},eventLag:{minSamples:10,sampleCount:3,lagThreshold:3},ajax:{trackMethods:["GET"],trackWebSockets:!0,ignoreURLs:[]}},B=function(){var a;return null!=(a="undefined"!=typeof performance&&null!==performance?"function"==typeof performance.now?performance.now():void 0:void 0)?a:+new Date},D=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame,s=window.cancelAnimationFrame||window.mozCancelAnimationFrame,null==D&&(D=function(a){return setTimeout(a,50)},s=function(a){return clearTimeout(a)}),F=function(a){var b,c;return b=B(),(c=function(){var d;return d=B()-b,d>=33?(b=B(),a(d,function(){return D(c)})):setTimeout(c,33-d)})()},E=function(){var a,b,c;return c=arguments[0],b=arguments[1],a=3<=arguments.length?W.call(arguments,2):[],"function"==typeof c[b]?c[b].apply(c,a):c[b]},u=function(){var a,b,c,d,e,f,g;for(b=arguments[0],d=2<=arguments.length?W.call(arguments,1):[],f=0,g=d.length;g>f;f++)if(c=d[f])for(a in c)X.call(c,a)&&(e=c[a],null!=b[a]&&"object"==typeof b[a]&&null!=e&&"object"==typeof e?u(b[a],e):b[a]=e);return b},p=function(a){var b,c,d,e,f;for(c=b=0,e=0,f=a.length;f>e;e++)d=a[e],c+=Math.abs(d),b++;return c/b},w=function(a,b){var c,d,e;if(null==a&&(a="options"),null==b&&(b=!0),e=document.querySelector("[data-pace-"+a+"]")){if(c=e.getAttribute("data-pace-"+a),!b)return c;try{return JSON.parse(c)}catch(f){return d=f,"undefined"!=typeof console&&null!==console?console.error("Error parsing inline pace options",d):void 0}}},g=function(){function a(){}return a.prototype.on=function(a,b,c,d){var e;return null==d&&(d=!1),null==this.bindings&&(this.bindings={}),null==(e=this.bindings)[a]&&(e[a]=[]),this.bindings[a].push({handler:b,ctx:c,once:d})},a.prototype.once=function(a,b,c){return this.on(a,b,c,!0)},a.prototype.off=function(a,b){var c,d,e;if(null!=(null!=(d=this.bindings)?d[a]:void 0)){if(null==b)return delete this.bindings[a];for(c=0,e=[];cP;P++)J=T[P],C[J]===!0&&(C[J]=t[J]);i=function(a){function b(){return U=b.__super__.constructor.apply(this,arguments)}return Y(b,a),b}(Error),b=function(){function a(){this.progress=0}return a.prototype.getElement=function(){var a;if(null==this.el){if(a=document.querySelector(C.target),!a)throw new i;this.el=document.createElement("div"),this.el.className="pace pace-active",document.body.className=document.body.className.replace(/pace-done/g,""),document.body.className+=" pace-running",this.el.innerHTML='
\n
\n
\n
',null!=a.firstChild?a.insertBefore(this.el,a.firstChild):a.appendChild(this.el)}return this.el},a.prototype.finish=function(){var a;return a=this.getElement(),a.className=a.className.replace("pace-active",""),a.className+=" pace-inactive",document.body.className=document.body.className.replace("pace-running",""),document.body.className+=" pace-done"},a.prototype.update=function(a){return this.progress=a,this.render()},a.prototype.destroy=function(){try{this.getElement().parentNode.removeChild(this.getElement())}catch(a){i=a}return this.el=void 0},a.prototype.render=function(){var a,b;return null==document.querySelector(C.target)?!1:(a=this.getElement(),a.children[0].style.width=""+this.progress+"%",(!this.lastRenderedProgress||this.lastRenderedProgress|0!==this.progress|0)&&(a.children[0].setAttribute("data-progress-text",""+(0|this.progress)+"%"),this.progress>=100?b="99":(b=this.progress<10?"0":"",b+=0|this.progress),a.children[0].setAttribute("data-progress",""+b)),this.lastRenderedProgress=this.progress)},a.prototype.done=function(){return this.progress>=100},a}(),h=function(){function a(){this.bindings={}}return a.prototype.trigger=function(a,b){var c,d,e,f,g;if(null!=this.bindings[a]){for(f=this.bindings[a],g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(c.call(this,b));return g}},a.prototype.on=function(a,b){var c;return null==(c=this.bindings)[a]&&(c[a]=[]),this.bindings[a].push(b)},a}(),O=window.XMLHttpRequest,N=window.XDomainRequest,M=window.WebSocket,v=function(a,b){var c,d,e,f;f=[];for(d in b.prototype)try{e=b.prototype[d],null==a[d]&&"function"!=typeof e?f.push(a[d]=e):f.push(void 0)}catch(g){c=g}return f},z=[],Pace.ignore=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?W.call(arguments,1):[],z.unshift("ignore"),c=b.apply(null,a),z.shift(),c},Pace.track=function(){var a,b,c;return b=arguments[0],a=2<=arguments.length?W.call(arguments,1):[],z.unshift("track"),c=b.apply(null,a),z.shift(),c},I=function(a){var b;if(null==a&&(a="GET"),"track"===z[0])return"force";if(!z.length&&C.ajax){if("socket"===a&&C.ajax.trackWebSockets)return!0;if(b=a.toUpperCase(),Z.call(C.ajax.trackMethods,b)>=0)return!0}return!1},j=function(a){function b(){var a,c=this;b.__super__.constructor.apply(this,arguments),a=function(a){var b;return b=a.open,a.open=function(d,e){return I(d)&&c.trigger("request",{type:d,url:e,request:a}),b.apply(a,arguments)}},window.XMLHttpRequest=function(b){var c;return c=new O(b),a(c),c},v(window.XMLHttpRequest,O),null!=N&&(window.XDomainRequest=function(){var b;return b=new N,a(b),b},v(window.XDomainRequest,N)),null!=M&&C.ajax.trackWebSockets&&(window.WebSocket=function(a,b){var d;return d=null!=b?new M(a,b):new M(a),I("socket")&&c.trigger("request",{type:"socket",url:a,protocols:b,request:d}),d},v(window.WebSocket,M))}return Y(b,a),b}(h),Q=null,x=function(){return null==Q&&(Q=new j),Q},H=function(a){var b,c,d,e;for(e=C.ajax.ignoreURLs,c=0,d=e.length;d>c;c++)if(b=e[c],"string"==typeof b){if(-1!==a.indexOf(b))return!0}else if(b.test(a))return!0;return!1},x().on("request",function(b){var c,d,e,f,g;return f=b.type,e=b.request,g=b.url,H(g)?void 0:Pace.running||C.restartOnRequestAfter===!1&&"force"!==I(f)?void 0:(d=arguments,c=C.restartOnRequestAfter||0,"boolean"==typeof c&&(c=0),setTimeout(function(){var b,c,g,h,i,j;if(b="socket"===f?e.readyState<2:0<(h=e.readyState)&&4>h){for(Pace.restart(),i=Pace.sources,j=[],c=0,g=i.length;g>c;c++){if(J=i[c],J instanceof a){J.watch.apply(J,d);break}j.push(void 0)}return j}},c))}),a=function(){function a(){var a=this;this.elements=[],x().on("request",function(){return a.watch.apply(a,arguments)})}return a.prototype.watch=function(a){var b,c,d,e;return d=a.type,b=a.request,e=a.url,H(e)?void 0:(c="socket"===d?new m(b):new n(b),this.elements.push(c))},a}(),n=function(){function a(a){var b,c,d,e,f,g,h=this;if(this.progress=0,null!=window.ProgressEvent)for(c=null,a.addEventListener("progress",function(a){return h.progress=a.lengthComputable?100*a.loaded/a.total:h.progress+(100-h.progress)/2}),g=["load","abort","timeout","error"],d=0,e=g.length;e>d;d++)b=g[d],a.addEventListener(b,function(){return h.progress=100});else f=a.onreadystatechange,a.onreadystatechange=function(){var b;return 0===(b=a.readyState)||4===b?h.progress=100:3===a.readyState&&(h.progress=50),"function"==typeof f?f.apply(null,arguments):void 0}}return a}(),m=function(){function a(a){var b,c,d,e,f=this;for(this.progress=0,e=["error","open"],c=0,d=e.length;d>c;c++)b=e[c],a.addEventListener(b,function(){return f.progress=100})}return a}(),d=function(){function a(a){var b,c,d,f;for(null==a&&(a={}),this.elements=[],null==a.selectors&&(a.selectors=[]),f=a.selectors,c=0,d=f.length;d>c;c++)b=f[c],this.elements.push(new e(b))}return a}(),e=function(){function a(a){this.selector=a,this.progress=0,this.check()}return a.prototype.check=function(){var a=this;return document.querySelector(this.selector)?this.done():setTimeout(function(){return a.check()},C.elements.checkInterval)},a.prototype.done=function(){return this.progress=100},a}(),c=function(){function a(){var a,b,c=this;this.progress=null!=(b=this.states[document.readyState])?b:100,a=document.onreadystatechange,document.onreadystatechange=function(){return null!=c.states[document.readyState]&&(c.progress=c.states[document.readyState]),"function"==typeof a?a.apply(null,arguments):void 0}}return a.prototype.states={loading:0,interactive:50,complete:100},a}(),f=function(){function a(){var a,b,c,d,e,f=this;this.progress=0,a=0,e=[],d=0,c=B(),b=setInterval(function(){var g;return g=B()-c-50,c=B(),e.push(g),e.length>C.eventLag.sampleCount&&e.shift(),a=p(e),++d>=C.eventLag.minSamples&&a=100&&(this.done=!0),b===this.last?this.sinceLastUpdate+=a:(this.sinceLastUpdate&&(this.rate=(b-this.last)/this.sinceLastUpdate),this.catchup=(b-this.progress)/C.catchupTime,this.sinceLastUpdate=0,this.last=b),b>this.progress&&(this.progress+=this.catchup*a),c=1-Math.pow(this.progress/100,C.easeFactor),this.progress+=c*this.rate*a,this.progress=Math.min(this.lastProgress+C.maxProgressPerFrame,this.progress),this.progress=Math.max(0,this.progress),this.progress=Math.min(100,this.progress),this.lastProgress=this.progress,this.progress},a}(),K=null,G=null,q=null,L=null,o=null,r=null,Pace.running=!1,y=function(){return C.restartOnPushState?Pace.restart():void 0},null!=window.history.pushState&&(S=window.history.pushState,window.history.pushState=function(){return y(),S.apply(window.history,arguments)}),null!=window.history.replaceState&&(V=window.history.replaceState,window.history.replaceState=function(){return y(),V.apply(window.history,arguments)}),k={ajax:a,elements:d,document:c,eventLag:f},(A=function(){var a,c,d,e,f,g,h,i;for(Pace.sources=K=[],g=["ajax","elements","document","eventLag"],c=0,e=g.length;e>c;c++)a=g[c],C[a]!==!1&&K.push(new k[a](C[a]));for(i=null!=(h=C.extraSources)?h:[],d=0,f=i.length;f>d;d++)J=i[d],K.push(new J(C));return Pace.bar=q=new b,G=[],L=new l})(),Pace.stop=function(){return Pace.trigger("stop"),Pace.running=!1,q.destroy(),r=!0,null!=o&&("function"==typeof s&&s(o),o=null),A()},Pace.restart=function(){return Pace.trigger("restart"),Pace.stop(),Pace.start()},Pace.go=function(){var a;return Pace.running=!0,q.render(),a=B(),r=!1,o=F(function(b,c){var d,e,f,g,h,i,j,k,m,n,o,p,s,t,u,v;for(k=100-q.progress,e=o=0,f=!0,i=p=0,t=K.length;t>p;i=++p)for(J=K[i],n=null!=G[i]?G[i]:G[i]=[],h=null!=(v=J.elements)?v:[J],j=s=0,u=h.length;u>s;j=++s)g=h[j],m=null!=n[j]?n[j]:n[j]=new l(g),f&=m.done,m.done||(e++,o+=m.tick(b));return d=o/e,q.update(L.tick(b,d)),q.done()||f||r?(q.update(100),Pace.trigger("done"),setTimeout(function(){return q.finish(),Pace.running=!1,Pace.trigger("hide")},Math.max(C.ghostTime,Math.max(C.minTime-(B()-a),0)))):c()})},Pace.start=function(a){u(C,a),Pace.running=!0;try{q.render()}catch(b){i=b}return document.querySelector(".pace")?(Pace.trigger("start"),Pace.go()):setTimeout(Pace.start,50)},"function"==typeof define&&define.amd?define(function(){return Pace}):"object"==typeof exports?module.exports=Pace:C.startOnPageLoad&&Pace.start()}).call(this); 3 | 4 | -------------------------------------------------------------------------------- /ofcourse/static/js/ripples.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b,c,d){"use strict";function e(b,c){g=this,this.element=a(b),this.options=a.extend({},h,c),this._defaults=h,this._name=f,this.init()}var f="ripples",g=null,h={};e.prototype.init=function(){var c=this.element;c.on("mousedown touchstart",function(d){if(g.isTouch()&&"mousedown"===d.type)return!1;c.find(".ripple-wrapper").length||c.append('
');var e=c.children(".ripple-wrapper"),f=g.getRelY(e,d),h=g.getRelX(e,d);if(f||h){var i=g.getRipplesColor(c),j=a("
");j.addClass("ripple").css({left:h,top:f,"background-color":i}),e.append(j),function(){return b.getComputedStyle(j[0]).opacity}(),g.rippleOn(c,j),setTimeout(function(){g.rippleEnd(j)},500),c.on("mouseup mouseleave touchend",function(){j.data("mousedown","off"),"off"===j.data("animating")&&g.rippleOut(j)})}})},e.prototype.getNewSize=function(a,b){return Math.max(a.outerWidth(),a.outerHeight())/b.outerWidth()*2.5},e.prototype.getRelX=function(a,b){var c=a.offset();return g.isTouch()?(b=b.originalEvent,1!==b.touches.length?b.touches[0].pageX-c.left:!1):b.pageX-c.left},e.prototype.getRelY=function(a,b){var c=a.offset();return g.isTouch()?(b=b.originalEvent,1!==b.touches.length?b.touches[0].pageY-c.top:!1):b.pageY-c.top},e.prototype.getRipplesColor=function(a){var c=a.data("ripple-color")?a.data("ripple-color"):b.getComputedStyle(a[0]).color;return c},e.prototype.hasTransitionSupport=function(){var a=c.body||c.documentElement,b=a.style,e=b.transition!==d||b.WebkitTransition!==d||b.MozTransition!==d||b.MsTransition!==d||b.OTransition!==d;return e},e.prototype.isTouch=function(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)},e.prototype.rippleEnd=function(a){a.data("animating","off"),"off"===a.data("mousedown")&&g.rippleOut(a)},e.prototype.rippleOut=function(a){a.off(),g.hasTransitionSupport()?a.addClass("ripple-out"):a.animate({opacity:0},100,function(){a.trigger("transitionend")}),a.on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd",function(){a.remove()})},e.prototype.rippleOn=function(a,b){var c=g.getNewSize(a,b);g.hasTransitionSupport()?b.css({"-ms-transform":"scale("+c+")","-moz-transform":"scale("+c+")","-webkit-transform":"scale("+c+")",transform:"scale("+c+")"}).addClass("ripple-on").data("animating","on").data("mousedown","on"):b.animate({width:2*Math.max(a.outerWidth(),a.outerHeight()),height:2*Math.max(a.outerWidth(),a.outerHeight()),"margin-left":-1*Math.max(a.outerWidth(),a.outerHeight()),"margin-top":-1*Math.max(a.outerWidth(),a.outerHeight()),opacity:.2},500,function(){b.trigger("transitionend")})},a.fn.ripples=function(b){return this.each(function(){a.data(this,"plugin_"+f)||a.data(this,"plugin_"+f,new e(this,b))})}}(jQuery,window,document); 2 | //# sourceMappingURL=ripples.min.js.map -------------------------------------------------------------------------------- /ofcourse/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ryansb/ofCourse/bfaf2049a95ac877bc98b2c077622327983a2713/ofcourse/tests/__init__.py -------------------------------------------------------------------------------- /ofcourse/tests/__main__.py: -------------------------------------------------------------------------------- 1 | import test_yaml 2 | import test_new 3 | 4 | test_yaml.run_tests() 5 | test_new.run_tests() 6 | -------------------------------------------------------------------------------- /ofcourse/tests/api.py: -------------------------------------------------------------------------------- 1 | # For pathname munging 2 | import os 3 | import subprocess 4 | 5 | # The module that build_tests comes from. 6 | from gabbi import driver 7 | 8 | import ofcourse.util 9 | 10 | ofcourse.util.base_dir = '/tmp/ofcourse-tests' 11 | 12 | from ofcourse.site import app # noqa PEP8 ignore 13 | 14 | try: 15 | subprocess.check_output(['rm', '-rf', '/tmp/ofcourse-tests']) 16 | except: 17 | pass 18 | 19 | os.mkdir('/tmp/ofcourse-tests') 20 | subprocess.check_call(['ofcourse', 'new'], cwd='/tmp/ofcourse-tests') 21 | 22 | 23 | # By convention the YAML files are put in a directory named 24 | # "gabbits" that is in the same directory as the Python test file. 25 | TESTS_DIR = 'gabbits' 26 | 27 | 28 | def get_app(): 29 | return app 30 | 31 | 32 | def load_tests(loader, tests, pattern): 33 | """Provide a TestSuite to the discovery process.""" 34 | test_dir = os.path.join(os.path.dirname(__file__), TESTS_DIR) 35 | return driver.build_tests(test_dir, loader, 36 | intercept=get_app) 37 | -------------------------------------------------------------------------------- /ofcourse/tests/gabbits/status.yaml: -------------------------------------------------------------------------------- 1 | 2 | # There is an included fixture named "SkipAllFixture" which can be 3 | # used to declare that all the tests in the given file are to be 4 | # skipped. 5 | 6 | # Each test file can specify a set of defaults that will be used for 7 | # every request. This is useful for always specifying a particular 8 | # header or always requiring SSL. These values will be used on every 9 | # test in the file unless overriden. Lists and dicts are merged one 10 | # level deep, except for "data" which is copied verbatim whether it 11 | # is a string, list or dict (it can be all three). 12 | 13 | defaults: 14 | ssl: False 15 | redirects: True 16 | 17 | tests: 18 | - name: home page 19 | url: / 20 | - name: participants page 21 | url: /participants 22 | - name: lectures page 23 | url: /lectures 24 | - name: resources page 25 | url: /resources 26 | - name: syllabus page 27 | url: /syllabus 28 | - name: instructor page 29 | url: /instructor 30 | 31 | -------------------------------------------------------------------------------- /ofcourse/tests/test_new.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import shutil 3 | import tempfile 4 | import unittest 5 | 6 | 7 | class TestOfcourseNew(unittest.TestCase): 8 | def test_new(self): 9 | dir = tempfile.mkdtemp('ofcoursetest') 10 | subprocess.check_call(['ofcourse', 'new'], cwd=dir) 11 | shutil.rmtree(dir) 12 | -------------------------------------------------------------------------------- /ofcourse/tests/test_yaml.py: -------------------------------------------------------------------------------- 1 | import os 2 | import yaml 3 | import unittest 4 | 5 | from validator import Required, validate, InstanceOf 6 | 7 | 8 | class TestAllYaml(unittest.TestCase): 9 | 10 | def test_recursive_yaml(self): 11 | yaml_files = [] 12 | for root, _, fnames in os.walk(os.getcwd()): 13 | for fname in fnames: 14 | if (fname.endswith('.yaml') or fname.endswith('.yml')): 15 | yaml_files.append(os.path.join(root, fname)) 16 | 17 | for fullname in yaml_files: 18 | with open(fullname, 'r') as yfile: 19 | try: 20 | yaml.safe_load(yfile) 21 | except Exception as e: 22 | msg = "File {name} is broken: {exc}" 23 | self.fail(msg.format(name=fullname, exc=str(e))) 24 | 25 | def test_student_yaml(self): 26 | is_str = InstanceOf(type("")) 27 | spec = { 28 | 'blog': [Required, is_str], 29 | 'feed': [Required, is_str], 30 | 'forges': [Required, InstanceOf(list)], 31 | 'hw': [Required, InstanceOf(dict)], 32 | 'irc': [Required, is_str], 33 | 'name': [Required, is_str], 34 | 'rit_dce': [Required, is_str], 35 | # optional fields 36 | 'bio': [is_str], 37 | 'twitter': [is_str], 38 | 'coderwall': [is_str], 39 | } 40 | 41 | student_files = [] 42 | for root, _, fnames in os.walk( 43 | os.path.join(os.getcwd(), "people")): 44 | for fname in fnames: 45 | if (fname.endswith('.yaml') or fname.endswith('.yml')): 46 | student_files.append(os.path.join(root, fname)) 47 | 48 | for fullname in student_files: 49 | with open(fullname, 'r') as student: 50 | content = yaml.safe_load(student) 51 | validity = validate(spec, content) 52 | if not validity[0]: 53 | out = "" 54 | for k, v in validity[1].items(): 55 | out += ("File: {f} Key: {key} " 56 | "{check}\n\n".format(key=k, 57 | check=v, 58 | f=fullname) 59 | ) 60 | self.fail(out) 61 | -------------------------------------------------------------------------------- /ofcourse/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import feedparser 4 | import yaml 5 | from datetime import datetime 6 | 7 | base_dir = os.getcwd() 8 | if os.path.isdir(os.path.join(os.getcwd(), "app-root", "repo")): 9 | base_dir = os.path.join(os.getcwd(), "app-root", "repo") 10 | 11 | 12 | def app_path(*args): 13 | return os.path.join(base_dir, *args) 14 | 15 | 16 | def count_posts(feed, start_dt, end_dt=None): 17 | """ 18 | Return the number of entries on this blog feed since a 19 | start date. 20 | 21 | :param feed: the url to the RSS or Atom feed for the blog 22 | :type feed: str 23 | 24 | :param start_dt: the start date after which posts will be counted 25 | :type start_dt: datetime 26 | 27 | :returns: the count of posts in the RSS feed after the start date 28 | :rtype: int 29 | 30 | """ 31 | 32 | if end_dt is None: 33 | end_dt = datetime.now() 34 | 35 | count = 0 36 | feed = feedparser.parse(feed) 37 | for item in feed.entries: 38 | publish_time = datetime.fromtimestamp(time.mktime(item.updated_parsed)) 39 | if end_dt < publish_time or publish_time < start_dt: 40 | continue 41 | count += 1 42 | return count 43 | 44 | 45 | def get_hw_keys(): 46 | """ 47 | Return the YAML keys for homework assignments for the current 48 | term. 49 | 50 | :returns: A list of YAML keys corresponding to each HW assignment 51 | :rtype: list 52 | """ 53 | 54 | keys = [] 55 | 56 | key_file = app_path("assignments.yaml") 57 | 58 | try: 59 | with open(key_file) as key_data: 60 | keys = yaml.safe_load(key_data)['hw'] 61 | except IOError: 62 | print("Error: File missing!" + key_file) 63 | return keys 64 | -------------------------------------------------------------------------------- /ofcourse/version.py: -------------------------------------------------------------------------------- 1 | """ 2 | Author: Ryan Brown 3 | License: Apache 2.0 4 | """ 5 | 6 | import pkg_resources 7 | 8 | __version__ = pkg_resources.get_distribution('ofcourse').version 9 | -------------------------------------------------------------------------------- /ofcourse/yamls/assignments.yaml: -------------------------------------------------------------------------------- 1 | # Example ofCourse assignments.yaml 2 | # Homework keys should be added as YAML 3 | # list elements. 4 | hw: 5 | - firstflight 6 | -------------------------------------------------------------------------------- /ofcourse/yamls/fake_student.yaml: -------------------------------------------------------------------------------- 1 | # The name does *not* need to be your full legal name, but will be used as your 2 | # display name throughout the class web site. It can be a nickname or just your 3 | # first name if you like. 4 | name: Thunder Dome 5 | blog: http://wordpress.com 6 | feed: http://en.blog.wordpress.com/feed/ 7 | forges: 8 | - http://fsf.org 9 | irc: Thunderdome 10 | rit_dce: abc123 11 | hw: 12 | firstflight: http://exampleblog.post 13 | -------------------------------------------------------------------------------- /ofcourse/yamls/oer.yaml: -------------------------------------------------------------------------------- 1 | # Resource Links: 2 | # Underscores will be replaced with spaces 3 | 4 | videos: 5 | Linus_Torvalds_Git_Talk: "http://www.youtube.com/watch?v=4XpnKHJAok8" 6 | #challenges: 7 | # How_To_Use_Bash: "http://lmgtfy.com/?q=how+to+use+bash" 8 | #decks: 9 | # Ryan_Scott_Brown_Git_Talk: "http://rsb.io/talks/git/" 10 | -------------------------------------------------------------------------------- /ofcourse/yamls/site.yaml: -------------------------------------------------------------------------------- 1 | course: 2 | name: HFOSS 3 | desc: The RIT Humanitarian Free/Open Source Software Development Course 4 | place: RIT 5 | start: 2014-09-25 6 | end: 2014-12-10 7 | times: Tues & Thurs 6:30PM - 7:45PM 8 | course: 4085.582.01 9 | location: Orange Hall (ORN)-1380 10 | repo: https://github.com/decause/hfoss 11 | # Optional if using OpenShift public_url: http://hfoss-ritigm.rhcloud.com 12 | 13 | instructor: 14 | name: Remy DeCausemaker 15 | email: remydcsi@rit.edu 16 | irc: decause 17 | avatar: http://www.libravatar.org/user/5d9d0a14af042b2f7877ab58de9b7702ca29bb9d34155dc58eff4ff6509d562c.png 18 | openhub: decause 19 | coderwall: decause 20 | twitter: Remy_D 21 | office: 22 | location: The MAGIC Center - SIH 1680 23 | hours: Monday, Wednesday, 4:00-5:00pm 24 | links: 25 | blog: http://foss.rit.edu/blog/decause 26 | linkedin: http://linkedin.com/in/decause 27 | github: http://github.com/decause 28 | opensource: http://opensource.com/users/remyd 29 | 30 | assistant: 31 | name: Thunderdome 32 | email: Thunderdome@foss.rit.edu 33 | irc: Thunderdome 34 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Click>=3.1 2 | Flask-Mako>=0.3 3 | Flask>=0.10.1 4 | Frozen-Flask>=0.11 5 | Mako>=1.0.0 6 | PyYAML>=3.11 7 | dulwich>=0.9.0,<0.11.0 8 | feedparser>=5.1.3 9 | icalendar>=3.9.1 10 | oshift>=0.2.3 11 | six>=1.7.3 12 | tornado>=4.0.1 13 | validator.py>=1.2.0 14 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = ofcourse 3 | author = Remy DeCausemaker 4 | author-email = remyd@civx.us 5 | summary = Python courseware with Flask on OpenShift 6 | description-file = README.md 7 | license = ASL2.0 8 | classifier = 9 | Natural Language :: English 10 | Operating System :: POSIX :: Linux 11 | Programming Language :: Python :: 2.7 12 | keywords = 13 | courseware 14 | ofcourse 15 | hflossk 16 | [files] 17 | packages = 18 | ofcourse 19 | ofcourse-templates 20 | [entry_points] 21 | console_scripts = 22 | ofcourse = ofcourse.cli:cli 23 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python 2 | # -*- coding: utf8 -*- 3 | 4 | from setuptools import setup 5 | 6 | setup( 7 | setup_requires=['pbr'], 8 | pbr=True, 9 | ) 10 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | tox 2 | nose>=1.3.3 3 | pep8 4 | coverage>=3.6 5 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 1.6 3 | envlist = py27,pep8 4 | 5 | [testenv] 6 | commands = nosetests 7 | setenv = VIRTUAL_ENV={envdir} 8 | PYTHONHASHSEED=0 9 | LC_ALL=C.UTF-8 10 | LANG=C.UTF-8 11 | deps = -r{toxinidir}/requirements.txt 12 | -r{toxinidir}/test-requirements.txt 13 | 14 | [testenv:pep8] 15 | commands = pep8 16 | 17 | [testenv:venv] 18 | commands = {posargs} 19 | 20 | [testenv:cover] 21 | commands = python setup.py test --coverage --testr-args='{posargs}' 22 | 23 | [pep8] 24 | ignore = H402, 25 | show-source = True 26 | exclude = .venv,.git,.tox,dist,docs,*egg,build,*setup.py 27 | --------------------------------------------------------------------------------