Commit bd4e6fc6 by Clinton Blackburn

Merge pull request #7 from edx/schema-updates

Schema updates
parents 1018ced0 fbaef361
...@@ -50,7 +50,7 @@ syncdb: ...@@ -50,7 +50,7 @@ syncdb:
$(foreach db_name,$(DATABASES),./manage.py syncdb --migrate --noinput --database=$(db_name);) $(foreach db_name,$(DATABASES),./manage.py syncdb --migrate --noinput --database=$(db_name);)
loaddata: syncdb loaddata: syncdb
python manage.py loaddata courses education_levels countries single_course_activity problem_response_answer_distribution --database=analytics python manage.py loaddata education_levels single_course_activity problem_response_answer_distribution --database=analytics
python manage.py generate_fake_enrollment_data python manage.py generate_fake_enrollment_data
demo: clean requirements loaddata demo: clean requirements loaddata
......
[
{
"model": "v0.country",
"fields": {
"code": "A1",
"name": "Anonymous Proxy"
}
},
{
"model": "v0.country",
"fields": {
"code": "A2",
"name": "Satellite Provider"
}
},
{
"model": "v0.country",
"fields": {
"code": "O1",
"name": "Other Country"
}
},
{
"model": "v0.country",
"fields": {
"code": "AD",
"name": "Andorra"
}
},
{
"model": "v0.country",
"fields": {
"code": "AE",
"name": "United Arab Emirates"
}
},
{
"model": "v0.country",
"fields": {
"code": "AF",
"name": "Afghanistan"
}
},
{
"model": "v0.country",
"fields": {
"code": "AG",
"name": "Antigua and Barbuda"
}
},
{
"model": "v0.country",
"fields": {
"code": "AI",
"name": "Anguilla"
}
},
{
"model": "v0.country",
"fields": {
"code": "AL",
"name": "Albania"
}
},
{
"model": "v0.country",
"fields": {
"code": "AM",
"name": "Armenia"
}
},
{
"model": "v0.country",
"fields": {
"code": "AO",
"name": "Angola"
}
},
{
"model": "v0.country",
"fields": {
"code": "AP",
"name": "Asia/Pacific Region"
}
},
{
"model": "v0.country",
"fields": {
"code": "AQ",
"name": "Antarctica"
}
},
{
"model": "v0.country",
"fields": {
"code": "AR",
"name": "Argentina"
}
},
{
"model": "v0.country",
"fields": {
"code": "AS",
"name": "American Samoa"
}
},
{
"model": "v0.country",
"fields": {
"code": "AT",
"name": "Austria"
}
},
{
"model": "v0.country",
"fields": {
"code": "AU",
"name": "Australia"
}
},
{
"model": "v0.country",
"fields": {
"code": "AW",
"name": "Aruba"
}
},
{
"model": "v0.country",
"fields": {
"code": "AX",
"name": "Aland Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "AZ",
"name": "Azerbaijan"
}
},
{
"model": "v0.country",
"fields": {
"code": "BA",
"name": "Bosnia and Herzegovina"
}
},
{
"model": "v0.country",
"fields": {
"code": "BB",
"name": "Barbados"
}
},
{
"model": "v0.country",
"fields": {
"code": "BD",
"name": "Bangladesh"
}
},
{
"model": "v0.country",
"fields": {
"code": "BE",
"name": "Belgium"
}
},
{
"model": "v0.country",
"fields": {
"code": "BF",
"name": "Burkina Faso"
}
},
{
"model": "v0.country",
"fields": {
"code": "BG",
"name": "Bulgaria"
}
},
{
"model": "v0.country",
"fields": {
"code": "BH",
"name": "Bahrain"
}
},
{
"model": "v0.country",
"fields": {
"code": "BI",
"name": "Burundi"
}
},
{
"model": "v0.country",
"fields": {
"code": "BJ",
"name": "Benin"
}
},
{
"model": "v0.country",
"fields": {
"code": "BL",
"name": "Saint Bartelemey"
}
},
{
"model": "v0.country",
"fields": {
"code": "BM",
"name": "Bermuda"
}
},
{
"model": "v0.country",
"fields": {
"code": "BN",
"name": "Brunei Darussalam"
}
},
{
"model": "v0.country",
"fields": {
"code": "BO",
"name": "Bolivia"
}
},
{
"model": "v0.country",
"fields": {
"code": "BQ",
"name": "Bonaire, Saint Eustatius and Saba"
}
},
{
"model": "v0.country",
"fields": {
"code": "BR",
"name": "Brazil"
}
},
{
"model": "v0.country",
"fields": {
"code": "BS",
"name": "Bahamas"
}
},
{
"model": "v0.country",
"fields": {
"code": "BT",
"name": "Bhutan"
}
},
{
"model": "v0.country",
"fields": {
"code": "BV",
"name": "Bouvet Island"
}
},
{
"model": "v0.country",
"fields": {
"code": "BW",
"name": "Botswana"
}
},
{
"model": "v0.country",
"fields": {
"code": "BY",
"name": "Belarus"
}
},
{
"model": "v0.country",
"fields": {
"code": "BZ",
"name": "Belize"
}
},
{
"model": "v0.country",
"fields": {
"code": "CA",
"name": "Canada"
}
},
{
"model": "v0.country",
"fields": {
"code": "CC",
"name": "Cocos (Keeling) Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "CD",
"name": "Congo, The Democratic Republic of the"
}
},
{
"model": "v0.country",
"fields": {
"code": "CF",
"name": "Central African Republic"
}
},
{
"model": "v0.country",
"fields": {
"code": "CG",
"name": "Congo"
}
},
{
"model": "v0.country",
"fields": {
"code": "CH",
"name": "Switzerland"
}
},
{
"model": "v0.country",
"fields": {
"code": "CI",
"name": "Cote d'Ivoire"
}
},
{
"model": "v0.country",
"fields": {
"code": "CK",
"name": "Cook Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "CL",
"name": "Chile"
}
},
{
"model": "v0.country",
"fields": {
"code": "CM",
"name": "Cameroon"
}
},
{
"model": "v0.country",
"fields": {
"code": "CN",
"name": "China"
}
},
{
"model": "v0.country",
"fields": {
"code": "CO",
"name": "Colombia"
}
},
{
"model": "v0.country",
"fields": {
"code": "CR",
"name": "Costa Rica"
}
},
{
"model": "v0.country",
"fields": {
"code": "CU",
"name": "Cuba"
}
},
{
"model": "v0.country",
"fields": {
"code": "CV",
"name": "Cape Verde"
}
},
{
"model": "v0.country",
"fields": {
"code": "CW",
"name": "Curacao"
}
},
{
"model": "v0.country",
"fields": {
"code": "CX",
"name": "Christmas Island"
}
},
{
"model": "v0.country",
"fields": {
"code": "CY",
"name": "Cyprus"
}
},
{
"model": "v0.country",
"fields": {
"code": "CZ",
"name": "Czech Republic"
}
},
{
"model": "v0.country",
"fields": {
"code": "DE",
"name": "Germany"
}
},
{
"model": "v0.country",
"fields": {
"code": "DJ",
"name": "Djibouti"
}
},
{
"model": "v0.country",
"fields": {
"code": "DK",
"name": "Denmark"
}
},
{
"model": "v0.country",
"fields": {
"code": "DM",
"name": "Dominica"
}
},
{
"model": "v0.country",
"fields": {
"code": "DO",
"name": "Dominican Republic"
}
},
{
"model": "v0.country",
"fields": {
"code": "DZ",
"name": "Algeria"
}
},
{
"model": "v0.country",
"fields": {
"code": "EC",
"name": "Ecuador"
}
},
{
"model": "v0.country",
"fields": {
"code": "EE",
"name": "Estonia"
}
},
{
"model": "v0.country",
"fields": {
"code": "EG",
"name": "Egypt"
}
},
{
"model": "v0.country",
"fields": {
"code": "EH",
"name": "Western Sahara"
}
},
{
"model": "v0.country",
"fields": {
"code": "ER",
"name": "Eritrea"
}
},
{
"model": "v0.country",
"fields": {
"code": "ES",
"name": "Spain"
}
},
{
"model": "v0.country",
"fields": {
"code": "ET",
"name": "Ethiopia"
}
},
{
"model": "v0.country",
"fields": {
"code": "EU",
"name": "Europe"
}
},
{
"model": "v0.country",
"fields": {
"code": "FI",
"name": "Finland"
}
},
{
"model": "v0.country",
"fields": {
"code": "FJ",
"name": "Fiji"
}
},
{
"model": "v0.country",
"fields": {
"code": "FK",
"name": "Falkland Islands (Malvinas)"
}
},
{
"model": "v0.country",
"fields": {
"code": "FM",
"name": "Micronesia, Federated States of"
}
},
{
"model": "v0.country",
"fields": {
"code": "FO",
"name": "Faroe Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "FR",
"name": "France"
}
},
{
"model": "v0.country",
"fields": {
"code": "GA",
"name": "Gabon"
}
},
{
"model": "v0.country",
"fields": {
"code": "GB",
"name": "United Kingdom"
}
},
{
"model": "v0.country",
"fields": {
"code": "GD",
"name": "Grenada"
}
},
{
"model": "v0.country",
"fields": {
"code": "GE",
"name": "Georgia"
}
},
{
"model": "v0.country",
"fields": {
"code": "GF",
"name": "French Guiana"
}
},
{
"model": "v0.country",
"fields": {
"code": "GG",
"name": "Guernsey"
}
},
{
"model": "v0.country",
"fields": {
"code": "GH",
"name": "Ghana"
}
},
{
"model": "v0.country",
"fields": {
"code": "GI",
"name": "Gibraltar"
}
},
{
"model": "v0.country",
"fields": {
"code": "GL",
"name": "Greenland"
}
},
{
"model": "v0.country",
"fields": {
"code": "GM",
"name": "Gambia"
}
},
{
"model": "v0.country",
"fields": {
"code": "GN",
"name": "Guinea"
}
},
{
"model": "v0.country",
"fields": {
"code": "GP",
"name": "Guadeloupe"
}
},
{
"model": "v0.country",
"fields": {
"code": "GQ",
"name": "Equatorial Guinea"
}
},
{
"model": "v0.country",
"fields": {
"code": "GR",
"name": "Greece"
}
},
{
"model": "v0.country",
"fields": {
"code": "GS",
"name": "South Georgia and the South Sandwich Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "GT",
"name": "Guatemala"
}
},
{
"model": "v0.country",
"fields": {
"code": "GU",
"name": "Guam"
}
},
{
"model": "v0.country",
"fields": {
"code": "GW",
"name": "Guinea-Bissau"
}
},
{
"model": "v0.country",
"fields": {
"code": "GY",
"name": "Guyana"
}
},
{
"model": "v0.country",
"fields": {
"code": "HK",
"name": "Hong Kong"
}
},
{
"model": "v0.country",
"fields": {
"code": "HM",
"name": "Heard Island and McDonald Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "HN",
"name": "Honduras"
}
},
{
"model": "v0.country",
"fields": {
"code": "HR",
"name": "Croatia"
}
},
{
"model": "v0.country",
"fields": {
"code": "HT",
"name": "Haiti"
}
},
{
"model": "v0.country",
"fields": {
"code": "HU",
"name": "Hungary"
}
},
{
"model": "v0.country",
"fields": {
"code": "ID",
"name": "Indonesia"
}
},
{
"model": "v0.country",
"fields": {
"code": "IE",
"name": "Ireland"
}
},
{
"model": "v0.country",
"fields": {
"code": "IL",
"name": "Israel"
}
},
{
"model": "v0.country",
"fields": {
"code": "IM",
"name": "Isle of Man"
}
},
{
"model": "v0.country",
"fields": {
"code": "IN",
"name": "India"
}
},
{
"model": "v0.country",
"fields": {
"code": "IO",
"name": "British Indian Ocean Territory"
}
},
{
"model": "v0.country",
"fields": {
"code": "IQ",
"name": "Iraq"
}
},
{
"model": "v0.country",
"fields": {
"code": "IR",
"name": "Iran, Islamic Republic of"
}
},
{
"model": "v0.country",
"fields": {
"code": "IS",
"name": "Iceland"
}
},
{
"model": "v0.country",
"fields": {
"code": "IT",
"name": "Italy"
}
},
{
"model": "v0.country",
"fields": {
"code": "JE",
"name": "Jersey"
}
},
{
"model": "v0.country",
"fields": {
"code": "JM",
"name": "Jamaica"
}
},
{
"model": "v0.country",
"fields": {
"code": "JO",
"name": "Jordan"
}
},
{
"model": "v0.country",
"fields": {
"code": "JP",
"name": "Japan"
}
},
{
"model": "v0.country",
"fields": {
"code": "KE",
"name": "Kenya"
}
},
{
"model": "v0.country",
"fields": {
"code": "KG",
"name": "Kyrgyzstan"
}
},
{
"model": "v0.country",
"fields": {
"code": "KH",
"name": "Cambodia"
}
},
{
"model": "v0.country",
"fields": {
"code": "KI",
"name": "Kiribati"
}
},
{
"model": "v0.country",
"fields": {
"code": "KM",
"name": "Comoros"
}
},
{
"model": "v0.country",
"fields": {
"code": "KN",
"name": "Saint Kitts and Nevis"
}
},
{
"model": "v0.country",
"fields": {
"code": "KP",
"name": "Korea, Democratic People's Republic of"
}
},
{
"model": "v0.country",
"fields": {
"code": "KR",
"name": "Korea, Republic of"
}
},
{
"model": "v0.country",
"fields": {
"code": "KW",
"name": "Kuwait"
}
},
{
"model": "v0.country",
"fields": {
"code": "KY",
"name": "Cayman Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "KZ",
"name": "Kazakhstan"
}
},
{
"model": "v0.country",
"fields": {
"code": "LA",
"name": "Lao People's Democratic Republic"
}
},
{
"model": "v0.country",
"fields": {
"code": "LB",
"name": "Lebanon"
}
},
{
"model": "v0.country",
"fields": {
"code": "LC",
"name": "Saint Lucia"
}
},
{
"model": "v0.country",
"fields": {
"code": "LI",
"name": "Liechtenstein"
}
},
{
"model": "v0.country",
"fields": {
"code": "LK",
"name": "Sri Lanka"
}
},
{
"model": "v0.country",
"fields": {
"code": "LR",
"name": "Liberia"
}
},
{
"model": "v0.country",
"fields": {
"code": "LS",
"name": "Lesotho"
}
},
{
"model": "v0.country",
"fields": {
"code": "LT",
"name": "Lithuania"
}
},
{
"model": "v0.country",
"fields": {
"code": "LU",
"name": "Luxembourg"
}
},
{
"model": "v0.country",
"fields": {
"code": "LV",
"name": "Latvia"
}
},
{
"model": "v0.country",
"fields": {
"code": "LY",
"name": "Libyan Arab Jamahiriya"
}
},
{
"model": "v0.country",
"fields": {
"code": "MA",
"name": "Morocco"
}
},
{
"model": "v0.country",
"fields": {
"code": "MC",
"name": "Monaco"
}
},
{
"model": "v0.country",
"fields": {
"code": "MD",
"name": "Moldova, Republic of"
}
},
{
"model": "v0.country",
"fields": {
"code": "ME",
"name": "Montenegro"
}
},
{
"model": "v0.country",
"fields": {
"code": "MF",
"name": "Saint Martin"
}
},
{
"model": "v0.country",
"fields": {
"code": "MG",
"name": "Madagascar"
}
},
{
"model": "v0.country",
"fields": {
"code": "MH",
"name": "Marshall Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "MK",
"name": "Macedonia"
}
},
{
"model": "v0.country",
"fields": {
"code": "ML",
"name": "Mali"
}
},
{
"model": "v0.country",
"fields": {
"code": "MM",
"name": "Myanmar"
}
},
{
"model": "v0.country",
"fields": {
"code": "MN",
"name": "Mongolia"
}
},
{
"model": "v0.country",
"fields": {
"code": "MO",
"name": "Macao"
}
},
{
"model": "v0.country",
"fields": {
"code": "MP",
"name": "Northern Mariana Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "MQ",
"name": "Martinique"
}
},
{
"model": "v0.country",
"fields": {
"code": "MR",
"name": "Mauritania"
}
},
{
"model": "v0.country",
"fields": {
"code": "MS",
"name": "Montserrat"
}
},
{
"model": "v0.country",
"fields": {
"code": "MT",
"name": "Malta"
}
},
{
"model": "v0.country",
"fields": {
"code": "MU",
"name": "Mauritius"
}
},
{
"model": "v0.country",
"fields": {
"code": "MV",
"name": "Maldives"
}
},
{
"model": "v0.country",
"fields": {
"code": "MW",
"name": "Malawi"
}
},
{
"model": "v0.country",
"fields": {
"code": "MX",
"name": "Mexico"
}
},
{
"model": "v0.country",
"fields": {
"code": "MY",
"name": "Malaysia"
}
},
{
"model": "v0.country",
"fields": {
"code": "MZ",
"name": "Mozambique"
}
},
{
"model": "v0.country",
"fields": {
"code": "NA",
"name": "Namibia"
}
},
{
"model": "v0.country",
"fields": {
"code": "NC",
"name": "New Caledonia"
}
},
{
"model": "v0.country",
"fields": {
"code": "NE",
"name": "Niger"
}
},
{
"model": "v0.country",
"fields": {
"code": "NF",
"name": "Norfolk Island"
}
},
{
"model": "v0.country",
"fields": {
"code": "NG",
"name": "Nigeria"
}
},
{
"model": "v0.country",
"fields": {
"code": "NI",
"name": "Nicaragua"
}
},
{
"model": "v0.country",
"fields": {
"code": "NL",
"name": "Netherlands"
}
},
{
"model": "v0.country",
"fields": {
"code": "NO",
"name": "Norway"
}
},
{
"model": "v0.country",
"fields": {
"code": "NP",
"name": "Nepal"
}
},
{
"model": "v0.country",
"fields": {
"code": "NR",
"name": "Nauru"
}
},
{
"model": "v0.country",
"fields": {
"code": "NU",
"name": "Niue"
}
},
{
"model": "v0.country",
"fields": {
"code": "NZ",
"name": "New Zealand"
}
},
{
"model": "v0.country",
"fields": {
"code": "OM",
"name": "Oman"
}
},
{
"model": "v0.country",
"fields": {
"code": "PA",
"name": "Panama"
}
},
{
"model": "v0.country",
"fields": {
"code": "PE",
"name": "Peru"
}
},
{
"model": "v0.country",
"fields": {
"code": "PF",
"name": "French Polynesia"
}
},
{
"model": "v0.country",
"fields": {
"code": "PG",
"name": "Papua New Guinea"
}
},
{
"model": "v0.country",
"fields": {
"code": "PH",
"name": "Philippines"
}
},
{
"model": "v0.country",
"fields": {
"code": "PK",
"name": "Pakistan"
}
},
{
"model": "v0.country",
"fields": {
"code": "PL",
"name": "Poland"
}
},
{
"model": "v0.country",
"fields": {
"code": "PM",
"name": "Saint Pierre and Miquelon"
}
},
{
"model": "v0.country",
"fields": {
"code": "PN",
"name": "Pitcairn"
}
},
{
"model": "v0.country",
"fields": {
"code": "PR",
"name": "Puerto Rico"
}
},
{
"model": "v0.country",
"fields": {
"code": "PS",
"name": "Palestinian Territory"
}
},
{
"model": "v0.country",
"fields": {
"code": "PT",
"name": "Portugal"
}
},
{
"model": "v0.country",
"fields": {
"code": "PW",
"name": "Palau"
}
},
{
"model": "v0.country",
"fields": {
"code": "PY",
"name": "Paraguay"
}
},
{
"model": "v0.country",
"fields": {
"code": "QA",
"name": "Qatar"
}
},
{
"model": "v0.country",
"fields": {
"code": "RE",
"name": "Reunion"
}
},
{
"model": "v0.country",
"fields": {
"code": "RO",
"name": "Romania"
}
},
{
"model": "v0.country",
"fields": {
"code": "RS",
"name": "Serbia"
}
},
{
"model": "v0.country",
"fields": {
"code": "RU",
"name": "Russian Federation"
}
},
{
"model": "v0.country",
"fields": {
"code": "RW",
"name": "Rwanda"
}
},
{
"model": "v0.country",
"fields": {
"code": "SA",
"name": "Saudi Arabia"
}
},
{
"model": "v0.country",
"fields": {
"code": "SB",
"name": "Solomon Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "SC",
"name": "Seychelles"
}
},
{
"model": "v0.country",
"fields": {
"code": "SD",
"name": "Sudan"
}
},
{
"model": "v0.country",
"fields": {
"code": "SE",
"name": "Sweden"
}
},
{
"model": "v0.country",
"fields": {
"code": "SG",
"name": "Singapore"
}
},
{
"model": "v0.country",
"fields": {
"code": "SH",
"name": "Saint Helena"
}
},
{
"model": "v0.country",
"fields": {
"code": "SI",
"name": "Slovenia"
}
},
{
"model": "v0.country",
"fields": {
"code": "SJ",
"name": "Svalbard and Jan Mayen"
}
},
{
"model": "v0.country",
"fields": {
"code": "SK",
"name": "Slovakia"
}
},
{
"model": "v0.country",
"fields": {
"code": "SL",
"name": "Sierra Leone"
}
},
{
"model": "v0.country",
"fields": {
"code": "SM",
"name": "San Marino"
}
},
{
"model": "v0.country",
"fields": {
"code": "SN",
"name": "Senegal"
}
},
{
"model": "v0.country",
"fields": {
"code": "SO",
"name": "Somalia"
}
},
{
"model": "v0.country",
"fields": {
"code": "SR",
"name": "Suriname"
}
},
{
"model": "v0.country",
"fields": {
"code": "SS",
"name": "South Sudan"
}
},
{
"model": "v0.country",
"fields": {
"code": "ST",
"name": "Sao Tome and Principe"
}
},
{
"model": "v0.country",
"fields": {
"code": "SV",
"name": "El Salvador"
}
},
{
"model": "v0.country",
"fields": {
"code": "SX",
"name": "Sint Maarten"
}
},
{
"model": "v0.country",
"fields": {
"code": "SY",
"name": "Syrian Arab Republic"
}
},
{
"model": "v0.country",
"fields": {
"code": "SZ",
"name": "Swaziland"
}
},
{
"model": "v0.country",
"fields": {
"code": "TC",
"name": "Turks and Caicos Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "TD",
"name": "Chad"
}
},
{
"model": "v0.country",
"fields": {
"code": "TF",
"name": "French Southern Territories"
}
},
{
"model": "v0.country",
"fields": {
"code": "TG",
"name": "Togo"
}
},
{
"model": "v0.country",
"fields": {
"code": "TH",
"name": "Thailand"
}
},
{
"model": "v0.country",
"fields": {
"code": "TJ",
"name": "Tajikistan"
}
},
{
"model": "v0.country",
"fields": {
"code": "TK",
"name": "Tokelau"
}
},
{
"model": "v0.country",
"fields": {
"code": "TL",
"name": "Timor-Leste"
}
},
{
"model": "v0.country",
"fields": {
"code": "TM",
"name": "Turkmenistan"
}
},
{
"model": "v0.country",
"fields": {
"code": "TN",
"name": "Tunisia"
}
},
{
"model": "v0.country",
"fields": {
"code": "TO",
"name": "Tonga"
}
},
{
"model": "v0.country",
"fields": {
"code": "TR",
"name": "Turkey"
}
},
{
"model": "v0.country",
"fields": {
"code": "TT",
"name": "Trinidad and Tobago"
}
},
{
"model": "v0.country",
"fields": {
"code": "TV",
"name": "Tuvalu"
}
},
{
"model": "v0.country",
"fields": {
"code": "TW",
"name": "Taiwan"
}
},
{
"model": "v0.country",
"fields": {
"code": "TZ",
"name": "Tanzania, United Republic of"
}
},
{
"model": "v0.country",
"fields": {
"code": "UA",
"name": "Ukraine"
}
},
{
"model": "v0.country",
"fields": {
"code": "UG",
"name": "Uganda"
}
},
{
"model": "v0.country",
"fields": {
"code": "UM",
"name": "United States Minor Outlying Islands"
}
},
{
"model": "v0.country",
"fields": {
"code": "US",
"name": "United States"
}
},
{
"model": "v0.country",
"fields": {
"code": "UY",
"name": "Uruguay"
}
},
{
"model": "v0.country",
"fields": {
"code": "UZ",
"name": "Uzbekistan"
}
},
{
"model": "v0.country",
"fields": {
"code": "VA",
"name": "Holy See (Vatican City State)"
}
},
{
"model": "v0.country",
"fields": {
"code": "VC",
"name": "Saint Vincent and the Grenadines"
}
},
{
"model": "v0.country",
"fields": {
"code": "VE",
"name": "Venezuela"
}
},
{
"model": "v0.country",
"fields": {
"code": "VG",
"name": "Virgin Islands, British"
}
},
{
"model": "v0.country",
"fields": {
"code": "VI",
"name": "Virgin Islands, U.S."
}
},
{
"model": "v0.country",
"fields": {
"code": "VN",
"name": "Vietnam"
}
},
{
"model": "v0.country",
"fields": {
"code": "VU",
"name": "Vanuatu"
}
},
{
"model": "v0.country",
"fields": {
"code": "WF",
"name": "Wallis and Futuna"
}
},
{
"model": "v0.country",
"fields": {
"code": "WS",
"name": "Samoa"
}
},
{
"model": "v0.country",
"fields": {
"code": "YE",
"name": "Yemen"
}
},
{
"model": "v0.country",
"fields": {
"code": "YT",
"name": "Mayotte"
}
},
{
"model": "v0.country",
"fields": {
"code": "ZA",
"name": "South Africa"
}
},
{
"model": "v0.country",
"fields": {
"code": "ZM",
"name": "Zambia"
}
},
{
"model": "v0.country",
"fields": {
"code": "ZW",
"name": "Zimbabwe"
}
}
]
\ No newline at end of file
[
{
"pk": 1,
"model": "v0.course",
"fields": {
"course_id": "edX/DemoX/Demo_Course"
}
}
]
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
"pk": 40, "pk": 40,
"model": "v0.CourseActivityByWeek", "model": "v0.CourseActivityByWeek",
"fields": { "fields": {
"course": ["edX/DemoX/Demo_Course"], "course_id": "edX/DemoX/Demo_Course",
"interval_start": "2014-05-24T00:00:00Z", "interval_start": "2014-05-24T00:00:00Z",
"activity_type": "posted_forum", "activity_type": "posted_forum",
"count": 100, "count": 100,
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
"pk": 106, "pk": 106,
"model": "v0.CourseActivityByWeek", "model": "v0.CourseActivityByWeek",
"fields": { "fields": {
"course": ["edX/DemoX/Demo_Course"], "course_id": "edX/DemoX/Demo_Course",
"interval_start": "2014-05-24T00:00:00Z", "interval_start": "2014-05-24T00:00:00Z",
"activity_type": "attempted_problem", "activity_type": "attempted_problem",
"count": 200, "count": 200,
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
"pk": 201, "pk": 201,
"model": "v0.CourseActivityByWeek", "model": "v0.CourseActivityByWeek",
"fields": { "fields": {
"course": ["edX/DemoX/Demo_Course"], "course_id": "edX/DemoX/Demo_Course",
"interval_start": "2014-05-24T00:00:00Z", "interval_start": "2014-05-24T00:00:00Z",
"activity_type": "any", "activity_type": "any",
"count": 300, "count": 300,
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
"pk": 725, "pk": 725,
"model": "v0.CourseActivityByWeek", "model": "v0.CourseActivityByWeek",
"fields": { "fields": {
"course": ["edX/DemoX/Demo_Course"], "course_id": "edX/DemoX/Demo_Course",
"interval_start": "2014-05-24T00:00:00Z", "interval_start": "2014-05-24T00:00:00Z",
"activity_type": "played_video", "activity_type": "played_video",
"count": 400, "count": 400,
......
# pylint: disable=line-too-long
import datetime import datetime
import random import random
...@@ -23,7 +25,7 @@ class Command(BaseCommand): ...@@ -23,7 +25,7 @@ class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):
days = 120 days = 120
course = models.Course.objects.first() course_id = 'edX/DemoX/Demo_Course'
start_date = datetime.date(year=2014, month=1, day=1) start_date = datetime.date(year=2014, month=1, day=1)
genders = { genders = {
...@@ -58,33 +60,36 @@ class Command(BaseCommand): ...@@ -58,33 +60,36 @@ class Command(BaseCommand):
birth_years = dict(zip(birth_years, ratios)) birth_years = dict(zip(birth_years, ratios))
# Delete existing data # Delete existing data
for model in [models.CourseEnrollmentDaily, models.CourseEnrollmentByGender, models.CourseEnrollmentByEducation, for model in [models.CourseEnrollmentDaily,
models.CourseEnrollmentByBirthYear, models.CourseEnrollmentByCountry]: models.CourseEnrollmentByGender,
models.CourseEnrollmentByEducation,
models.CourseEnrollmentByBirthYear,
models.CourseEnrollmentByCountry]:
model.objects.all().delete() model.objects.all().delete()
# Create new data data # Create new data
daily_total = 1500 daily_total = 1500
for i in range(days): for i in range(days):
daily_total = get_count(daily_total) daily_total = get_count(daily_total)
date = start_date + datetime.timedelta(days=i) date = start_date + datetime.timedelta(days=i)
models.CourseEnrollmentDaily.objects.create(course=course, date=date, count=daily_total) models.CourseEnrollmentDaily.objects.create(course_id=course_id, date=date, count=daily_total)
for gender, ratio in genders.iteritems(): for gender, ratio in genders.iteritems():
count = int(ratio * daily_total) count = int(ratio * daily_total)
models.CourseEnrollmentByGender.objects.create(course=course, date=date, count=count, gender=gender) models.CourseEnrollmentByGender.objects.create(course_id=course_id, date=date, count=count, gender=gender)
for short_name, ratio in education_levels.iteritems(): for short_name, ratio in education_levels.iteritems():
education_level = models.EducationLevel.objects.get(short_name=short_name) education_level = models.EducationLevel.objects.get(short_name=short_name)
count = int(ratio * daily_total) count = int(ratio * daily_total)
models.CourseEnrollmentByEducation.objects.create(course=course, date=date, count=count, models.CourseEnrollmentByEducation.objects.create(course_id=course_id, date=date, count=count,
education_level=education_level) education_level=education_level)
for code, ratio in countries.iteritems(): for country_code, ratio in countries.iteritems():
country = models.Country.objects.get(code=code)
count = int(ratio * daily_total) count = int(ratio * daily_total)
models.CourseEnrollmentByCountry.objects.create(course=course, date=date, count=count, country=country) models.CourseEnrollmentByCountry.objects.create(course_id=course_id, date=date, count=count,
country_code=country_code)
for birth_year, ratio in birth_years.iteritems(): for birth_year, ratio in birth_years.iteritems():
count = int(ratio * daily_total) count = int(ratio * daily_total)
models.CourseEnrollmentByBirthYear.objects.create(course=course, date=date, count=count, models.CourseEnrollmentByBirthYear.objects.create(course_id=course_id, date=date, count=count,
birth_year=birth_year) birth_year=birth_year)
from django.db import models
class CourseManager(models.Manager):
def get_by_natural_key(self, course_id):
return self.get(course_id=course_id)
class CountryManager(models.Manager):
def get_by_natural_key(self, code):
return self.get(code=code)
from collections import namedtuple
from django.db import models from django.db import models
from analytics_data_api.v0.managers import CourseManager, CountryManager from iso3166 import countries
class Course(models.Model):
course_id = models.CharField(unique=True, max_length=255)
objects = CourseManager() # pylint: disable=no-value-for-parameter
class Meta(object):
db_table = 'courses'
class CourseActivityByWeek(models.Model): class CourseActivityByWeek(models.Model):
...@@ -17,7 +9,7 @@ class CourseActivityByWeek(models.Model): ...@@ -17,7 +9,7 @@ class CourseActivityByWeek(models.Model):
class Meta(object): class Meta(object):
db_table = 'course_activity' db_table = 'course_activity'
course = models.ForeignKey(Course, null=False) course_id = models.CharField(max_length=255)
interval_start = models.DateTimeField() interval_start = models.DateTimeField()
interval_end = models.DateTimeField() interval_end = models.DateTimeField()
activity_type = models.CharField(db_index=True, max_length=255) activity_type = models.CharField(db_index=True, max_length=255)
...@@ -26,33 +18,35 @@ class CourseActivityByWeek(models.Model): ...@@ -26,33 +18,35 @@ class CourseActivityByWeek(models.Model):
@classmethod @classmethod
def get_most_recent(cls, course_id, activity_type): def get_most_recent(cls, course_id, activity_type):
"""Activity for the week that was mostly recently computed.""" """Activity for the week that was mostly recently computed."""
return cls.objects.filter(course__course_id=course_id, activity_type=activity_type).latest('interval_end') return cls.objects.filter(course_id=course_id, activity_type=activity_type).latest('interval_end')
class BaseCourseEnrollment(models.Model): class BaseCourseEnrollment(models.Model):
course = models.ForeignKey(Course, null=False) course_id = models.CharField(max_length=255)
date = models.DateField(null=False, db_index=True) date = models.DateField(null=False, db_index=True)
count = models.IntegerField(null=False) count = models.IntegerField(null=False)
created = models.DateTimeField(auto_now_add=True)
class Meta(object): class Meta(object):
abstract = True abstract = True
get_latest_by = 'date' get_latest_by = 'date'
index_together = [('course_id', 'date',)]
class CourseEnrollmentDaily(BaseCourseEnrollment): class CourseEnrollmentDaily(BaseCourseEnrollment):
class Meta(BaseCourseEnrollment.Meta): class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_daily' db_table = 'course_enrollment_daily'
ordering = ('date', 'course') ordering = ('date', 'course_id')
unique_together = [('course', 'date',)] unique_together = [('course_id', 'date',)]
class CourseEnrollmentByBirthYear(BaseCourseEnrollment): class CourseEnrollmentByBirthYear(BaseCourseEnrollment):
birth_year = models.IntegerField(null=False) birth_year = models.IntegerField(null=False)
class Meta(BaseCourseEnrollment.Meta): class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_birth_year' db_table = 'course_enrollment_birth_year_daily'
ordering = ('date', 'course', 'birth_year') ordering = ('date', 'course_id', 'birth_year')
unique_together = [('course', 'date', 'birth_year')] unique_together = [('course_id', 'date', 'birth_year')]
class EducationLevel(models.Model): class EducationLevel(models.Model):
...@@ -70,18 +64,18 @@ class CourseEnrollmentByEducation(BaseCourseEnrollment): ...@@ -70,18 +64,18 @@ class CourseEnrollmentByEducation(BaseCourseEnrollment):
education_level = models.ForeignKey(EducationLevel) education_level = models.ForeignKey(EducationLevel)
class Meta(BaseCourseEnrollment.Meta): class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_education_level' db_table = 'course_enrollment_education_level_daily'
ordering = ('date', 'course', 'education_level') ordering = ('date', 'course_id', 'education_level')
unique_together = [('course', 'date', 'education_level')] unique_together = [('course_id', 'date', 'education_level')]
class CourseEnrollmentByGender(BaseCourseEnrollment): class CourseEnrollmentByGender(BaseCourseEnrollment):
gender = models.CharField(max_length=255, null=False) gender = models.CharField(max_length=255, null=False)
class Meta(BaseCourseEnrollment.Meta): class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_gender' db_table = 'course_enrollment_gender_daily'
ordering = ('date', 'course', 'gender') ordering = ('date', 'course_id', 'gender')
unique_together = [('course', 'date', 'gender')] unique_together = [('course_id', 'date', 'gender')]
class ProblemResponseAnswerDistribution(models.Model): class ProblemResponseAnswerDistribution(models.Model):
...@@ -102,23 +96,18 @@ class ProblemResponseAnswerDistribution(models.Model): ...@@ -102,23 +96,18 @@ class ProblemResponseAnswerDistribution(models.Model):
created = models.DateTimeField(auto_now_add=True, db_column='created') created = models.DateTimeField(auto_now_add=True, db_column='created')
class Country(models.Model): Country = namedtuple('Country', 'name, code')
code = models.CharField(max_length=2, primary_key=True)
name = models.CharField(max_length=255, unique=True, null=False)
objects = CountryManager() # pylint: disable=no-value-for-parameter
class Meta(object):
db_table = 'countries'
def __unicode__(self):
return "{0} - {1}".format(self.code, self.name)
class CourseEnrollmentByCountry(BaseCourseEnrollment): class CourseEnrollmentByCountry(BaseCourseEnrollment):
country = models.ForeignKey(Country, null=False, db_column='country_code') country_code = models.CharField(max_length=255, null=False, db_column='country_code')
@property
def country(self):
country = countries.get(self.country_code)
return Country(country.name, country.alpha2)
class Meta(BaseCourseEnrollment.Meta): class Meta(BaseCourseEnrollment.Meta):
db_table = 'course_enrollment_location' db_table = 'course_enrollment_location_current'
ordering = ('date', 'course', 'country') ordering = ('date', 'course_id', 'country_code')
unique_together = [('course', 'date', 'country')] unique_together = [('course_id', 'date', 'country_code')]
...@@ -3,16 +3,7 @@ from rest_framework import serializers ...@@ -3,16 +3,7 @@ from rest_framework import serializers
from analytics_data_api.v0 import models from analytics_data_api.v0 import models
class CourseIdMixin(object): class CourseActivityByWeekSerializer(serializers.ModelSerializer):
def get_course_id(self, obj):
return obj.course.course_id
class RequiredSerializerMethodField(serializers.SerializerMethodField):
required = True
class CourseActivityByWeekSerializer(serializers.ModelSerializer, CourseIdMixin):
""" """
Representation of CourseActivityByWeek that excludes the id field. Representation of CourseActivityByWeek that excludes the id field.
...@@ -20,8 +11,6 @@ class CourseActivityByWeekSerializer(serializers.ModelSerializer, CourseIdMixin) ...@@ -20,8 +11,6 @@ class CourseActivityByWeekSerializer(serializers.ModelSerializer, CourseIdMixin)
particular record is likely to change unexpectedly so we avoid exposing it. particular record is likely to change unexpectedly so we avoid exposing it.
""" """
course_id = RequiredSerializerMethodField('get_course_id')
class Meta(object): class Meta(object):
model = models.CourseActivityByWeek model = models.CourseActivityByWeek
fields = ('interval_start', 'interval_end', 'activity_type', 'count', 'course_id') fields = ('interval_start', 'interval_end', 'activity_type', 'count', 'course_id')
...@@ -51,8 +40,7 @@ class ProblemResponseAnswerDistributionSerializer(serializers.ModelSerializer): ...@@ -51,8 +40,7 @@ class ProblemResponseAnswerDistributionSerializer(serializers.ModelSerializer):
) )
class BaseCourseEnrollmentModelSerializer(serializers.ModelSerializer, CourseIdMixin): class BaseCourseEnrollmentModelSerializer(serializers.ModelSerializer):
course_id = RequiredSerializerMethodField('get_course_id')
date = serializers.DateField(format=settings.DATE_FORMAT) date = serializers.DateField(format=settings.DATE_FORMAT)
...@@ -64,11 +52,9 @@ class CourseEnrollmentDailySerializer(BaseCourseEnrollmentModelSerializer): ...@@ -64,11 +52,9 @@ class CourseEnrollmentDailySerializer(BaseCourseEnrollmentModelSerializer):
fields = ('course_id', 'date', 'count') fields = ('course_id', 'date', 'count')
# pylint: disable=no-value-for-parameter class CountrySerializer(serializers.Serializer):
class CountrySerializer(serializers.ModelSerializer): code = serializers.CharField()
class Meta(object): name = serializers.CharField()
model = models.Country
fields = ('code', 'name')
# pylint: disable=no-value-for-parameter # pylint: disable=no-value-for-parameter
...@@ -79,7 +65,8 @@ class EducationLevelSerializer(serializers.ModelSerializer): ...@@ -79,7 +65,8 @@ class EducationLevelSerializer(serializers.ModelSerializer):
class CourseEnrollmentByCountrySerializer(BaseCourseEnrollmentModelSerializer): class CourseEnrollmentByCountrySerializer(BaseCourseEnrollmentModelSerializer):
country = CountrySerializer() # pylint: disable=unexpected-keyword-arg
country = CountrySerializer(many=False)
class Meta(object): class Meta(object):
model = models.CourseEnrollmentByCountry model = models.CourseEnrollmentByCountry
......
from django.core.exceptions import ObjectDoesNotExist
from django.test import TestCase
from django_dynamic_fixture import G
from analytics_data_api.v0.models import Course, Country
class CourseManagerTests(TestCase):
def test_get_by_natural_key(self):
course_id = 'edX/DemoX/Demo_Course'
self.assertRaises(ObjectDoesNotExist, Course.objects.get_by_natural_key, course_id)
course = G(Course, course_id=course_id)
self.assertEqual(course, Course.objects.get_by_natural_key(course_id))
class CountryManagerTests(TestCase):
def test_get_by_natural_key(self):
code = 'US'
self.assertRaises(ObjectDoesNotExist, Country.objects.get_by_natural_key, code)
country = G(Country, code=code)
self.assertEqual(country, Country.objects.get_by_natural_key(code))
...@@ -2,22 +2,31 @@ from django.test import TestCase ...@@ -2,22 +2,31 @@ from django.test import TestCase
from django_dynamic_fixture import G from django_dynamic_fixture import G
from analytics_data_api.v0.models import EducationLevel, Country from analytics_data_api.v0 import models
class EducationLevelTests(TestCase): class EducationLevelTests(TestCase):
def test_unicode(self): def test_unicode(self):
short_name = 'high_school' short_name = 'high_school'
name = 'High School' name = 'High School'
education_level = G(EducationLevel, short_name=short_name, name=name) education_level = G(models.EducationLevel, short_name=short_name,
name=name)
self.assertEqual(unicode(education_level), "{0} - {1}".format(short_name, name)) self.assertEqual(unicode(education_level),
"{0} - {1}".format(short_name, name))
class CountryTests(TestCase): class CountryTests(TestCase):
def test_unicode(self): # pylint: disable=no-member
code = 'US' def test_attributes(self):
name = 'United States of America' country = models.Country('Canada', 'CA')
country = G(Country, code=code, name=name) self.assertEqual(country.code, 'CA')
self.assertEqual(country.name, 'Canada')
self.assertEqual(unicode(country), "{0} - {1}".format(code, name))
class CourseEnrollmentByCountryTests(TestCase):
def test_country(self):
country = models.Country('United States', 'US')
instance = G(models.CourseEnrollmentByCountry,
country_code=country.code)
self.assertEqual(instance.country, country)
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
import StringIO import StringIO
import csv import csv
import datetime import datetime
import random
from django.conf import settings from django.conf import settings
from django_dynamic_fixture import G from django_dynamic_fixture import G
from iso3166 import countries
import pytz import pytz
from analytics_data_api.v0 import models from analytics_data_api.v0 import models
...@@ -17,19 +17,19 @@ from analyticsdataserver.tests import TestCaseWithAuthentication ...@@ -17,19 +17,19 @@ from analyticsdataserver.tests import TestCaseWithAuthentication
class CourseActivityLastWeekTest(TestCaseWithAuthentication): class CourseActivityLastWeekTest(TestCaseWithAuthentication):
# pylint: disable=line-too-long
def setUp(self): def setUp(self):
super(CourseActivityLastWeekTest, self).setUp() super(CourseActivityLastWeekTest, self).setUp()
self.course_id = 'edX/DemoX/Demo_Course' self.course_id = 'edX/DemoX/Demo_Course'
self.course = G(models.Course, course_id=self.course_id)
interval_start = '2014-05-24T00:00:00Z' interval_start = '2014-05-24T00:00:00Z'
interval_end = '2014-06-01T00:00:00Z' interval_end = '2014-06-01T00:00:00Z'
G(models.CourseActivityByWeek, course=self.course, interval_start=interval_start, interval_end=interval_end, G(models.CourseActivityByWeek, course_id=self.course_id, interval_start=interval_start, interval_end=interval_end,
activity_type='posted_forum', count=100) activity_type='posted_forum', count=100)
G(models.CourseActivityByWeek, course=self.course, interval_start=interval_start, interval_end=interval_end, G(models.CourseActivityByWeek, course_id=self.course_id, interval_start=interval_start, interval_end=interval_end,
activity_type='attempted_problem', count=200) activity_type='attempted_problem', count=200)
G(models.CourseActivityByWeek, course=self.course, interval_start=interval_start, interval_end=interval_end, G(models.CourseActivityByWeek, course_id=self.course_id, interval_start=interval_start, interval_end=interval_end,
activity_type='any', count=300) activity_type='any', count=300)
G(models.CourseActivityByWeek, course=self.course, interval_start=interval_start, interval_end=interval_end, G(models.CourseActivityByWeek, course_id=self.course_id, interval_start=interval_start, interval_end=interval_end,
activity_type='played_video', count=400) activity_type='played_video', count=400)
def test_activity(self): def test_activity(self):
...@@ -86,27 +86,18 @@ class CourseEnrollmentViewTestCase(object): ...@@ -86,27 +86,18 @@ class CourseEnrollmentViewTestCase(object):
path = None path = None
order_by = [] order_by = []
def _get_non_existent_course_id(self):
course_id = random.randint(100, 9999)
if not models.Course.objects.filter(course_id=course_id).exists():
return course_id
return self._get_non_existent_course_id()
def get_expected_response(self, *args): def get_expected_response(self, *args):
raise NotImplementedError raise NotImplementedError
def test_get_not_found(self): def test_get_not_found(self):
""" Requests made against non-existent courses should return a 404 """ """ Requests made against non-existent courses should return a 404 """
course_id = self._get_non_existent_course_id() course_id = 'edX/DemoX/Non_Existent_Course'
self.assertFalse(self.model.objects.filter(course__course_id=course_id).exists())
response = self.authenticated_get('/api/v0/courses/%s%s' % (course_id, self.path)) response = self.authenticated_get('/api/v0/courses/%s%s' % (course_id, self.path))
self.assertEquals(response.status_code, 404) self.assertEquals(response.status_code, 404)
def test_get(self): def test_get(self):
# Validate the basic response status # Validate the basic response status
response = self.authenticated_get('/api/v0/courses/%s%s' % (self.course.course_id, self.path,)) response = self.authenticated_get('/api/v0/courses/%s%s' % (self.course_id, self.path,))
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
# Validate the data is correct and sorted chronologically # Validate the data is correct and sorted chronologically
...@@ -114,7 +105,7 @@ class CourseEnrollmentViewTestCase(object): ...@@ -114,7 +105,7 @@ class CourseEnrollmentViewTestCase(object):
self.assertEquals(response.data, expected) self.assertEquals(response.data, expected)
def test_get_csv(self): def test_get_csv(self):
path = '/api/v0/courses/%s%s' % (self.course.course_id, self.path,) path = '/api/v0/courses/%s%s' % (self.course_id, self.path,)
csv_content_type = 'text/csv' csv_content_type = 'text/csv'
response = self.authenticated_get(path, HTTP_ACCEPT=csv_content_type) response = self.authenticated_get(path, HTTP_ACCEPT=csv_content_type)
...@@ -142,17 +133,15 @@ class CourseEnrollmentViewTestCase(object): ...@@ -142,17 +133,15 @@ class CourseEnrollmentViewTestCase(object):
self.assertIntervalFilteringWorks(expected, self.date, self.date + datetime.timedelta(days=1)) self.assertIntervalFilteringWorks(expected, self.date, self.date + datetime.timedelta(days=1))
def assertIntervalFilteringWorks(self, expected_response, start_date, end_date): def assertIntervalFilteringWorks(self, expected_response, start_date, end_date):
course = self.course
# If start date is after date of existing data, no data should be returned # If start date is after date of existing data, no data should be returned
date = (start_date + datetime.timedelta(days=30)).strftime(settings.DATE_FORMAT) date = (start_date + datetime.timedelta(days=30)).strftime(settings.DATE_FORMAT)
response = self.authenticated_get('/api/v0/courses/%s%s?start_date=%s' % (course.course_id, self.path, date)) response = self.authenticated_get('/api/v0/courses/%s%s?start_date=%s' % (self.course_id, self.path, date))
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assertListEqual([], response.data) self.assertListEqual([], response.data)
# If end date is before date of existing data, no data should be returned # If end date is before date of existing data, no data should be returned
date = (start_date - datetime.timedelta(days=30)).strftime(settings.DATE_FORMAT) date = (start_date - datetime.timedelta(days=30)).strftime(settings.DATE_FORMAT)
response = self.authenticated_get('/api/v0/courses/%s%s?end_date=%s' % (course.course_id, self.path, date)) response = self.authenticated_get('/api/v0/courses/%s%s?end_date=%s' % (self.course_id, self.path, date))
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assertListEqual([], response.data) self.assertListEqual([], response.data)
...@@ -160,7 +149,7 @@ class CourseEnrollmentViewTestCase(object): ...@@ -160,7 +149,7 @@ class CourseEnrollmentViewTestCase(object):
start_date = start_date.strftime(settings.DATE_FORMAT) start_date = start_date.strftime(settings.DATE_FORMAT)
end_date = end_date.strftime(settings.DATE_FORMAT) end_date = end_date.strftime(settings.DATE_FORMAT)
response = self.authenticated_get( response = self.authenticated_get(
'/api/v0/courses/%s%s?start_date=%s&end_date=%s' % (course.course_id, self.path, start_date, end_date)) '/api/v0/courses/%s%s?start_date=%s&end_date=%s' % (self.course_id, self.path, start_date, end_date))
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
self.assertListEqual(response.data, expected_response) self.assertListEqual(response.data, expected_response)
...@@ -172,20 +161,20 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr ...@@ -172,20 +161,20 @@ class CourseEnrollmentByBirthYearViewTests(TestCaseWithAuthentication, CourseEnr
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.course = G(models.Course) cls.course_id = 'edX/DemoX/Demo_Course'
cls.date = datetime.date(2014, 1, 1) cls.date = datetime.date(2014, 1, 1)
G(cls.model, course=cls.course, date=cls.date, birth_year=1956) G(cls.model, course_id=cls.course_id, date=cls.date, birth_year=1956)
G(cls.model, course=cls.course, date=cls.date, birth_year=1986) G(cls.model, course_id=cls.course_id, date=cls.date, birth_year=1986)
G(cls.model, course=cls.course, date=cls.date - datetime.timedelta(days=10), birth_year=1956) G(cls.model, course_id=cls.course_id, date=cls.date - datetime.timedelta(days=10), birth_year=1956)
G(cls.model, course=cls.course, date=cls.date - datetime.timedelta(days=10), birth_year=1986) G(cls.model, course_id=cls.course_id, date=cls.date - datetime.timedelta(days=10), birth_year=1986)
def get_expected_response(self, *args): def get_expected_response(self, *args):
return [ return [
{'course_id': str(ce.course.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT), {'course_id': str(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT),
'birth_year': ce.birth_year} for ce in args] 'birth_year': ce.birth_year} for ce in args]
def test_get(self): def test_get(self):
response = self.authenticated_get('/api/v0/courses/%s%s' % (self.course.course_id, self.path,)) response = self.authenticated_get('/api/v0/courses/%s%s' % (self.course_id, self.path,))
self.assertEquals(response.status_code, 200) self.assertEquals(response.status_code, 200)
expected = self.get_expected_response(*self.model.objects.filter(date=self.date)) expected = self.get_expected_response(*self.model.objects.filter(date=self.date))
...@@ -205,16 +194,16 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr ...@@ -205,16 +194,16 @@ class CourseEnrollmentByEducationViewTests(TestCaseWithAuthentication, CourseEnr
def setUpClass(cls): def setUpClass(cls):
cls.el1 = G(models.EducationLevel, name='Doctorate', short_name='doctorate') cls.el1 = G(models.EducationLevel, name='Doctorate', short_name='doctorate')
cls.el2 = G(models.EducationLevel, name='Top Secret', short_name='top_secret') cls.el2 = G(models.EducationLevel, name='Top Secret', short_name='top_secret')
cls.course = G(models.Course) cls.course_id = 'edX/DemoX/Demo_Course'
cls.date = datetime.date(2014, 1, 1) cls.date = datetime.date(2014, 1, 1)
G(cls.model, course=cls.course, date=cls.date, education_level=cls.el1) G(cls.model, course_id=cls.course_id, date=cls.date, education_level=cls.el1)
G(cls.model, course=cls.course, date=cls.date, education_level=cls.el2) G(cls.model, course_id=cls.course_id, date=cls.date, education_level=cls.el2)
G(cls.model, course=cls.course, date=cls.date - datetime.timedelta(days=2), G(cls.model, course_id=cls.course_id, date=cls.date - datetime.timedelta(days=2),
education_level=cls.el2) education_level=cls.el2)
def get_expected_response(self, *args): def get_expected_response(self, *args):
return [ return [
{'course_id': str(ce.course.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT), {'course_id': str(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT),
'education_level': {'name': ce.education_level.name, 'short_name': ce.education_level.short_name}} for 'education_level': {'name': ce.education_level.name, 'short_name': ce.education_level.short_name}} for
ce in args] ce in args]
...@@ -226,15 +215,15 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll ...@@ -226,15 +215,15 @@ class CourseEnrollmentByGenderViewTests(TestCaseWithAuthentication, CourseEnroll
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.course = G(models.Course) cls.course_id = 'edX/DemoX/Demo_Course'
cls.date = datetime.date(2014, 1, 1) cls.date = datetime.date(2014, 1, 1)
G(cls.model, course=cls.course, gender='m', date=cls.date, count=34) G(cls.model, course_id=cls.course_id, gender='m', date=cls.date, count=34)
G(cls.model, course=cls.course, gender='f', date=cls.date, count=45) G(cls.model, course_id=cls.course_id, gender='f', date=cls.date, count=45)
G(cls.model, course=cls.course, gender='f', date=cls.date - datetime.timedelta(days=2), count=45) G(cls.model, course_id=cls.course_id, gender='f', date=cls.date - datetime.timedelta(days=2), count=45)
def get_expected_response(self, *args): def get_expected_response(self, *args):
return [ return [
{'course_id': str(ce.course.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT), {'course_id': str(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT),
'gender': ce.gender} for ce in args] 'gender': ce.gender} for ce in args]
...@@ -275,14 +264,14 @@ class CourseEnrollmentViewTests(TestCaseWithAuthentication, CourseEnrollmentView ...@@ -275,14 +264,14 @@ class CourseEnrollmentViewTests(TestCaseWithAuthentication, CourseEnrollmentView
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.course = G(models.Course) cls.course_id = 'edX/DemoX/Demo_Course'
cls.date = datetime.date(2014, 1, 1) cls.date = datetime.date(2014, 1, 1)
G(cls.model, course=cls.course, date=cls.date, count=203) G(cls.model, course_id=cls.course_id, date=cls.date, count=203)
G(cls.model, course=cls.course, date=cls.date - datetime.timedelta(days=5), count=203) G(cls.model, course_id=cls.course_id, date=cls.date - datetime.timedelta(days=5), count=203)
def get_expected_response(self, *args): def get_expected_response(self, *args):
return [ return [
{'course_id': str(ce.course.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT)} {'course_id': str(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT)}
for ce in args] for ce in args]
...@@ -291,15 +280,16 @@ class CourseEnrollmentByLocationViewTests(TestCaseWithAuthentication, CourseEnro ...@@ -291,15 +280,16 @@ class CourseEnrollmentByLocationViewTests(TestCaseWithAuthentication, CourseEnro
model = models.CourseEnrollmentByCountry model = models.CourseEnrollmentByCountry
def get_expected_response(self, *args): def get_expected_response(self, *args):
args = sorted(args, key=lambda item: (item.date, item.course_id, item.country.code))
return [ return [
{'course_id': str(ce.course.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT), {'course_id': str(ce.course_id), 'count': ce.count, 'date': ce.date.strftime(settings.DATE_FORMAT),
'country': {'code': ce.country.code, 'name': ce.country.name}} for ce in args] 'country': {'code': ce.country.code, 'name': ce.country.name}} for ce in args]
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.course = G(models.Course) cls.course_id = 'edX/DemoX/Demo_Course'
cls.date = datetime.date(2014, 1, 1) cls.date = datetime.date(2014, 1, 1)
G(cls.model, course=cls.course, country=G(models.Country), count=455, date=cls.date) cls.country = countries.get('US')
G(cls.model, course=cls.course, country=G(models.Country), count=356, date=cls.date) G(cls.model, course_id=cls.course_id, country_code='US', count=455, date=cls.date)
G(cls.model, course=cls.course, country=G(models.Country), count=12, G(cls.model, course_id=cls.course_id, country_code='CA', count=356, date=cls.date)
date=cls.date - datetime.timedelta(days=29)) G(cls.model, course_id=cls.course_id, country_code='IN', count=12, date=cls.date - datetime.timedelta(days=29))
...@@ -5,7 +5,6 @@ from django.core.exceptions import ObjectDoesNotExist ...@@ -5,7 +5,6 @@ from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Max from django.db.models import Max
from django.http import Http404 from django.http import Http404
from rest_framework import generics from rest_framework import generics
from rest_framework.generics import get_object_or_404
from analytics_data_api.v0 import models, serializers from analytics_data_api.v0 import models, serializers
...@@ -53,8 +52,11 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView): ...@@ -53,8 +52,11 @@ class CourseActivityMostRecentWeekView(generics.RetrieveAPIView):
class BaseCourseEnrollmentView(generics.ListAPIView): class BaseCourseEnrollmentView(generics.ListAPIView):
def get_course_or_404(self): def verify_course_exists_or_404(self, course_id):
return get_object_or_404(models.Course, course_id=self.kwargs.get('course_id')) if self.model.objects.filter(course_id=course_id).exists():
return True
raise Http404
def apply_date_filtering(self, queryset): def apply_date_filtering(self, queryset):
if 'start_date' in self.request.QUERY_PARAMS or 'end_date' in self.request.QUERY_PARAMS: if 'start_date' in self.request.QUERY_PARAMS or 'end_date' in self.request.QUERY_PARAMS:
...@@ -77,8 +79,9 @@ class BaseCourseEnrollmentView(generics.ListAPIView): ...@@ -77,8 +79,9 @@ class BaseCourseEnrollmentView(generics.ListAPIView):
return queryset return queryset
def get_queryset(self): def get_queryset(self):
course = self.get_course_or_404() course_id = self.kwargs.get('course_id')
queryset = self.model.objects.filter(course=course) self.verify_course_exists_or_404(course_id)
queryset = self.model.objects.filter(course_id=course_id)
queryset = self.apply_date_filtering(queryset) queryset = self.apply_date_filtering(queryset)
return queryset return queryset
...@@ -185,9 +188,4 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView): ...@@ -185,9 +188,4 @@ class CourseEnrollmentByLocationView(BaseCourseEnrollmentView):
""" """
serializer_class = serializers.CourseEnrollmentByCountrySerializer serializer_class = serializers.CourseEnrollmentByCountrySerializer
model = models.CourseEnrollmentByCountry
def get_queryset(self):
course = self.get_course_or_404()
queryset = models.CourseEnrollmentByCountry.objects.filter(course=course)
queryset = self.apply_date_filtering(queryset)
return queryset
...@@ -6,3 +6,4 @@ djangorestframework==2.3.5 ...@@ -6,3 +6,4 @@ djangorestframework==2.3.5
ipython==2.1.0 ipython==2.1.0
django-rest-swagger==0.1.14 django-rest-swagger==0.1.14
djangorestframework-csv==1.3.3 djangorestframework-csv==1.3.3
iso3166==0.1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment