5737481c5365e98e2459d30c1100ea201bcb44f3
[oweals/karmaworld.git] / README.md
1 # KarmaWorld
2 __Description__: A django application for sharing and uploading class notes.
3
4 __Copyright__: FinalsClub, a 501c3 non-profit organization
5
6 __License__: GPLv3 except where otherwise noted
7
8 __Contact__: info@karmanotes.org
9
10 v3.0 of the karmanotes.org website from the FinalsClub Foundation
11
12 # Purpose
13
14 KarmaNotes is an online database of college lecture notes.  KarmaNotes empowers college students to participate in the free exchange of knowledge. 
15
16 # Pre-Installation
17
18 ## Code
19
20 Before doing anything, you'll need the code. Grab it from github.
21
22 Clone the project from the central repo using your github account:
23
24     git clone git@github.com:FinalsClub/karmaworld.git
25
26 If you aren't using a system setup for github, then grab the project with
27 this command instead:
28
29     git clone https://github.com/FinalsClub/karmaworld.git
30
31 Generally speaking, this will create a subdirectory called `karmaworld` under
32 the directory where the `git` command was run. This git repository directory
33 will be referred to herein as `{project_root}`.
34
35 There might be some confusion as the git repository's directory will likely be
36 called `karmaworld` (this is `{project_root}`), but there is also a `karmaworld`
37 directory underneath that (`{project_root}/karmaworld`) alongside files like
38 `fabfile.py` (`{project_root}/fabfile.py`) and `README.md`
39 (`{project_root}/README.md`).
40
41 ## External Service Dependencies
42
43 Notice: This software makes use of external third party services which require
44 accounts to access the service APIs. Without these third parties available,
45 this software may require considerable overhaul.
46
47 ### Filepicker
48 This software uses [Filepicker.io](https://www.inkfilepicker.com/) for uploading
49 files. This requires an account with Filepicker.
50
51 Filepicker requires an additional third party file hosting site where it may
52 send uploaded files. For this project, we have used Amazon S3.
53
54 Filepicker will provide an API key. This is needed by the software.
55
56 ### Amazon S3
57
58 #### for Filepicker
59 This software uses [Amazon S3](http://aws.amazon.com/s3/) as a third party file
60 hosting site. The primary use case is a destination for Filepicker files. The
61 software won't directly need any S3 information for this use case; it will be
62 provided directly to Filepicker.
63
64 #### for Static File hosting
65 A secondary use case for S3 is hosting static files. The software will need to
66 update static files on the S3 bucket. In this case, the software will need the
67 S3 bucket name, access key, and secret key.
68
69 The code assumes S3 is used for static files in a production environment. To
70 obviate the need for hosting static files through S3 (noting that it still might
71 be necessary for Filepicker), a workaround was explained [in this Github ticket](https://github.com/FinalsClub/karmaworld/issues/192#issuecomment-30193617).
72
73 That workaround is repeated here. Make the following changes to
74 `{project_root}/karmaworld/settings/prod.py`:
75
76 1. comment out everything about static_s3 from imports
77 2. comment out storages from the `INSTALLED_APPS`
78 3. change `STATIC_URL` to `'/assets/'`
79 4. comment out the entire storages section (save for part of `INSTALLED_APPS` and `STATIC_URL`)
80 5. add this to the nginx config:
81
82     location /assets/ {
83         root /var/www/karmaworld/karmaworld/;
84     }
85     
86 ### IndexDen
87 KarmaNotes uses IndexDen to create a searchable index of all the notes
88 in the system. Create an free IndexDen account at [their homepage](http://indexden.com/).
89 You will be given a private URL that accesses your IndexDen account.
90 Create a file at karmaworld/secret/indexden.py, and enter your private URL, and the name
91 of the index you want KarmaNotes to use. The index will be created automatically when
92 KarmaNotes is run if it doesn't already exist. For example,
93 ```
94 PRIVATE_URL = 'http://:secretsecret@secret.api.indexden.com'
95 INDEX = 'karmanotes_something_something'
96 ```
97
98 ### Google Drive
99 This software uses [Google Drive](https://developers.google.com/drive/) to
100 convert documents to and from various file formats.
101
102 A Google Drive service account with access to the Google Drive is required. Thismay be done with a Google Apps account with administrative privileges, or ask
103 your business sysadmin.
104
105 These are the instructions to create a Google Drive service account:
106 https://developers.google.com/drive/delegation
107
108 When completed, you'll have a file called `client_secrets.json` and a p12 file
109 which is the key to access the service account. Both are needed by the software.
110
111 ### Twitter
112
113 Twitter is used to post updates about new courses. Access to the Twitter API
114 will be required for this task.
115
116 If this Twitter feature is desired, the consumer key and secret as well as the
117 access token key and secret are needed by the software.
118
119 If the required files are not found, then no errors will occur.
120
121 To set this up, create a new Twitter application at https://dev.twitter.com/apps/new.
122 Make sure this application has read/write access. Generate an access token. Go to your
123 OAuth settings, and grab the "Consumer key", "Consumer secret", "Access token", and
124 "Access token secret".
125
126 Create a file at karmaworld/secret/twitter.py, and enter these tokens. For example,
127 ```
128 CONSUMER_KEY = '???'
129 CONSUMER_SECRET = '???'
130 ACCESS_TOKEN_KEY = '???'
131 ACCESS_TOKEN_SECRET = '???'
132 ```
133
134 ### SSL Certificate
135
136 If you wish to host your system publicly, you'll need an SSL certificate
137 signed by a proper authority.
138
139 If you are working on local system for development, a self signed certificate
140 will suffice. There are plenty of resources available for learning how to
141 create one, so that will not be detailed here. Note that the Vagrant file will
142 automatically generated a self signed certificate within the virtual machine.
143
144 The certificate should be installed using nginx.
145
146 # Development Install
147
148 If you need to setup the project for development, it is highly recommend that
149 you grab create a development virtual machine or (if available) grab one that
150 has already been created for your site.
151
152 The *host machine* is the system which runs e.g. VirtualBox, while the
153 *virtual machine* refers to the system running inside e.g. VirtualBox. 
154
155 ## Creating a Virtual Machine by hand
156
157 Create a virtual machine with your favorite VM software. Configure the virtual
158 machine for production with the steps shown in the [Production Install](#production-install) section.
159
160 ## Creating a Virtual Machine with Vagrant
161
162 Vagrant supports a variety of virtual machine software and there is additional
163 support for Vagrant to deploy to a wider variety. However, for these
164 instructions, it is assumed Vagrant will be deployed to VirtualBox.
165
166 1. Configure external dependencies on the host machine:
167    * Under `{project_root}/karmaworld/secret/`:
168         1. Copy files with the example extension to the corresponding filename
169           without the example extension (e.g.
170           `cp filepicker.py.example filepicker.py`)
171         1. Modify those files, but ignore `db_settings.py` (Vagrant takes care of that one)
172         1. Copy the Google Drive service account p12 file to `drive.p12`
173            (this filename and location may be changed in `drive.py`)
174         1. Ensure `*.py` in `secret/` are never added to the git repo.
175            (.gitignore should help warn against taking this action)
176
177 1. Install [VirtualBox](http://www.virtualbox.org/)
178
179 1. Install [vagrant](http://www.vagrantup.com/) 1.3 or higher
180
181 1. Use Vagrant to create the virtual machine.
182    * While in `cd {project_root}`, type `vagrant up`
183
184 1. Connect to the virtual machine with `vagrant ssh`
185
186 Note:
187 Port 443 of the virtual machine will be configured as port 6659 on the host
188 system. While on the host system, fire up your favorite browser and point it at
189 `https://localhost:6659/`. This connects to your host system on port 6659, which
190 forwards to your virtual machine's web site using SSL.
191
192 Port 80 of the virtual machine will be configured as port 16659 on the host
193 system. While on the host system, fire up your favorite browser and point it at
194 `http://localhost:16659/`. This connects to your host system on port 16659,
195 which forwards to your virtual machine's web site using plain text.
196
197 ## Completing the Virtual Machine with Fabric
198
199 *Notice* Fabric might not run properly if you presently in a virtualenv.
200 `deactivate` prior to running fab commands.
201
202 ### From the Host Machine
203
204 If Fabric is available on the host machine, you should be able to run Fabric
205 commands directly on the host machine, pointed at the virtual machine. If
206 Fabric is not available on the Host Machine, see the next section.
207
208 To setup the host machine properly, see the section about
209 [accessing the VM via fabric](#accessing-the-vm-via-fabric) and then return to
210 this section.
211
212 Assuming those steps were followed with the alias, the following instructions
213 should complete the virtual machine setup:
214
215 1. `cd {project_root}` on the host machine.
216
217 1. type `vmfab first_deploy`.
218
219 ### From within the Virtual Machine
220
221 If Fabric is not available on the host machine, or just for funsies, you may
222 run the Fabric commands within the virtual machine.
223
224 1. Connect to the virtual machine with `vagrant ssh`.
225
226 1. On the virtual machine, type `cd karmanotes` to get into the code
227    repository.
228
229 1. In the code repo of the VM, type `fab -H 127.0.0.1 first_deploy`
230
231    During this process, you will be queried to create a Django site admin.
232    Provide information. You will be asked to remove duplicate schools. Respond
233    with yes.
234
235 # Production Install
236
237 These steps are taken care of by automatic utilities. Vagrant performs the
238 first subsection of these instructions and Fabric performs the second
239 subsection. These instructions are detailed here for good measure, but should
240 not generally be needed.
241
242 1. Ensure the following are installed:
243    * `git`
244    * `7zip` (for unzipping US Department of Education files)
245    * `PostgreSQL` (server and client)
246    * `nginx`
247    * `libxslt` and `libxml2` (used by some Python libraries)
248    * `RabbitMQ` (server)
249    * `memcached`
250    * `Python`
251    * `PIP`
252    * `virtualenv`
253    * `virtualenvwrapper` (might not be needed anymore)
254    * `pdf2htmlEX`
255
256    On a Debian system supporting Apt, this can be done with:
257 ```
258     sudo apt-get install python-pip postgresql python-virtualenv nginx \
259     virtualenvwrapper git libxml2-dev p7zip-full \
260     postgresql-server-dev-9.1 libxslt1-dev \
261     libmemcached-dev python-dev rabbitmq-server \
262     cmake libpng-dev libjpeg-dev libgtk2.0-dev \
263     pkg-config libfontconfig1-dev autoconf libtool
264
265     wget http://poppler.freedesktop.org/poppler-0.24.4.tar.xz
266     tar xf poppler-0.24.4.tar.xz
267     cd poppler-0.24.4
268     ./configure --prefix=/usr --enable-xpdf-headers
269     make
270     sudo make install
271     cd ~/
272
273     git clone https://github.com/fontforge/fontforge.git
274     cd fontforge
275     ./bootstrap
276     ./configure --prefix=/usr
277     make
278     sudo make install
279     cd ~/
280
281     git clone https://github.com/charlesconnell/pdf2htmlEX.git
282     cd pdf2htmlEX
283     ./configure --prefix=/usr
284     cmake .
285     make
286     sudo make install
287 ```
288
289 1. Generate a PostgreSQL database and a role with read/write permissions.
290    * For Debian, these instructions are helpful: https://wiki.debian.org/PostgreSql
291
292 1. Modify configuration files.
293    * There are settings in `{project_root}/karmaworld/settings/prod.py`
294        * Most of the setting should work fine by default.
295    * There are additional configuration options for external dependencies
296      under `{project_root}/karmaworld/secret/`.
297         1. Copy files with the example extension to the corresponding filename
298           without the example extension (e.g.
299           `cp filepicker.py.example filepicker.py`)
300         1. Modify those files.
301            * Ensure `PROD_DB_USERNAME`, `PROD_DB_PASSWORD`, and `PROD_DB_NAME`
302              inside `db_settings.py` match the role, password, and database
303              generated in the previous step.
304         1. Copy the Google Drive service account p12 file to `drive.p12`
305            (this filename and location may be changed in `drive.py`)
306         1. Ensure `*.py` in `secret/` are never added to the git repo.
307            (.gitignore should help warn against taking this action)
308
309 1. Make sure that /var/www exists, is owned by the www-data group, and that
310    the desired user is a member of the www-data group.
311
312 1. Configure nginx with a `proxy_pass` to port 8000 (or whatever port gunicorn
313    will be running the site on) and any virtual hosting that is desired.
314    Here is an example server file to put into `/etc/nginx/sites-available/`
315
316         server {
317             listen 80;
318             server_name localhost;
319             return 301 https://$host$request_uri;
320         }
321
322         server {
323             listen 443;
324             ssl on;
325             server_name localhost;
326             client_max_body_size 20M;
327         
328             location / {
329                 # pass traffic through to gunicorn
330                 proxy_pass http://127.0.0.1:8000;
331                 # pass HTTP(S) status through to Django
332                 proxy_set_header X-Forwarded-SSL $https;
333                 proxy_set_header X-Forwarded-Protocol $scheme;
334                 proxy_set_header X-Forwarded-Proto $scheme;
335                 # pass nginx site back to Django
336                 proxy_set_header Host $http_host;
337             }
338         }
339
340 1. Configure the system to start supervisor on boot. An init script for
341    supervisor is in the repo at `{project_root}/karmaworld/confs/supervisor`.
342    `update-rc.d supervisor defaults` is the Debian command to load the init
343    script into the correct directories.
344
345 1. Make sure `{project_root)/var/log` and `{project_root}/var/run` exist and
346    may be written to, or else put the desired logging and run file paths into
347    `{project_root}/confs/prod/supervisord.conf`
348
349 1. Create a virtualenv under `/var/www/karmaworld/venv`
350
351 1. Change into the virtualenv with `. /var/www/karmaworld/venv/bin/activate`.
352    Within the virtualenv:
353
354     1. Update the Python depenencies with `pip -i {project_root}/reqs/prod.txt`
355         * If you want debugging on a production-like system:
356             1. run `pip -i {project_root}/reqs/vmdev.txt`
357             1. change `{project_root}/manage.py` to point at `vmdev.py`
358                instead of `prod.py`
359             1. ensure firefox is installed on the system (such as by
360                `sudo apt-get install firefox`)
361     
362     1. Setup the database with `python {project_root}/manage.py syncdb --migrate`
363
364     1. Collect static resources and put them in the static hosting location with
365        `python {project_root}/manage.py collect_static`
366
367 1. The database needs to be populated with schools. A list of accredited schools
368    may be found on the US Department of Education website:
369    http://ope.ed.gov/accreditation/GetDownloadFile.aspx
370
371    Alternatively, use the built-in scripts while in the virtualenv:
372
373    1. Fetch USDE schools with
374       `python {project_root}/manage.py fetch_usde_csv ./schools.csv`
375
376    1. Upload the schools into the database with
377       `python {project_root}/manage.py import_usde _csv ./schools.csv`
378
379    1. Clean up redundant information with
380       `python {project_root}/manage.py sanitize_usde_schools`
381
382 1. Startup `supervisor`, which will run `celery` and `gunicorn`. This may be
383    done from within the virtualenv by typing
384    `python {project_root}/manage.py start_supervisord`
385
386 1. If everything went well, gunicorn should be running the website on port 8000
387    and nginx should be serving gunicorn on port 80.
388
389 # Update a deployed system
390
391 Once code has been updated, the running web service will need to be updated
392 to stay in sync with the code.
393
394 ## Fabric
395
396 Run the `deploy` fab command. For example:
397 `fab -H 127.0.0.1 deploy`
398
399 ## By Hand
400
401 1. pull code in from the repo with `git pull`
402 1. If any Python requirements have changed, install/upgrade them:
403     `pip install -r --upgrade reqs/prod.txt`
404 1. If the database has changed, update the database with:
405     `python manage.py syncdb --migrate`
406 1. If any static files have changed, synchornize them with;
407     `python manage.py collectstatic`
408 1. Django will probably need a restart.
409     * For a dev system, ctrl-c the running process and restart it.
410     * For a production system, there are two options.
411         * `python manage.py restart_supervisord` if far reaching changes
412           have been made (that might effect celery, beat, etc)
413         * `python manage.py restart_gunicorn` if only minor Django changes
414           have been made
415         * If you are uncertain, best bet is to restart supervisord.
416
417 # Accessing the Vagrant Virtual Machine
418
419 ## Accessing the VM via Fabric
420 If you have Fabric on the host machine, you can configure your host machine
421 to run Fabric against the virtual machine.
422
423 You will need to setup the host machine with the proper SSH credentials to
424 access the virtual machine. This is done by running `vagrant ssh-config` from
425 `{project_root}` and copying the results into your SSH configuration file
426 (usually found at `~/.ssh/config`).
427
428 The VM will, by default, route its SSH connection through localhost port 2222
429 on the host machine and the base user with be vagrant. Point Fabric there when
430 running fab commands from `{project_root}`. So the command will look like this:
431
432         fab -H 127.0.0.1 --port=2222 -u vagrant <commands>
433
434 In unix, it might be convenient to create and use an alias like so:
435
436         alias vmfab='fab -H 127.0.0.1 --port=2222 -u vagrant'
437         vmfab <commands>
438
439 Removing a unix alias is done with `unalias`.
440
441 ## Connecting to the VM via SSH
442 If you have installed a virtual machine using `vagrant up`, you can connect
443 to it by running `vagrant ssh` from `{project_root}`.
444
445 ## Connecting to the development website on the VM
446 To access the website running on the VM, point your browser at
447 http://localhost:6659/ using your host computer.
448
449 Port 6659 on your local machine is set to forward to the VM's port 80.
450
451 Fun fact: 6659 was chosen because of OM (sanskrit) and KW (KarmaWorld) on a
452 phone: 66 59.
453
454 ## Updating the VM code repository
455 Once connected to the virtual machine by SSH, you will see `karmaworld` in
456 the home directory. That is the `{project_root}` in the virtual machine.
457
458 `cd karmaworld` and then use `git fetch; git merge` and/or `git pull origin` as
459 desired.
460
461 The virtual machine's code repository is set to use your host machine's
462 local repository as the origin. So if you make changes locally and commit them,
463 without pushing them anywhere, your VM can pull those changes in for testing.
464
465 This may seem like duplication. It is. The duplication allows your host machine
466 to maintain git credentials and manage repository access control so that your
467 virtual machine doesn't need sensitive information. Your virtual machine simply
468 pulls from the local repository on your local file system without needing
469 credentials, etc.
470
471 ## Deleting the Virtual Machine
472 If you want to start a fresh virtual machine or simply remove the virtual
473 machine from your hard drive, Vagrant has a command for that. While in 
474 `{project_root}` of the host system, type `vagrant destroy` and confirm with
475 `y`. This will remove the VM from your hard drive.
476
477 If you wanted a fresh VM, the next step is to run `vagrant up`, which will
478 start a brand new VM (since the old one is gone).
479
480 ## Other Vagrant commands
481 Please see [vagrant documentation](http://docs.vagrantup.com/v2/cli/index.html)
482 for more information on how to use the vagrant CLI to manage your development
483 VM.
484
485 # Django Database management
486
487 ## South
488
489 We have setup Django to use
490 [south](http://south.aeracode.org/wiki/QuickStartGuide) for migrations. When
491 changing models, it is important to run
492 `python {project_root}/manage.py schemamigration` which will create a migration
493  to reflect the model changes into the database. These changes can be pulled
494 into the database with `python {project_root}/manage.py migrate`.
495
496 Sometimes the database already has a migration performed on it, but that
497 information wasn't told to south. There are subtleties to the process which
498 require looking at the south docs. As a tip, start by looking at the `--fake`
499 flag.
500
501 # Assets from Third Parties
502
503 A number of assets have been added to the repository which come from external
504 sources. It would be difficult to keep a complete list in this README and keep
505 it up to date. Software which originally came from outside parties can
506 generally be found in `{project_root}/karmaworld/assets`.
507
508 Additionally, all third party Python projects (downloaded and installed with
509 pip) are listed in these files:
510
511 * `{project_root}/reqs/common.txt`
512 * `{project_root}/reqs/dev.txt`
513 * `{project_root}/reqs/prod.txt`
514 * `{project_root}/reqs/vmdev.txt` (just a combo of dev.txt and prod.txt)
515
516 # Thanks
517
518 * KarmaNotes.org is a project of the FinalsClub Foundation with generous funding from the William and Flora Hewlett Foundation
519
520 * Also thanks to [rdegges](https://github.com/rdegges/django-skel) for the django-skel template