From a4df4fc30edc942689a6b5e38c596ff789e275ce Mon Sep 17 00:00:00 2001 From: Mohammad Nadeem Date: Wed, 7 Mar 2018 21:20:24 +0500 Subject: [PATCH 1/9] Fix OEF and team bugs (#372) --- lms/djangoapps/oef/views.py | 10 ++++++---- .../teams/static/teams/js/views/team_profile.js | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lms/djangoapps/oef/views.py b/lms/djangoapps/oef/views.py index e13aa144fb5f..5de1abe901c3 100644 --- a/lms/djangoapps/oef/views.py +++ b/lms/djangoapps/oef/views.py @@ -18,11 +18,11 @@ def oef_dashboard(request): View for OEF dashboard """ - user_surveys = OrganizationOefScore.objects.filter(user_id=request.user.id) surveys = [] user_survey_status = get_user_survey_status(request.user, create_new_survey=False) user_extended_profile = request.user.extended_profile is_first_user = user_extended_profile.is_first_signup_in_org if user_extended_profile.organization else False + user_surveys = OrganizationOefScore.objects.filter(org=user_extended_profile.organization) context = { 'user_has_organization': bool(user_extended_profile.organization), @@ -63,8 +63,9 @@ def get_survey_by_id(request, user_survey_id): """ Get a particular survey by its id """ + organization = request.user.extended_profile.organization - uos = OrganizationOefScore.objects.get(id=int(user_survey_id), user_id=request.user.id) + uos = OrganizationOefScore.objects.get(id=int(user_survey_id), org=organization) survey = OefSurvey.objects.filter(is_enabled=True).latest('created') topics = get_survey_topics(uos, survey.id) levels = get_option_levels() @@ -74,7 +75,7 @@ def get_survey_by_id(request, user_survey_id): "topics": topics, "instructions": get_oef_instructions(), "levels": levels, - 'organization': request.user.extended_profile.organization.label, + 'organization': organization.label, 'date': uos.modified.strftime('%m/%d/%Y') }) @@ -112,7 +113,8 @@ def save_answer(request): Save answers submitted by user """ data = json.loads(request.body) - uos = OrganizationOefScore.objects.filter(user_id=request.user.id).latest('start_date') + organization = request.user.extended_profile.organization + uos = OrganizationOefScore.objects.filter(org=organization).latest('start_date') for answer_data in data['answers']: setattr(uos, answer_data['score_name'], int(float(answer_data['answer_id']))) diff --git a/lms/djangoapps/teams/static/teams/js/views/team_profile.js b/lms/djangoapps/teams/static/teams/js/views/team_profile.js index 280ea88be88f..96b5f32e3d99 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_profile.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_profile.js @@ -65,11 +65,13 @@ var teamDiscussionLink = nodeBBUrl + '/chats/' + rooms[activeTeam]; var discussionLinkElem = document.getElementById('discussion-link'); - if (rooms[activeTeam] && memberships > 1) { - discussionLinkElem.href = teamDiscussionLink; - } else { - discussionLinkElem.href = "javascript:void(0)" + discussionLinkElem.href = 'javascript:void(0)'; + if (!rooms[activeTeam]) { + discussionLinkElem.innerHTML = ''; + } else if (rooms[activeTeam] && memberships <= 1) { discussionLinkElem.innerHTML = '

Not enough members to start a discussion

' + } else { + discussionLinkElem.href = teamDiscussionLink; } this.setFocusToHeaderFunc(); From 6ebf7ff92caf6be0bd4bb43dea40c523c5712971 Mon Sep 17 00:00:00 2001 From: Mohammad Nadeem Date: Thu, 8 Mar 2018 14:15:22 +0500 Subject: [PATCH 2/9] Mnadeem/revert oef (#377) * Show previously submitted OEFs --- lms/djangoapps/oef/views.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lms/djangoapps/oef/views.py b/lms/djangoapps/oef/views.py index e13aa144fb5f..4355d7a81277 100644 --- a/lms/djangoapps/oef/views.py +++ b/lms/djangoapps/oef/views.py @@ -18,10 +18,14 @@ def oef_dashboard(request): View for OEF dashboard """ - user_surveys = OrganizationOefScore.objects.filter(user_id=request.user.id) + user_extended_profile = request.user.extended_profile + user_surveys = list(OrganizationOefScore.objects.filter(user_id=request.user.id)) + organization_surveys = list(OrganizationOefScore.objects.filter(org=user_extended_profile.organization).exclude(finish_date__isnull=True)) + user_surveys = set(user_surveys + organization_surveys) + surveys = [] user_survey_status = get_user_survey_status(request.user, create_new_survey=False) - user_extended_profile = request.user.extended_profile + is_first_user = user_extended_profile.is_first_signup_in_org if user_extended_profile.organization else False context = { @@ -63,8 +67,8 @@ def get_survey_by_id(request, user_survey_id): """ Get a particular survey by its id """ - - uos = OrganizationOefScore.objects.get(id=int(user_survey_id), user_id=request.user.id) + organization = request.user.extended_profile.organization + uos = OrganizationOefScore.objects.get(id=int(user_survey_id), org=organization) survey = OefSurvey.objects.filter(is_enabled=True).latest('created') topics = get_survey_topics(uos, survey.id) levels = get_option_levels() @@ -112,7 +116,7 @@ def save_answer(request): Save answers submitted by user """ data = json.loads(request.body) - uos = OrganizationOefScore.objects.filter(user_id=request.user.id).latest('start_date') + uos = OrganizationOefScore.objects.filter(user_id=request.user.id).latest('id') for answer_data in data['answers']: setattr(uos, answer_data['score_name'], int(float(answer_data['answer_id']))) From 80487f9bc04563cab942d02b48ba25d9128ee235 Mon Sep 17 00:00:00 2001 From: Muhammad Nadee Date: Thu, 8 Mar 2018 16:59:25 +0500 Subject: [PATCH 3/9] Fix incorrect OEF merge --- lms/djangoapps/oef/views.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lms/djangoapps/oef/views.py b/lms/djangoapps/oef/views.py index 5cd12959d7d7..a29242a1cc2d 100644 --- a/lms/djangoapps/oef/views.py +++ b/lms/djangoapps/oef/views.py @@ -9,7 +9,6 @@ from lms.djangoapps.oef.decorators import can_take_oef from lms.djangoapps.oef.helpers import * from lms.djangoapps.onboarding.models import Organization -from lms.djangoapps.onboarding.helpers import oef_eligible_first_learner @login_required @@ -27,7 +26,6 @@ def oef_dashboard(request): user_survey_status = get_user_survey_status(request.user, create_new_survey=False) is_first_user = user_extended_profile.is_first_signup_in_org if user_extended_profile.organization else False - user_surveys = OrganizationOefScore.objects.filter(org=user_extended_profile.organization) context = { 'user_has_organization': bool(user_extended_profile.organization), From 0ac64fac5157b61d5c9bb58212f4fa0b0ddfa3c4 Mon Sep 17 00:00:00 2001 From: Muhammad Osama Arshad Date: Thu, 8 Mar 2018 20:54:24 +0500 Subject: [PATCH 4/9] Fix: close p tag (#381) --- lms/djangoapps/teams/static/teams/js/views/team_card.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/djangoapps/teams/static/teams/js/views/team_card.js b/lms/djangoapps/teams/static/teams/js/views/team_card.js index a6f9ec169033..085c82698b09 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_card.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_card.js @@ -114,7 +114,7 @@ if (this.memberships > 1) { this.$el.html(this.template({roomID: this.roomID, teamID: this.teamID, nodeBBUrl: this.nodeBBUrl})); } else { - this.$el.html('

You can not start discussion until more members join this team.

'); + this.$el.html('

You can not start discussion until more members join this team.

'); } } }); From 9e406fadfe9767db5d5d22e4f4e44eafbdf77cc6 Mon Sep 17 00:00:00 2001 From: Muhammad Osama Arshad Date: Fri, 9 Mar 2018 19:11:05 +0500 Subject: [PATCH 5/9] Improve: get user profiles in course teams view (#382) * Improve: get user profiles in course teams view * Improve: improve docstring and add newline * Fix: improve docstring --- lms/djangoapps/philu_api/urls.py | 4 ++- lms/djangoapps/philu_api/views.py | 12 ++++++- .../static/teams/js/views/team_profile.js | 32 +++++++++++++------ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/lms/djangoapps/philu_api/urls.py b/lms/djangoapps/philu_api/urls.py index cc67ee079875..b57c92361be2 100644 --- a/lms/djangoapps/philu_api/urls.py +++ b/lms/djangoapps/philu_api/urls.py @@ -3,12 +3,14 @@ """ from django.conf.urls import url, patterns -from lms.djangoapps.philu_api.views import PlatformSyncService, get_user_chat, mark_user_chat_read +from lms.djangoapps.philu_api.views import PlatformSyncService, \ + get_user_chat, mark_user_chat_read, get_user_data urlpatterns = patterns( 'philu_api.views', url(r'platform/sync/service/', PlatformSyncService.as_view(), name='get_shared_data'), url(r'profile/update/', PlatformSyncService.as_view(), name='update_community_profile_update'), url(r'profile/chats/?$', get_user_chat, name='get_user_chat'), + url(r'profile/data/?$', get_user_data, name='get_user_data'), url(r'profile/chats/mark/?$', mark_user_chat_read, name='mark_user_chat_read'), ) diff --git a/lms/djangoapps/philu_api/views.py b/lms/djangoapps/philu_api/views.py index 642f35b81888..f6d1157dae08 100644 --- a/lms/djangoapps/philu_api/views.py +++ b/lms/djangoapps/philu_api/views.py @@ -54,7 +54,7 @@ def post(self, request): username = request.GET.get("username") email = request.GET.get("email") - + try: user = User.objects.get(email=email) except User.DoesNotExist: @@ -117,3 +117,13 @@ def mark_user_chat_read(request): data={'_uid': 1, 'username': username}, headers=headers) return JsonResponse(response.json()) + +def get_user_data(request): + """ Get the user profile data from NodeBB for current user """ + data_endpoint = settings.NODEBB_ENDPOINT + '/api/v2/users/data' + username = request.user.username + headers = {'Authorization': 'Bearer ' + settings.NODEBB_MASTER_TOKEN} + response = requests.post(data_endpoint, + data={'_uid': 1, 'username': username}, + headers=headers) + return JsonResponse(response.json()) diff --git a/lms/djangoapps/teams/static/teams/js/views/team_profile.js b/lms/djangoapps/teams/static/teams/js/views/team_profile.js index 96b5f32e3d99..a31980e5f7c2 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_profile.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_profile.js @@ -4,6 +4,7 @@ (function(define) { 'use strict'; define([ + 'jquery', 'backbone', 'underscore', 'gettext', @@ -14,7 +15,7 @@ 'text!teams/templates/team-profile.underscore', 'text!teams/templates/team-member.underscore' ], - function(Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils, + function($, Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils, teamTemplate, teamMemberTemplate) { var TeamProfileView = Backbone.View.extend({ @@ -81,14 +82,27 @@ renderTeamMembers: function() { var view = this; _.each(this.model.get('membership'), function(membership) { - HtmlUtils.append( - view.$('.members-info'), - HtmlUtils.template(teamMemberTemplate)({ - imageUrl: membership.user.profile_image.image_url_medium, - username: membership.user.username, - memberProfileUrl: '/u/' + membership.user.username - }) - ); + $.post({ + url: '/philu/api/profile/data', + data: { + username: membership.user.username + }, + success: function(data) { + HtmlUtils.append( + view.$('.members-info'), + HtmlUtils.template(teamMemberTemplate)({ + imageUrl: data.payload.picture !== "" ? + data.payload.picture : + membership.user.profile_image.image_url_medium, + username: membership.user.username, + memberProfileUrl: data.payload.profileUrl + }) + ); + }, + error: function(err) { + console.error("Error: " + err) + } + }); }); }, From 5ddbd1bb56895fca2f388292629764df6c1537e0 Mon Sep 17 00:00:00 2001 From: Muhammad Nadeem Date: Fri, 9 Mar 2018 20:12:36 +0500 Subject: [PATCH 6/9] Create history for org metrics & org partners --- lms/djangoapps/onboarding/admin.py | 9 ++++++- lms/djangoapps/onboarding/constants.py | 1 + lms/djangoapps/onboarding/forms.py | 7 ++--- lms/djangoapps/onboarding/models.py | 36 ++++++++++++++++++++++---- lms/djangoapps/onboarding/views.py | 8 +++--- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/lms/djangoapps/onboarding/admin.py b/lms/djangoapps/onboarding/admin.py index 97982c7a5938..60f9dfa340cc 100644 --- a/lms/djangoapps/onboarding/admin.py +++ b/lms/djangoapps/onboarding/admin.py @@ -13,6 +13,7 @@ Organization, UserExtendedProfile, FunctionArea, + OrganizationPartner, ) @@ -41,7 +42,7 @@ class TotalEmployeeAdmin(BaseDropdownOrderAdmin): class PartnerNetworkAdmin(BaseDropdownOrderAdmin): - pass + list_display = ('order', 'code', 'label', 'is_partner_affiliated') class EducationLevelAdmin(BaseDropdownOrderAdmin): @@ -68,6 +69,11 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): list_display = ('user', 'organization', 'country_of_employment', 'role_in_org', 'hours_per_week', ) +class OrganizationPartnerAdmin(admin.ModelAdmin): + list_display = ('organization', 'partner', 'start_date', 'end_date',) + list_filter = ('organization', 'partner', 'start_date', 'end_date',) + + admin.site.register(Currency, CurrencyAdmin) admin.site.register(RoleInsideOrg, RoleInsideOrgAdmin) admin.site.register(OrgSector, OrgSectorAdmin) @@ -80,3 +86,4 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): admin.site.register(Organization, OraganizationAdmin) admin.site.register(UserExtendedProfile, UserExtendedProfileAdmin) admin.site.register(FunctionArea, FunctionAreaAdmin) +admin.site.register(OrganizationPartner, OrganizationPartnerAdmin) diff --git a/lms/djangoapps/onboarding/constants.py b/lms/djangoapps/onboarding/constants.py index 6c33c4dc37aa..bdf1639637e6 100644 --- a/lms/djangoapps/onboarding/constants.py +++ b/lms/djangoapps/onboarding/constants.py @@ -1 +1,2 @@ ACTIVATION_ERROR = 'Your account has not been activated. Please check your email to activate your account.' +ORG_PARTNERSHIP_END_DATE_PLACEHOLDER = '3000-01-01' diff --git a/lms/djangoapps/onboarding/forms.py b/lms/djangoapps/onboarding/forms.py index 2595671800ad..a65d200cf6e3 100644 --- a/lms/djangoapps/onboarding/forms.py +++ b/lms/djangoapps/onboarding/forms.py @@ -500,9 +500,10 @@ def save(self, request, commit=True): organization.save() partners = request.POST.getlist('partner_networks') + removed_partners = request.POST.get('removed_org_partners', '').split(",") - if partners: - OrganizationPartner.update_organization_partners(organization, partners) + if partners or removed_partners: + OrganizationPartner.update_organization_partners(organization, partners, removed_partners) class RegModelForm(BaseOnboardingModelForm): @@ -1140,7 +1141,7 @@ def clean_total_program_expenses(self): def save(self, request): user_extended_profile = request.user.extended_profile - + self.instance.pk = None org_detail = super(OrganizationMetricModelForm, self).save(commit=False) org_detail.user = request.user org_detail.org = user_extended_profile.organization diff --git a/lms/djangoapps/onboarding/models.py b/lms/djangoapps/onboarding/models.py index 0b6a577d220d..e753a3ffd538 100644 --- a/lms/djangoapps/onboarding/models.py +++ b/lms/djangoapps/onboarding/models.py @@ -2,7 +2,10 @@ import uuid import re -from dateutil.relativedelta import relativedelta + +from pytz import utc + +from constants import ORG_PARTNERSHIP_END_DATE_PLACEHOLDER from datetime import datetime from django.contrib.auth.models import User from simple_history import register @@ -205,6 +208,10 @@ def admin_info(self): """ return "%s" % self.admin.email if self.admin else "Administrator not assigned yet." + def get_active_partners(self): + """ Return list of active organization partners""" + return self.organization_partners.filter(end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + def __str__(self): return self.label @@ -219,18 +226,37 @@ class OrganizationPartner(models.Model): end_date = models.DateTimeField() @classmethod - def update_organization_partners(cls, organization, partners): + def update_organization_partners(cls, organization, partners, removed_partners): """ Add/Update partners data or an organization """ - cls.objects.filter(organization=organization).delete() - _partners = PartnerNetwork.objects.filter(code__in=partners) + # Set unchecked partners end date to today + cls.objects.filter(organization=organization, + partner__in=removed_partners, end_date__gt=datetime.utcnow().date()).update( + end_date=datetime.now(utc).date()) + + # Mark removed partner affliation flag to False if not selected in any organization + _removed_partners = PartnerNetwork.objects.filter(code__in=removed_partners) + for partner in _removed_partners: + p = cls.objects.filter(partner=partner.code).first() + if not p: + partner.is_partner_affiliated = False + partner.save() + + # Get already added partners for an organization + no_updated_selections = cls.objects.filter(organization=organization, + partner__in=partners, end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + + # Filter out new/reselected Partners + new_selections = [p for p in partners if p not in no_updated_selections] + _partners = PartnerNetwork.objects.filter(code__in=new_selections) + # Add new/reselected Partners and mark network as affiliated lst_to_create = [] for partner in _partners: start_date = datetime.now() - end_date = start_date + relativedelta(years=100) + end_date = ORG_PARTNERSHIP_END_DATE_PLACEHOLDER obj = cls(organization=organization, partner=partner.code, start_date=start_date, end_date=end_date) lst_to_create.append(obj) diff --git a/lms/djangoapps/onboarding/views.py b/lms/djangoapps/onboarding/views.py index f58385d67052..1b1046769b80 100644 --- a/lms/djangoapps/onboarding/views.py +++ b/lms/djangoapps/onboarding/views.py @@ -217,7 +217,7 @@ def organization(request): initial = { 'country': COUNTRIES.get(_organization.country), 'is_org_url_exist': '1' if _organization.url else '0', - 'partner_networks': _organization.organization_partners.values_list('partner', flat=True), + 'partner_networks': _organization.get_active_partners(), } if request.method == 'POST': @@ -305,9 +305,7 @@ def get_country_names(request): def org_detail_survey(request): user_extended_profile = request.user.extended_profile are_forms_complete = not(bool(user_extended_profile.unattended_surveys(_type='list'))) - - latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization, - user=request.user).last() + latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization).last() initial = { 'actual_data': '1' if latest_survey and latest_survey.actual_data else '0', @@ -393,6 +391,8 @@ def update_account_settings(request): if not are_forms_complete : return redirect(reverse('organization')) + return redirect(reverse('update_account_settings')) + else: form = forms.UpdateRegModelForm( instance=user_extended_profile, From 68a16b0694b21e89292eb81abe5d5d4e734862e3 Mon Sep 17 00:00:00 2001 From: Mohammad Nadeem Date: Fri, 9 Mar 2018 20:32:15 +0500 Subject: [PATCH 7/9] Create history for org metrics & org partners (#383) --- lms/djangoapps/onboarding/admin.py | 9 ++++++- lms/djangoapps/onboarding/constants.py | 1 + lms/djangoapps/onboarding/forms.py | 7 ++--- lms/djangoapps/onboarding/models.py | 36 ++++++++++++++++++++++---- lms/djangoapps/onboarding/views.py | 8 +++--- 5 files changed, 48 insertions(+), 13 deletions(-) diff --git a/lms/djangoapps/onboarding/admin.py b/lms/djangoapps/onboarding/admin.py index 97982c7a5938..60f9dfa340cc 100644 --- a/lms/djangoapps/onboarding/admin.py +++ b/lms/djangoapps/onboarding/admin.py @@ -13,6 +13,7 @@ Organization, UserExtendedProfile, FunctionArea, + OrganizationPartner, ) @@ -41,7 +42,7 @@ class TotalEmployeeAdmin(BaseDropdownOrderAdmin): class PartnerNetworkAdmin(BaseDropdownOrderAdmin): - pass + list_display = ('order', 'code', 'label', 'is_partner_affiliated') class EducationLevelAdmin(BaseDropdownOrderAdmin): @@ -68,6 +69,11 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): list_display = ('user', 'organization', 'country_of_employment', 'role_in_org', 'hours_per_week', ) +class OrganizationPartnerAdmin(admin.ModelAdmin): + list_display = ('organization', 'partner', 'start_date', 'end_date',) + list_filter = ('organization', 'partner', 'start_date', 'end_date',) + + admin.site.register(Currency, CurrencyAdmin) admin.site.register(RoleInsideOrg, RoleInsideOrgAdmin) admin.site.register(OrgSector, OrgSectorAdmin) @@ -80,3 +86,4 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): admin.site.register(Organization, OraganizationAdmin) admin.site.register(UserExtendedProfile, UserExtendedProfileAdmin) admin.site.register(FunctionArea, FunctionAreaAdmin) +admin.site.register(OrganizationPartner, OrganizationPartnerAdmin) diff --git a/lms/djangoapps/onboarding/constants.py b/lms/djangoapps/onboarding/constants.py index 6c33c4dc37aa..bdf1639637e6 100644 --- a/lms/djangoapps/onboarding/constants.py +++ b/lms/djangoapps/onboarding/constants.py @@ -1 +1,2 @@ ACTIVATION_ERROR = 'Your account has not been activated. Please check your email to activate your account.' +ORG_PARTNERSHIP_END_DATE_PLACEHOLDER = '3000-01-01' diff --git a/lms/djangoapps/onboarding/forms.py b/lms/djangoapps/onboarding/forms.py index 2595671800ad..a65d200cf6e3 100644 --- a/lms/djangoapps/onboarding/forms.py +++ b/lms/djangoapps/onboarding/forms.py @@ -500,9 +500,10 @@ def save(self, request, commit=True): organization.save() partners = request.POST.getlist('partner_networks') + removed_partners = request.POST.get('removed_org_partners', '').split(",") - if partners: - OrganizationPartner.update_organization_partners(organization, partners) + if partners or removed_partners: + OrganizationPartner.update_organization_partners(organization, partners, removed_partners) class RegModelForm(BaseOnboardingModelForm): @@ -1140,7 +1141,7 @@ def clean_total_program_expenses(self): def save(self, request): user_extended_profile = request.user.extended_profile - + self.instance.pk = None org_detail = super(OrganizationMetricModelForm, self).save(commit=False) org_detail.user = request.user org_detail.org = user_extended_profile.organization diff --git a/lms/djangoapps/onboarding/models.py b/lms/djangoapps/onboarding/models.py index 0b6a577d220d..e753a3ffd538 100644 --- a/lms/djangoapps/onboarding/models.py +++ b/lms/djangoapps/onboarding/models.py @@ -2,7 +2,10 @@ import uuid import re -from dateutil.relativedelta import relativedelta + +from pytz import utc + +from constants import ORG_PARTNERSHIP_END_DATE_PLACEHOLDER from datetime import datetime from django.contrib.auth.models import User from simple_history import register @@ -205,6 +208,10 @@ def admin_info(self): """ return "%s" % self.admin.email if self.admin else "Administrator not assigned yet." + def get_active_partners(self): + """ Return list of active organization partners""" + return self.organization_partners.filter(end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + def __str__(self): return self.label @@ -219,18 +226,37 @@ class OrganizationPartner(models.Model): end_date = models.DateTimeField() @classmethod - def update_organization_partners(cls, organization, partners): + def update_organization_partners(cls, organization, partners, removed_partners): """ Add/Update partners data or an organization """ - cls.objects.filter(organization=organization).delete() - _partners = PartnerNetwork.objects.filter(code__in=partners) + # Set unchecked partners end date to today + cls.objects.filter(organization=organization, + partner__in=removed_partners, end_date__gt=datetime.utcnow().date()).update( + end_date=datetime.now(utc).date()) + + # Mark removed partner affliation flag to False if not selected in any organization + _removed_partners = PartnerNetwork.objects.filter(code__in=removed_partners) + for partner in _removed_partners: + p = cls.objects.filter(partner=partner.code).first() + if not p: + partner.is_partner_affiliated = False + partner.save() + + # Get already added partners for an organization + no_updated_selections = cls.objects.filter(organization=organization, + partner__in=partners, end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + + # Filter out new/reselected Partners + new_selections = [p for p in partners if p not in no_updated_selections] + _partners = PartnerNetwork.objects.filter(code__in=new_selections) + # Add new/reselected Partners and mark network as affiliated lst_to_create = [] for partner in _partners: start_date = datetime.now() - end_date = start_date + relativedelta(years=100) + end_date = ORG_PARTNERSHIP_END_DATE_PLACEHOLDER obj = cls(organization=organization, partner=partner.code, start_date=start_date, end_date=end_date) lst_to_create.append(obj) diff --git a/lms/djangoapps/onboarding/views.py b/lms/djangoapps/onboarding/views.py index f58385d67052..1b1046769b80 100644 --- a/lms/djangoapps/onboarding/views.py +++ b/lms/djangoapps/onboarding/views.py @@ -217,7 +217,7 @@ def organization(request): initial = { 'country': COUNTRIES.get(_organization.country), 'is_org_url_exist': '1' if _organization.url else '0', - 'partner_networks': _organization.organization_partners.values_list('partner', flat=True), + 'partner_networks': _organization.get_active_partners(), } if request.method == 'POST': @@ -305,9 +305,7 @@ def get_country_names(request): def org_detail_survey(request): user_extended_profile = request.user.extended_profile are_forms_complete = not(bool(user_extended_profile.unattended_surveys(_type='list'))) - - latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization, - user=request.user).last() + latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization).last() initial = { 'actual_data': '1' if latest_survey and latest_survey.actual_data else '0', @@ -393,6 +391,8 @@ def update_account_settings(request): if not are_forms_complete : return redirect(reverse('organization')) + return redirect(reverse('update_account_settings')) + else: form = forms.UpdateRegModelForm( instance=user_extended_profile, From 6d828ef8db594e7a20acfdd313446f0f4c87681d Mon Sep 17 00:00:00 2001 From: Mohammad Nadeem Date: Fri, 9 Mar 2018 20:44:23 +0500 Subject: [PATCH 8/9] onboarding org metics history fix (#384) --- lms/djangoapps/onboarding/admin.py | 9 ++++- lms/djangoapps/onboarding/constants.py | 1 + lms/djangoapps/onboarding/forms.py | 7 ++-- lms/djangoapps/onboarding/models.py | 36 ++++++++++++++++--- lms/djangoapps/onboarding/views.py | 8 ++--- lms/djangoapps/philu_api/urls.py | 4 ++- lms/djangoapps/philu_api/views.py | 12 ++++++- .../teams/static/teams/js/views/team_card.js | 2 +- .../static/teams/js/views/team_profile.js | 32 ++++++++++++----- 9 files changed, 86 insertions(+), 25 deletions(-) diff --git a/lms/djangoapps/onboarding/admin.py b/lms/djangoapps/onboarding/admin.py index 97982c7a5938..60f9dfa340cc 100644 --- a/lms/djangoapps/onboarding/admin.py +++ b/lms/djangoapps/onboarding/admin.py @@ -13,6 +13,7 @@ Organization, UserExtendedProfile, FunctionArea, + OrganizationPartner, ) @@ -41,7 +42,7 @@ class TotalEmployeeAdmin(BaseDropdownOrderAdmin): class PartnerNetworkAdmin(BaseDropdownOrderAdmin): - pass + list_display = ('order', 'code', 'label', 'is_partner_affiliated') class EducationLevelAdmin(BaseDropdownOrderAdmin): @@ -68,6 +69,11 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): list_display = ('user', 'organization', 'country_of_employment', 'role_in_org', 'hours_per_week', ) +class OrganizationPartnerAdmin(admin.ModelAdmin): + list_display = ('organization', 'partner', 'start_date', 'end_date',) + list_filter = ('organization', 'partner', 'start_date', 'end_date',) + + admin.site.register(Currency, CurrencyAdmin) admin.site.register(RoleInsideOrg, RoleInsideOrgAdmin) admin.site.register(OrgSector, OrgSectorAdmin) @@ -80,3 +86,4 @@ class UserExtendedProfileAdmin(admin.ModelAdmin): admin.site.register(Organization, OraganizationAdmin) admin.site.register(UserExtendedProfile, UserExtendedProfileAdmin) admin.site.register(FunctionArea, FunctionAreaAdmin) +admin.site.register(OrganizationPartner, OrganizationPartnerAdmin) diff --git a/lms/djangoapps/onboarding/constants.py b/lms/djangoapps/onboarding/constants.py index 6c33c4dc37aa..bdf1639637e6 100644 --- a/lms/djangoapps/onboarding/constants.py +++ b/lms/djangoapps/onboarding/constants.py @@ -1 +1,2 @@ ACTIVATION_ERROR = 'Your account has not been activated. Please check your email to activate your account.' +ORG_PARTNERSHIP_END_DATE_PLACEHOLDER = '3000-01-01' diff --git a/lms/djangoapps/onboarding/forms.py b/lms/djangoapps/onboarding/forms.py index 2595671800ad..a65d200cf6e3 100644 --- a/lms/djangoapps/onboarding/forms.py +++ b/lms/djangoapps/onboarding/forms.py @@ -500,9 +500,10 @@ def save(self, request, commit=True): organization.save() partners = request.POST.getlist('partner_networks') + removed_partners = request.POST.get('removed_org_partners', '').split(",") - if partners: - OrganizationPartner.update_organization_partners(organization, partners) + if partners or removed_partners: + OrganizationPartner.update_organization_partners(organization, partners, removed_partners) class RegModelForm(BaseOnboardingModelForm): @@ -1140,7 +1141,7 @@ def clean_total_program_expenses(self): def save(self, request): user_extended_profile = request.user.extended_profile - + self.instance.pk = None org_detail = super(OrganizationMetricModelForm, self).save(commit=False) org_detail.user = request.user org_detail.org = user_extended_profile.organization diff --git a/lms/djangoapps/onboarding/models.py b/lms/djangoapps/onboarding/models.py index 0b6a577d220d..e753a3ffd538 100644 --- a/lms/djangoapps/onboarding/models.py +++ b/lms/djangoapps/onboarding/models.py @@ -2,7 +2,10 @@ import uuid import re -from dateutil.relativedelta import relativedelta + +from pytz import utc + +from constants import ORG_PARTNERSHIP_END_DATE_PLACEHOLDER from datetime import datetime from django.contrib.auth.models import User from simple_history import register @@ -205,6 +208,10 @@ def admin_info(self): """ return "%s" % self.admin.email if self.admin else "Administrator not assigned yet." + def get_active_partners(self): + """ Return list of active organization partners""" + return self.organization_partners.filter(end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + def __str__(self): return self.label @@ -219,18 +226,37 @@ class OrganizationPartner(models.Model): end_date = models.DateTimeField() @classmethod - def update_organization_partners(cls, organization, partners): + def update_organization_partners(cls, organization, partners, removed_partners): """ Add/Update partners data or an organization """ - cls.objects.filter(organization=organization).delete() - _partners = PartnerNetwork.objects.filter(code__in=partners) + # Set unchecked partners end date to today + cls.objects.filter(organization=organization, + partner__in=removed_partners, end_date__gt=datetime.utcnow().date()).update( + end_date=datetime.now(utc).date()) + + # Mark removed partner affliation flag to False if not selected in any organization + _removed_partners = PartnerNetwork.objects.filter(code__in=removed_partners) + for partner in _removed_partners: + p = cls.objects.filter(partner=partner.code).first() + if not p: + partner.is_partner_affiliated = False + partner.save() + + # Get already added partners for an organization + no_updated_selections = cls.objects.filter(organization=organization, + partner__in=partners, end_date__gt=datetime.utcnow().date()).values_list('partner', flat=True) + + # Filter out new/reselected Partners + new_selections = [p for p in partners if p not in no_updated_selections] + _partners = PartnerNetwork.objects.filter(code__in=new_selections) + # Add new/reselected Partners and mark network as affiliated lst_to_create = [] for partner in _partners: start_date = datetime.now() - end_date = start_date + relativedelta(years=100) + end_date = ORG_PARTNERSHIP_END_DATE_PLACEHOLDER obj = cls(organization=organization, partner=partner.code, start_date=start_date, end_date=end_date) lst_to_create.append(obj) diff --git a/lms/djangoapps/onboarding/views.py b/lms/djangoapps/onboarding/views.py index f58385d67052..1b1046769b80 100644 --- a/lms/djangoapps/onboarding/views.py +++ b/lms/djangoapps/onboarding/views.py @@ -217,7 +217,7 @@ def organization(request): initial = { 'country': COUNTRIES.get(_organization.country), 'is_org_url_exist': '1' if _organization.url else '0', - 'partner_networks': _organization.organization_partners.values_list('partner', flat=True), + 'partner_networks': _organization.get_active_partners(), } if request.method == 'POST': @@ -305,9 +305,7 @@ def get_country_names(request): def org_detail_survey(request): user_extended_profile = request.user.extended_profile are_forms_complete = not(bool(user_extended_profile.unattended_surveys(_type='list'))) - - latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization, - user=request.user).last() + latest_survey = OrganizationMetric.objects.filter(org=user_extended_profile.organization).last() initial = { 'actual_data': '1' if latest_survey and latest_survey.actual_data else '0', @@ -393,6 +391,8 @@ def update_account_settings(request): if not are_forms_complete : return redirect(reverse('organization')) + return redirect(reverse('update_account_settings')) + else: form = forms.UpdateRegModelForm( instance=user_extended_profile, diff --git a/lms/djangoapps/philu_api/urls.py b/lms/djangoapps/philu_api/urls.py index cc67ee079875..b57c92361be2 100644 --- a/lms/djangoapps/philu_api/urls.py +++ b/lms/djangoapps/philu_api/urls.py @@ -3,12 +3,14 @@ """ from django.conf.urls import url, patterns -from lms.djangoapps.philu_api.views import PlatformSyncService, get_user_chat, mark_user_chat_read +from lms.djangoapps.philu_api.views import PlatformSyncService, \ + get_user_chat, mark_user_chat_read, get_user_data urlpatterns = patterns( 'philu_api.views', url(r'platform/sync/service/', PlatformSyncService.as_view(), name='get_shared_data'), url(r'profile/update/', PlatformSyncService.as_view(), name='update_community_profile_update'), url(r'profile/chats/?$', get_user_chat, name='get_user_chat'), + url(r'profile/data/?$', get_user_data, name='get_user_data'), url(r'profile/chats/mark/?$', mark_user_chat_read, name='mark_user_chat_read'), ) diff --git a/lms/djangoapps/philu_api/views.py b/lms/djangoapps/philu_api/views.py index 642f35b81888..f6d1157dae08 100644 --- a/lms/djangoapps/philu_api/views.py +++ b/lms/djangoapps/philu_api/views.py @@ -54,7 +54,7 @@ def post(self, request): username = request.GET.get("username") email = request.GET.get("email") - + try: user = User.objects.get(email=email) except User.DoesNotExist: @@ -117,3 +117,13 @@ def mark_user_chat_read(request): data={'_uid': 1, 'username': username}, headers=headers) return JsonResponse(response.json()) + +def get_user_data(request): + """ Get the user profile data from NodeBB for current user """ + data_endpoint = settings.NODEBB_ENDPOINT + '/api/v2/users/data' + username = request.user.username + headers = {'Authorization': 'Bearer ' + settings.NODEBB_MASTER_TOKEN} + response = requests.post(data_endpoint, + data={'_uid': 1, 'username': username}, + headers=headers) + return JsonResponse(response.json()) diff --git a/lms/djangoapps/teams/static/teams/js/views/team_card.js b/lms/djangoapps/teams/static/teams/js/views/team_card.js index a6f9ec169033..085c82698b09 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_card.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_card.js @@ -114,7 +114,7 @@ if (this.memberships > 1) { this.$el.html(this.template({roomID: this.roomID, teamID: this.teamID, nodeBBUrl: this.nodeBBUrl})); } else { - this.$el.html('

You can not start discussion until more members join this team.

'); + this.$el.html('

You can not start discussion until more members join this team.

'); } } }); diff --git a/lms/djangoapps/teams/static/teams/js/views/team_profile.js b/lms/djangoapps/teams/static/teams/js/views/team_profile.js index 96b5f32e3d99..a31980e5f7c2 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_profile.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_profile.js @@ -4,6 +4,7 @@ (function(define) { 'use strict'; define([ + 'jquery', 'backbone', 'underscore', 'gettext', @@ -14,7 +15,7 @@ 'text!teams/templates/team-profile.underscore', 'text!teams/templates/team-member.underscore' ], - function(Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils, + function($, Backbone, _, gettext, HtmlUtils, TeamDiscussionView, ViewUtils, TeamUtils, teamTemplate, teamMemberTemplate) { var TeamProfileView = Backbone.View.extend({ @@ -81,14 +82,27 @@ renderTeamMembers: function() { var view = this; _.each(this.model.get('membership'), function(membership) { - HtmlUtils.append( - view.$('.members-info'), - HtmlUtils.template(teamMemberTemplate)({ - imageUrl: membership.user.profile_image.image_url_medium, - username: membership.user.username, - memberProfileUrl: '/u/' + membership.user.username - }) - ); + $.post({ + url: '/philu/api/profile/data', + data: { + username: membership.user.username + }, + success: function(data) { + HtmlUtils.append( + view.$('.members-info'), + HtmlUtils.template(teamMemberTemplate)({ + imageUrl: data.payload.picture !== "" ? + data.payload.picture : + membership.user.profile_image.image_url_medium, + username: membership.user.username, + memberProfileUrl: data.payload.profileUrl + }) + ); + }, + error: function(err) { + console.error("Error: " + err) + } + }); }); }, From 28f86459df84b5d3f488b1c3f7609ee06a6e2c2e Mon Sep 17 00:00:00 2001 From: Muhammad Nadeem Date: Fri, 9 Mar 2018 23:21:47 +0500 Subject: [PATCH 9/9] Add OrganizationMetric table at admin panel --- lms/djangoapps/onboarding/admin.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/onboarding/admin.py b/lms/djangoapps/onboarding/admin.py index 60f9dfa340cc..e5b76aafe3b3 100644 --- a/lms/djangoapps/onboarding/admin.py +++ b/lms/djangoapps/onboarding/admin.py @@ -14,6 +14,7 @@ UserExtendedProfile, FunctionArea, OrganizationPartner, + OrganizationMetric ) @@ -61,7 +62,13 @@ class CurrencyAdmin(admin.ModelAdmin): list_display = ('country', 'name', 'alphabetic_code', ) -class OraganizationAdmin(admin.ModelAdmin): +class OrganizationMetricAdmin(admin.ModelAdmin): + list_display = ('org', 'user', 'submission_date', 'actual_data', 'effective_date', 'total_clients', + 'total_employees', 'local_currency', 'total_revenue', 'total_donations', 'total_expenses', + 'total_program_expenses') + + +class OrganizationAdmin(admin.ModelAdmin): list_display = ('label', 'admin', 'country', 'unclaimed_org_admin_email', 'founding_year', ) @@ -83,7 +90,8 @@ class OrganizationPartnerAdmin(admin.ModelAdmin): admin.site.register(PartnerNetwork, PartnerNetworkAdmin) admin.site.register(EducationLevel, EnglishProficiencyAdmin) admin.site.register(EnglishProficiency, EnglishProficiencyAdmin) -admin.site.register(Organization, OraganizationAdmin) +admin.site.register(Organization, OrganizationAdmin) admin.site.register(UserExtendedProfile, UserExtendedProfileAdmin) admin.site.register(FunctionArea, FunctionAreaAdmin) admin.site.register(OrganizationPartner, OrganizationPartnerAdmin) +admin.site.register(OrganizationMetric, OrganizationMetricAdmin)