Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
e8196d97
Commit
e8196d97
authored
Nov 19, 2013
by
Julia Hansbrough
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Events for entering verified flow & buying
parent
09befd2a
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
188 additions
and
10 deletions
+188
-10
common/djangoapps/course_modes/views.py
+5
-1
common/djangoapps/student/models.py
+6
-2
common/djangoapps/student/tests/tests.py
+8
-1
common/djangoapps/track/contexts.py
+10
-0
lms/djangoapps/courseware/features/certificates.feature
+2
-0
lms/djangoapps/shoppingcart/migrations/0005_auto__add_field_certificateitem_upgrade.py
+116
-0
lms/djangoapps/shoppingcart/models.py
+16
-4
lms/djangoapps/shoppingcart/tests/test_models.py
+24
-2
lms/djangoapps/shoppingcart/tests/test_views.py
+1
-0
No files found.
common/djangoapps/course_modes/views.py
View file @
e8196d97
...
...
@@ -17,10 +17,12 @@ from edxmako.shortcuts import render_to_response
from
course_modes.models
import
CourseMode
from
courseware.access
import
has_access
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
,
UserMethods
from
student.views
import
course_from_id
from
verify_student.models
import
SoftwareSecurePhotoVerification
EVENT_NAME_USER_CLICKED_UPGRADE
=
'edx.user.upgrade.clicked'
class
ChooseModeView
(
View
):
"""
...
...
@@ -37,6 +39,8 @@ class ChooseModeView(View):
enrollment_mode
=
CourseEnrollment
.
enrollment_mode_for_user
(
request
.
user
,
course_id
)
upgrade
=
request
.
GET
.
get
(
'upgrade'
,
False
)
if
upgrade
==
"True"
:
UserMethods
.
emit_event
(
request
.
user
,
course_id
,
EVENT_NAME_USER_CLICKED_UPGRADE
)
# verified users do not need to register or upgrade
if
enrollment_mode
==
'verified'
:
...
...
common/djangoapps/student/models.py
View file @
e8196d97
...
...
@@ -16,6 +16,12 @@ import json
import
logging
import
uuid
import
crum
from
track
import
contexts
from
track.views
import
server_track
from
eventtracking
import
tracker
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.signals
import
user_logged_in
,
user_logged_out
...
...
@@ -35,9 +41,7 @@ from track import contexts
from
track.views
import
server_track
from
eventtracking
import
tracker
unenroll_done
=
Signal
(
providing_args
=
[
"course_enrollment"
])
log
=
logging
.
getLogger
(
__name__
)
AUDIT_LOG
=
logging
.
getLogger
(
"audit"
)
...
...
common/djangoapps/student/tests/tests.py
View file @
e8196d97
...
...
@@ -340,7 +340,14 @@ class EnrollInCourseTest(TestCase):
user
=
user
,
course_id
=
course_id
)
self
.
assertFalse
(
enrollment_record
.
is_active
)
def
test_user_emitted_events
(
self
):
user
=
User
.
objects
.
create_user
(
"joe"
,
"joe@joe.com"
,
"password"
)
course_id
=
"edX/Test101/2013"
course_id_partial
=
"edX/Test101"
with
patch
(
'eventtracking.tracker.get_tracker'
,
side_effect
=
Exception
):
UserMethods
.
emit_event
(
user
,
course_id
,
"fake"
)
self
.
assertTrue
(
True
)
# Make sure mode is updated properly if user unenrolls & re-enrolls
enrollment
=
CourseEnrollment
.
enroll
(
user
,
course_id
,
"verified"
)
...
...
common/djangoapps/track/contexts.py
View file @
e8196d97
...
...
@@ -57,3 +57,13 @@ def course_context_from_course_id(course_id):
)
return
context
def
user_context
(
user
):
"""
Creates a user context from `user`
"""
context
=
{
'user'
:
user
,
}
return
context
lms/djangoapps/courseware/features/certificates.feature
View file @
e8196d97
...
...
@@ -94,5 +94,7 @@ Feature: LMS.Verified certificates
And
I navigate to my dashboard
Then
I see the course on my dashboard
And
I see that I am on the verified track
And
a
"edx.user.upgrade.clicked"
server event is emitted
And
a
"edx.user.upgrade.purchased"
sever event is emitted
And
a
"edx.course.enrollment.activated"
server event is emitted
lms/djangoapps/shoppingcart/migrations/0005_auto__add_field_certificateitem_upgrade.py
0 → 100644
View file @
e8196d97
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding field 'CertificateItem.upgrade'
db
.
add_column
(
'shoppingcart_certificateitem'
,
'upgrade'
,
self
.
gf
(
'django.db.models.fields.BooleanField'
)(
default
=
False
),
keep_default
=
False
)
def
backwards
(
self
,
orm
):
# Deleting field 'CertificateItem.upgrade'
db
.
delete_column
(
'shoppingcart_certificateitem'
,
'upgrade'
)
models
=
{
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'shoppingcart.certificateitem'
:
{
'Meta'
:
{
'object_name'
:
'CertificateItem'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_enrollment'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['student.CourseEnrollment']"
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
}),
'upgrade'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
})
},
'shoppingcart.order'
:
{
'Meta'
:
{
'object_name'
:
'Order'
},
'bill_to_cardtype'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'blank'
:
'True'
}),
'bill_to_ccnum'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'8'
,
'blank'
:
'True'
}),
'bill_to_city'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_country'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_first'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_last'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'64'
,
'blank'
:
'True'
}),
'bill_to_postalcode'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'16'
,
'blank'
:
'True'
}),
'bill_to_state'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'8'
,
'blank'
:
'True'
}),
'bill_to_street1'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'bill_to_street2'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'blank'
:
'True'
}),
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'processor_reply_dump'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'purchase_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
,
'blank'
:
'True'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.orderitem'
:
{
'Meta'
:
{
'object_name'
:
'OrderItem'
},
'currency'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'usd'"
,
'max_length'
:
'8'
}),
'fulfilled_time'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'null'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'line_desc'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'Misc. Item'"
,
'max_length'
:
'1024'
}),
'order'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['shoppingcart.Order']"
}),
'qty'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'default'
:
'1'
}),
'status'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'cart'"
,
'max_length'
:
'32'
}),
'unit_cost'
:
(
'django.db.models.fields.DecimalField'
,
[],
{
'default'
:
'0.0'
,
'max_digits'
:
'30'
,
'decimal_places'
:
'2'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
},
'shoppingcart.paidcourseregistration'
:
{
'Meta'
:
{
'object_name'
:
'PaidCourseRegistration'
,
'_ormbases'
:
[
'shoppingcart.OrderItem'
]},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
,
'db_index'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'50'
}),
'orderitem_ptr'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['shoppingcart.OrderItem']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
})
},
'student.courseenrollment'
:
{
'Meta'
:
{
'ordering'
:
"('user', 'course_id')"
,
'unique_together'
:
"(('user', 'course_id'),)"
,
'object_name'
:
'CourseEnrollment'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'auto_now_add'
:
'True'
,
'null'
:
'True'
,
'db_index'
:
'True'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'mode'
:
(
'django.db.models.fields.CharField'
,
[],
{
'default'
:
"'honor'"
,
'max_length'
:
'100'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.User']"
})
}
}
complete_apps
=
[
'shoppingcart'
]
\ No newline at end of file
lms/djangoapps/shoppingcart/models.py
View file @
e8196d97
...
...
@@ -25,7 +25,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from
course_modes.models
import
CourseMode
from
edxmako.shortcuts
import
render_to_string
from
student.views
import
course_from_id
from
student.models
import
CourseEnrollment
,
unenroll_done
from
student.models
import
CourseEnrollment
,
unenroll_done
,
UserMethods
from
verify_student.models
import
SoftwareSecurePhotoVerification
...
...
@@ -43,6 +43,8 @@ ORDER_STATUSES = (
# we need a tuple to represent the primary key of various OrderItem subclasses
OrderItemSubclassPK
=
namedtuple
(
'OrderItemSubclassPK'
,
[
'cls'
,
'pk'
])
# pylint: disable=C0103
EVENT_NAME_USER_UPGRADED
=
'edx.user.upgrade.purchased'
class
Order
(
models
.
Model
):
"""
...
...
@@ -489,6 +491,7 @@ class CertificateItem(OrderItem):
course_id
=
models
.
CharField
(
max_length
=
128
,
db_index
=
True
)
course_enrollment
=
models
.
ForeignKey
(
CourseEnrollment
)
mode
=
models
.
SlugField
()
upgrade
=
models
.
BooleanField
()
@receiver
(
unenroll_done
)
def
refund_cert_callback
(
sender
,
course_enrollment
=
None
,
**
kwargs
):
...
...
@@ -557,7 +560,14 @@ class CertificateItem(OrderItem):
"""
super
(
CertificateItem
,
cls
)
.
add_to_order
(
order
,
course_id
,
cost
,
currency
=
currency
)
course_enrollment
=
CourseEnrollment
.
get_or_create_enrollment
(
order
.
user
,
course_id
)
try
:
# If a course_enrollment already exists, this is an "upgrade" order
course_enrollment
=
CourseEnrollment
.
objects
.
get
(
user
=
order
.
user
,
course_id
=
course_id
)
upgrade
=
True
except
ObjectDoesNotExist
:
course_enrollment
=
CourseEnrollment
.
get_or_create_enrollment
(
order
.
user
,
course_id
)
upgrade
=
False
# do some validation on the enrollment mode
valid_modes
=
CourseMode
.
modes_for_course_dict
(
course_id
)
...
...
@@ -570,7 +580,8 @@ class CertificateItem(OrderItem):
user
=
order
.
user
,
course_id
=
course_id
,
course_enrollment
=
course_enrollment
,
mode
=
mode
mode
=
mode
,
upgrade
=
upgrade
,
)
item
.
status
=
order
.
status
item
.
qty
=
1
...
...
@@ -595,7 +606,8 @@ class CertificateItem(OrderItem):
log
.
exception
(
"Could not submit verification attempt for enrollment {}"
.
format
(
self
.
course_enrollment
)
)
if
self
.
upgrade
is
True
:
UserMethods
.
emit_event
(
self
.
user
,
self
.
course_enrollment
.
course_id
,
EVENT_NAME_USER_UPGRADED
)
self
.
course_enrollment
.
change_mode
(
self
.
mode
)
self
.
course_enrollment
.
activate
()
...
...
lms/djangoapps/shoppingcart/tests/test_models.py
View file @
e8196d97
...
...
@@ -6,7 +6,7 @@ import StringIO
from
textwrap
import
dedent
from
boto.exception
import
BotoServerError
# this is a super-class of SESError and catches connection errors
from
mock
import
patch
,
MagicMock
from
mock
import
patch
,
MagicMock
,
sentinel
from
django.core
import
mail
from
django.conf
import
settings
from
django.db
import
DatabaseError
...
...
@@ -425,15 +425,37 @@ class CertificateItemTest(ModuleStoreTestCase):
min_price
=
self
.
cost
)
course_mode
.
save
()
patcher
=
patch
(
'student.models.server_track'
)
self
.
mock_server_track
=
patcher
.
start
()
self
.
addCleanup
(
patcher
.
stop
)
crum_patcher
=
patch
(
'student.models.crum.get_current_request'
)
self
.
mock_get_current_request
=
crum_patcher
.
start
()
self
.
addCleanup
(
crum_patcher
.
stop
)
self
.
mock_get_current_request
.
return_value
=
sentinel
.
request
def
test_existing_enrollment
(
self
):
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course_id
)
enrollment
=
CourseEnrollment
.
enroll
(
self
.
user
,
self
.
course_id
)
cart
=
Order
.
get_cart_for_user
(
user
=
self
.
user
)
CertificateItem
.
add_to_order
(
cart
,
self
.
course_id
,
self
.
cost
,
'verified'
)
# verify that we are still enrolled
self
.
assertTrue
(
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_id
))
self
.
mock_server_track
.
reset_mock
()
cart
.
purchase
()
enrollment
=
CourseEnrollment
.
objects
.
get
(
user
=
self
.
user
,
course_id
=
self
.
course_id
)
self
.
assertEquals
(
enrollment
.
mode
,
u'verified'
)
self
.
assert_upgrade_event_was_emitted
(
self
.
user
,
self
.
course_id
)
def
assert_upgrade_event_was_emitted
(
self
,
user
,
course_id
):
""" Helper function; checks that a particular was called only once """
self
.
mock_server_track
.
assert_called_once_with
(
sentinel
.
request
,
'edx.user.upgrade.purchased'
,
{
'user'
:
user
,
'course_id'
:
course_id
,
}
)
self
.
mock_server_track
.
reset_mock
()
def
test_single_item_template
(
self
):
cart
=
Order
.
get_cart_for_user
(
user
=
self
.
user
)
...
...
lms/djangoapps/shoppingcart/tests/test_views.py
View file @
e8196d97
...
...
@@ -83,6 +83,7 @@ class ShoppingCartViewsTests(ModuleStoreTestCase):
def
test_add_course_to_cart_success
(
self
):
self
.
login_user
()
reverse
(
'shoppingcart.views.add_course_to_cart'
,
args
=
[
self
.
course_id
])
resp
=
self
.
client
.
post
(
reverse
(
'shoppingcart.views.add_course_to_cart'
,
args
=
[
self
.
course_id
]))
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertTrue
(
PaidCourseRegistration
.
contained_in_order
(
self
.
cart
,
self
.
course_id
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment