diff --git a/lms/envs/common.py b/lms/envs/common.py index 615fcd6149..90b2c457f0 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -3510,6 +3510,7 @@ ACCOUNT_VISIBILITY_CONFIGURATION["admin_fields"] = ( "state", "goals", "is_active", + "last_login", "mailing_address", "requires_parental_consent", "secondary_email", diff --git a/openedx/core/djangoapps/user_api/accounts/serializers.py b/openedx/core/djangoapps/user_api/accounts/serializers.py index 390e16bf8b..d3023f934c 100644 --- a/openedx/core/djangoapps/user_api/accounts/serializers.py +++ b/openedx/core/djangoapps/user_api/accounts/serializers.py @@ -123,7 +123,6 @@ class UserReadOnlySerializer(serializers.Serializer): account_recovery = None accomplishments_shared = badges_enabled() - data = { "username": user.username, "url": self.context.get('request').build_absolute_uri( @@ -135,6 +134,7 @@ class UserReadOnlySerializer(serializers.Serializer): # DRF JSONEncoder will not include it in the serialized value. # https://docs.djangoproject.com/en/1.8/ref/databases/#fractional-seconds-support-for-time-and-datetime-fields "date_joined": user.date_joined.replace(microsecond=0), + "last_login": user.last_login, "is_active": user.is_active, "bio": None, "country": None, diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py index bc3f1789d5..2f05528375 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py @@ -522,9 +522,11 @@ class AccountSettingsOnCreationTest(CreateAccountMixin, TestCase): request.user = user account_settings = get_account_settings(request)[0] - # Expect a date joined field but remove it to simplify the following comparison + # Expect a date joined and last login field but remove it to simplify the following comparison self.assertIsNotNone(account_settings['date_joined']) + self.assertIsNotNone(account_settings['last_login']) del account_settings['date_joined'] + del account_settings['last_login'] # Expect all the values to be defaulted self.assertEqual(account_settings, { diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py index f789f1eea7..867176e83a 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py @@ -267,7 +267,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): Verify that all account fields are returned (even those that are not shareable). """ data = response.data - self.assertEqual(25, len(data)) + self.assertEqual(26, len(data)) # public fields (3) expected_account_privacy = ( @@ -282,6 +282,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): self.assertEqual(TEST_BIO_VALUE, data["bio"]) self.assertEqual("US", data["country"]) self.assertIsNotNone(data["date_joined"]) + self.assertIsNotNone(data["last_login"]) self.assertEqual([{"code": TEST_LANGUAGE_PROFICIENCY_CODE}], data["language_proficiencies"]) self.assertEqual("m", data["level_of_education"]) self.assertIsNotNone(data["social_links"]) @@ -497,7 +498,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): with self.assertNumQueries(queries): response = self.send_get(self.client) data = response.data - self.assertEqual(25, len(data)) + self.assertEqual(26, len(data)) self.assertEqual(self.user.username, data["username"]) self.assertEqual(self.user.first_name + " " + self.user.last_name, data["name"]) for empty_field in ("year_of_birth", "level_of_education", "mailing_address", "bio"): @@ -508,6 +509,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): self.assertEqual("Learn a lot", data["goals"]) self.assertEqual(self.user.email, data["email"]) self.assertIsNotNone(data["date_joined"]) + self.assertIsNotNone(data["last_login"]) self.assertEqual(self.user.is_active, data["is_active"]) self._verify_profile_image_data(data, False) self.assertTrue(data["requires_parental_consent"]) @@ -916,7 +918,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): response = self.send_get(client) if has_full_access: data = response.data - self.assertEqual(25, len(data)) + self.assertEqual(26, len(data)) self.assertEqual(self.user.username, data["username"]) self.assertEqual(self.user.first_name + " " + self.user.last_name, data["name"]) self.assertEqual(self.user.email, data["email"]) @@ -927,6 +929,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): self.assertEqual("Learn a lot", data["goals"]) self.assertTrue(data["is_active"]) self.assertIsNotNone(data["date_joined"]) + self.assertIsNotNone(data["last_login"]) self._verify_profile_image_data(data, False) self.assertTrue(data["requires_parental_consent"]) self.assertEqual(PRIVATE_VISIBILITY, data["account_privacy"]) diff --git a/openedx/core/djangoapps/user_api/accounts/views.py b/openedx/core/djangoapps/user_api/accounts/views.py index 27ea1bdc1f..4ca93c57f1 100644 --- a/openedx/core/djangoapps/user_api/accounts/views.py +++ b/openedx/core/djangoapps/user_api/accounts/views.py @@ -169,6 +169,7 @@ class AccountViewSet(ViewSet): * country: An ISO 3166 country code or null. * date_joined: The date the account was created, in the string format provided by datetime. For example, "2014-08-26T17:52:11Z". + * last_login: The latest date the user logged in, in the string datetime format. * email: Email address for the user. New email addresses must be confirmed via a confirmation email, so GET does not reflect the change until the address has been confirmed.