Skip to content

Commit a79aa8a

Browse files
Merge branch 'main' into cc-payment-display
2 parents 0bb47a6 + a17a307 commit a79aa8a

File tree

168 files changed

+4302
-3797
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+4302
-3797
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ before_install:
99
install:
1010
- sudo apt-get install -y $(cat esp/packages_base.txt | grep -v ^memcached | grep -v ^postgres) -qq
1111
- esp/packages_base_manual_install.sh
12-
- (pip install -r esp/requirements.txt --use-mirrors || tail pip.log 1>&2 2>/dev/null) | tee pip.log | grep '^Downloading/unpacking'
12+
- pip install -r esp/requirements.txt --use-mirrors -q --log pip.log || (tail pip.log && exit 1)
1313
before_script:
1414
- cp esp/esp/local_settings.py.travis esp/esp/local_settings.py
1515
- ln -s `pwd`/esp/public/media/default_images esp/public/media/images

docs/dev/contributing.rst

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
Workflow
2+
========
3+
4+
Recommended one-time setup
5+
--------------------------
6+
7+
Remove ``--global`` if you don't want it to apply to other git repos on your computer: ::
8+
9+
git config --global user.name "Your Name"
10+
git config --global user.email [email protected]
11+
12+
Other git config you might find useful: ::
13+
14+
git config --global push.default simple # makes git only push the current branch, which is useful for not accidentally messing things up
15+
git config --global color.{ui,status,branch,diff,interactive,grep} auto # makes various interfaces usefully colorful
16+
git config --global log.decorate true # shows branch information in `git log` by default
17+
git config --global core.pager "less -R"
18+
git config --global pager.{status,branch,diff,show,log} true
19+
git config --global color.pager true
20+
git config --global core.editor vim # or emacs, nano, your favorite text editor, etc.
21+
git config --global grep.lineNumber true
22+
23+
For normal, non-urgent features and bug fixes
24+
---------------------------------------------
25+
26+
The following workflow applies if you've already been added as a collaborator to the repository. If not, you should fork it using the button on Github, add it as a remote, and replace all of the ``git push``/``git push origin`` steps with ``git push remote-name``.
27+
28+
From the directory ``/esp``: ::
29+
30+
git checkout main
31+
git pull
32+
./update_deps.sh # on vagrant: see vagrant docs; no need to bother if deps haven’t changed
33+
./manage.py update # on vagrant: fab manage:cmd=update
34+
git checkout -b new-branch-name
35+
36+
Write some code!
37+
Test your code!
38+
39+
Look at what you’ve changed (``git status`` and/or ``git diff``), and then run ``git commit -a``, and type a commit message (see `<http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>` for good commit message style). Repeat a few times from “write some code!” if you want to make multiple commits.
40+
41+
When you’re ready to make a pull request, or want other people to be able to pull your code: ::
42+
43+
git push --set-upstream origin new-branch-name
44+
45+
Now go to `<https://github.com/learning-unlimited/ESP-Website>`_. If you’re logged in, you should see a banner near the top suggesting that you make a pull request. Click the button, write up a summary of your pull request, and submit it.
46+
47+
Now wait for someone to review your pull request. If it takes a long time, poke a friend to review it.
48+
49+
When someone reviews your pull request, they will probably have some comments. If they have a lot to say, or suggest some major changes, don’t feel bad! This is a normal part of the code review process. Now you need to address their comments. If you disagree with what they’ve said, or want to discuss more, feel free to do that on the pull request. To change your code: ::
50+
51+
git checkout new-branch-name # only if you’d switched to another branch while waiting
52+
Make some changes
53+
Test your changes
54+
git commit -a -m "Fixed foo, bar, and baz"
55+
git push
56+
57+
The reviewer will look at your changes. In some cases, you might go through a few more cycles of this. Once everything is resolved, they’ll merge your pull request. Congratulations!
58+
59+
For urgent features and fixes
60+
-----------------------------
61+
62+
The following is MIT-specific, although by using a different prod branch a similar thing might work for other sites. ::
63+
64+
git pull
65+
git checkout $(git merge-base main mit-prod)
66+
git checkout -b urgent-branch-name
67+
68+
Write some code!
69+
Test your code! Be careful, since you’re putting this on our live server without full review. ::
70+
71+
git commit -a -m "Did something important"
72+
git push --set-upstream origin urgent-branch-name
73+
74+
git checkout mit-prod
75+
git merge urgent-branch-name
76+
git push
77+
78+
Pull code as described in `<https://esp.mit.edu/wiki/index.php/The_Server#Pulling_New_Code>`_.
79+
80+
Make a pull request for ``urgent-branch-name`` as described above

docs/dev/directory_structure.rst

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Directory Structure
2+
===================
3+
4+
This is not intended to be an exhaustive list, just a summary with some comments, to help new devs find things. Starting in the root of the repository:
5+
6+
* ``docs``: Fairly new and therefore very incomplete; add to it!
7+
8+
* ``admin``
9+
* ``dev``
10+
11+
* ``esp``: The root of the django project.
12+
13+
* ``esp``: The root of the python code. Every folder from here on down needs to have an ``__init__.py``. Contains a directory for each django app we use, along with some other files and directories. There are a whole lot of these, but the most important are below. Each app contains a file or directory for models, views, and tests; most also contain a directory for migrations.
14+
15+
* ``accounting``: The website’s accounting system (``accounting_core`` and ``accounting_docs`` are old and deprecated).
16+
* ``program``: This app contains most of the site’s logic. For the most part it’s a fairly normal Django app, but it also contains all the program modules.
17+
18+
* ``models``: The main models for ``Program``, ``ClassSubject``, ``ClassSection``; many of the other models used by programs live in other apps, such as ``resources``.
19+
* ``modules``: This directory contains all the program modules. The python code for the individual modules is in ``handlers/<name>module.py``; most of the remainder of the directory won’t be relevant to you. See `<program_modules.rst>`_ for details on the program module system.
20+
21+
* ``qsd``: This app contains all of the code for QSD (which is the website’s system for admin-editable "quasi-static" text).
22+
* ``users``: This app contains important models like ``ESPUser``, ``Permission``, ``Record``, ``ContactInfo``, and so on.
23+
* ``*_settings.py``: Various files in which our django settings are kept. ``django_settings.py`` contains settings used by all sites; ``local_settings.py`` is generated by the setup script and contains site-specific settings.
24+
* ``urls.py``: Master URL routing table.
25+
26+
* ``manage.py``: Runs django commands like ``./manage.py shell_plus``.
27+
* ``public``: Contains things that will be served statically.
28+
* ``media``: Includes our javascript, CSS, images, etc. Served statically at ``/media/``, so ``/esp/public/media/scripts/foo.js`` would be served at ``doma.in/media/scripts/foo.js``.
29+
* ``templates``: Contains our django templates, in a directory structure roughly mirroring that of ``/esp/esp``.
30+
* ``useful_scripts``: Contains various scripts intended to be run by devs, of varying usefulness, which may be site-specific, terribly-coded, completely broken, etc.

docs/dev/models.png

2.81 MB
Loading

docs/dev/useful_models.rst

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Commonly Used Models
2+
====================
3+
Since `this picture <models.png>`_ may not be the most useful way to figure out what model does what you want, here’s a list of the most commonly used ones:
4+
5+
* ``esp.users.models.ESPUser``: This is a proxy model over ``django.contrib.auth.User`` (the standard user account model). Proxy model basically means it attaches extra methods, but not extra fields.
6+
* ``django.contrib.auth.models.Group``: Keeps track of types of users, like Student and Teacher.
7+
* ``esp.users.models.Permission``: Permissions for various parts of the website; can be applied to a particular ESPUser or Group.
8+
* ``esp.users.models.{StudentInfo,TeacherInfo,ContactInfo}``: Various types of extra information associated with various types of users.
9+
* ``esp.program.models.Program``: An instance of a program, like Splash Fall 2014.
10+
* ``esp.program.models.class_.ClassSubject``: A class, like "X9002: Steak: Theory and Practice".
11+
* ``esp.program.models.class_.ClassSection``: A section of a class, like X9002s1.
12+
* ``esp.program.models.RegistrationProfile``: An instance of the profile information that students fill out before registering for a program.
13+
* ``esp.program.models.StudentRegistration``: A student’s registration for or interest in a ClassSection, like "benkraft marked X9002s1 as Priority/1".
14+
* ``esp.program.models.StudentSubjectInterest``: A student’s interest in a ClassSubject (correspond to stars in the two-phase lottery).
15+
* ``esp.cal.models.Event``: A timeblock (or sometimes another timed event such as a teacher training), associated with a program.
16+
* ``esp.resources.models.Resource``: A resource, like a classroom or the LCD projector in a classroom. Note``: the resources models are a bit of a mess, and we’re working on rewriting them.
17+
* ``esp.accounting.models.LineItemType``: A type of payment, such as "Saturday Lunch for Splash Fall 2014 ($5)"
18+
* ``esp.tagdict.models.Tag``: A generic key/value pair, optionally associated to another object in the database (most commonly a Program). Commonly used for settings.

docs/dev/utils.rst

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Other Utilities and Customizations
2+
==================================
3+
4+
The ESP website has a bunch of other things in it that are useful to know. Here are some of them:
5+
6+
Rendering and Templates
7+
-----------------------
8+
* We use a modified ``esp.web.util.main.render_to_response`` instead of django's; it takes the template path, the request, and the context, and inserts useful things into the context, like information needed to render navbars, and the request.
9+
* To render a block of text that will be editable by admins, put a ``{% load render_qsd %}`` somewhere, and then put a ``{% render_inline_qsd name %}`` or ``{% render_inline_program_qsd program name %}`` in where you want the block. If you want it to have default text, put it between the tags ``{% inline_qsd_block name %}`` and ``{% end_inline_qsd_block %}`` or ``{% inline_program_qsd_block program name %}`` and ``{% end_inline_program_qsd_block %}``.
10+
* You almost always want your template to begin with ``{% extends "main.html" %}``, to inherit from the main site layout; then put the content between ``{% block content %}`` and ``{% endblock content %}`` tags. Even if you don't want the main site layout, you may want to inherit ``{% extends "elements/html" %}`` to get our standard JS and CSS and such.
11+
* We have a model in the database, ``esp.utils.TemplateOverride``, that makes it possible to override a template on a particular site by saving a new one in the database. It's terrifying but also kind of useful. The theme editor uses these, and we often use them to customize printables.
12+
13+
Caching
14+
-------
15+
* We have a caching library, ``esp.cache``, which does a lot of magical and terrifying things to make the website not have to recompute data all the time.
16+
* You can cache any function by putting an ``@cache_function`` decorator on top. The hard part is of caching is making sure it always gets expired correctly, when any data that it depended on changes. You do this by, after the function, putting ``function_name.depend_on_row()`` or similar calls, to make it depend on certain rows of the database. The syntax is powerful but long, and probably best learned by looking at examples.
17+
* Don't cache things unless you’re sure you're expiring the cache at the correct times!
18+
* More details and the internals of the caching library are described in `<cache.rst>`_.
19+
20+
Testing
21+
-------
22+
* You should always test features manually on your dev server before you push them to a server. Seriously, do it.
23+
* We use the `Django Debug Toolbar <//django-debug-toolbar.readthedocs.org>`_, which will show up automatically on the right of all pages on a dev server. It has useful things to tell you how slow your page is, and help debug errors.
24+
* We also have automated tests; they don't cover as much of the codebase as we'd like, but they do exist. They get automatically run by Travis when you push to Github; you can also run some locally, although you probably don't want to run the whole test suite locally. You should write more tests! See the Django testing docs for details on how.

esp/esp/accounting/migrations/0002_accounting_data.py

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from esp.accounting.controllers import GlobalAccountingController, ProgramAccountingController
88
from esp.program.models import Program
9-
from esp.accounting_core.models import Transaction, LineItem
109

1110
class Migration(SchemaMigration):
1211

esp/esp/accounting/migrations/0005_import_data.py

+2-150
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from django.db import models
66
from django import db as django_db
77

8-
from esp.accounting_docs.models import Document
98
from esp.program.models import Program
109
from esp.users.models import ESPUser
1110

@@ -15,154 +14,7 @@
1514

1615
def migrate_program(program):
1716

18-
docs = Document.objects.filter(anchor=program.anchor, doctype=2)
19-
num_uncategorized = 0
20-
num = 0
21-
found_for_program = False
22-
student_dict = {}
23-
24-
# Clear financial data for this program
25-
pac = ProgramAccountingController(program)
26-
pac.clear_all_data()
27-
lineitem_types = {'one': {}, 'multi': {}, 'select': []}
28-
lineitem_choices = {}
29-
30-
# Build a database of each student's financial transactions for the program
31-
for doc in docs:
32-
student_id = doc.user.id
33-
34-
if student_id not in student_dict:
35-
student_dict[student_id] = {
36-
'admission': {},
37-
'finaid_amt': Decimal('0'),
38-
'items': [],
39-
'items_select': [],
40-
'payment': [],
41-
}
42-
lineitems = doc.txn.lineitem_set.all()
43-
"""
44-
if lineitems.count() > 0:
45-
try:
46-
print '\nStudent: %s' % doc.user.name()
47-
except:
48-
print '\nStudent: %s' % doc.user.username
49-
"""
50-
for li in lineitems:
51-
found_for_program = True
52-
base_txt = '[%5d] %s: %.2f' % (li.id, li.text, li.amount)
53-
# if li.anchor.uri == splash.anchor.uri + '/LineItemTypes/Required':
54-
if 'admission' in li.text.lower() or 'cost of attending' in li.text.lower():
55-
base_txt = '(Admission) ' + base_txt
56-
if li.text not in student_dict[student_id]['admission']:
57-
student_dict[student_id]['admission'][li.text] = li.amount.copy_abs()
58-
elif 'financial aid' in li.text.lower():
59-
base_txt = '(Finaid) ' + base_txt
60-
student_dict[student_id]['finaid_amt'] += li.amount
61-
elif 'payment received' in li.text.lower():
62-
base_txt = '(Payment) ' + base_txt
63-
student_dict[student_id]['payment'].append(li.amount.copy_abs())
64-
elif 'expecting on-site payment' in li.text.lower():
65-
base_txt = '(Payment expected) ' + base_txt
66-
student_dict[student_id]['payment'].append(li.amount.copy_abs())
67-
elif 'BuyMultiSelect' in li.anchor.uri:
68-
base_txt = '(Select: field "%s", choice "%s") ' % (li.anchor.name, li.text) + base_txt
69-
student_dict[student_id]['items_select'].append((li.anchor.name, li.text, li.amount.copy_abs()))
70-
if li.anchor.name not in lineitem_types['select']:
71-
lineitem_types['select'].append(li.anchor.name)
72-
lineitem_choices[li.anchor.name] = []
73-
if (li.text, li.amount.copy_abs()) not in lineitem_choices[li.anchor.name]:
74-
lineitem_choices[li.anchor.name].append((li.text, li.amount.copy_abs()))
75-
elif 'BuyMany' in li.anchor.uri or 'shirt' in li.text.lower():
76-
base_txt = '(Multi: field "%s") ' % (li.anchor.name) + base_txt
77-
student_dict[student_id]['items'].append((li.text, li.amount.copy_abs()))
78-
if li.text not in lineitem_types['multi']:
79-
lineitem_types['multi'][li.text] = li.amount.copy_abs()
80-
elif 'BuyOne' in li.anchor.uri or 'lunch' in li.text.lower() or 'dinner' in li.text.lower() or 'photo' in li.text.lower():
81-
base_txt = '(Single: field "%s") ' % (li.anchor.name) + base_txt
82-
student_dict[student_id]['items'].append((li.text, li.amount.copy_abs()))
83-
if li.text not in lineitem_types['one']:
84-
lineitem_types['one'][li.text] = li.amount.copy_abs()
85-
else:
86-
num_uncategorized += 1
87-
print 'WARNING: Uncategorized line item: %s' % base_txt
88-
# raise Exception('Uncategorized line item: %s' % base_txt)
89-
90-
num += 1
91-
# print '-- %s' % base_txt
92-
"""
93-
if student_dict[student_id]['finaid_amt'] > 0:
94-
print student_dict[student_id]
95-
elif len(student_dict[student_id]['items_multi']) > 0:
96-
print student_dict[student_id]
97-
"""
98-
99-
if found_for_program:
100-
num_programs = 1
101-
else:
102-
num_programs = 0
103-
104-
# Populate line item types for the program
105-
optional_items = []
106-
for item in lineitem_types['one']:
107-
optional_items.append((item, lineitem_types['one'][item], 1))
108-
for item in lineitem_types['multi']:
109-
optional_items.append((item, lineitem_types['multi'][item], 10))
110-
select_items = []
111-
for item in lineitem_types['select']:
112-
select_items.append((item, lineitem_choices[item]))
113-
114-
# print optional_items
115-
# print select_items
116-
pac.setup_accounts()
117-
pac.setup_lineitemtypes(0.0, optional_items, select_items)
118-
119-
# Create new transfer records for this student
120-
for student_id in student_dict:
121-
user = ESPUser.objects.get(id=student_id)
122-
iac = IndividualAccountingController(program, user)
123-
rec = student_dict[student_id]
124-
125-
# Admission fee
126-
admission_total_cost = 0
127-
for key in rec['admission']:
128-
admission_total_cost += rec['admission'][key]
129-
if admission_total_cost > 0:
130-
initial_transfer = iac.add_required_transfers()[0]
131-
initial_transfer.amount_dec = admission_total_cost
132-
initial_transfer.save()
133-
134-
# Financial aid
135-
if rec['finaid_amt'] > 0:
136-
if rec['finaid_amt'] >= admission_total_cost:
137-
iac.grant_full_financial_aid()
138-
else:
139-
iac.set_finaid_params(rec['finaid_amt'], None)
140-
141-
# Optional items
142-
prefs = []
143-
for item in rec['items']:
144-
prefs.append((item[0], 1, item[1]))
145-
for item in rec['items_select']:
146-
prefs.append((item[0], 1, item[2]))
147-
iac.apply_preferences(prefs)
148-
149-
# Payments
150-
for payment in rec['payment']:
151-
iac.submit_payment(payment)
152-
153-
try:
154-
attended_ids = program.students()['attended'].values_list('id', flat=True)
155-
156-
# Execute transfers for the students that are marked as attended
157-
pac.execute_pending_transfers(ESPUser.objects.filter(id__in=attended_ids))
158-
159-
# Clear transfers for the students that are marked as not attended
160-
pac.remove_pending_transfers(ESPUser.objects.exclude(id__in=attended_ids))
161-
except:
162-
print 'Unable to determine which students attended the program; all transfers remain unexecuted'
163-
164-
print 'Converted %d line items for %s; %d uncategorized' % (num, program.niceName(), num_uncategorized)
165-
return num_programs
17+
return 0
16618

16719
class Migration(SchemaMigration):
16820

@@ -323,4 +175,4 @@ def backwards(self, orm):
323175
}
324176
}
325177

326-
complete_apps = ['accounting']
178+
complete_apps = ['accounting']

esp/esp/accounting_core/AUTHORS

-3
This file was deleted.

0 commit comments

Comments
 (0)