Skip to content

Commit

Permalink
Merge branch 'release-0-14-10'
Browse files Browse the repository at this point in the history
  • Loading branch information
jjoseba committed Apr 27, 2023
2 parents dea0c18 + 0bff0f0 commit 1948fbb
Show file tree
Hide file tree
Showing 53 changed files with 1,088 additions and 478 deletions.
3 changes: 2 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[flake8]
max-line-length = 120
max-line-length = 120
extend-ignore = F403
22 changes: 11 additions & 11 deletions api/resources/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,8 @@ def obj_create(self, bundle, **kwargs):
tracker = Tracker()
tracker.user = u
tracker.type = 'login'
tracker.ip = bundle.request.META.get('REMOTE_ADDR',
DEFAULT_IP_ADDRESS)
tracker.agent = bundle.request.META.get('HTTP_USER_AGENT',
'unknown')
tracker.ip = bundle.request.META.get('REMOTE_ADDR', DEFAULT_IP_ADDRESS)
tracker.agent = bundle.request.META.get('HTTP_USER_AGENT', 'unknown')
tracker.save()
else:
raise BadRequest(_(u'Authentication failure'))
Expand All @@ -82,15 +80,17 @@ def obj_create(self, bundle, **kwargs):
bundle.data['api_key'] = key.key

try:
up = UserProfile.objects.get(user__username=username)
job_title = up.job_title
organisation = up.organisation
profile = UserProfile.objects.get(user=bundle.obj)
bundle.data['job_title'] = profile.job_title
bundle.data['organisation'] = profile.organisation

customfields = profile.get_customfields_dict()
bundle.data.update(customfields)

except UserProfile.DoesNotExist:
job_title = ""
organisation = ""
bundle.data['job_title'] = ''
bundle.data['organisation'] = ''

bundle.data['job_title'] = job_title
bundle.data['organisation'] = organisation
bundle.obj = u
return bundle

Expand Down
77 changes: 76 additions & 1 deletion api/resources/profile.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib.auth.forms import SetPasswordForm
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from tastypie import fields
from tastypie.authentication import ApiKeyAuthentication
from tastypie.authorization import Authorization
Expand All @@ -10,9 +11,11 @@
from api.serializers import UserJSONSerializer
from api.utils import check_required_params
from datarecovery.models import DataRecovery
from oppia.models import Participant
from oppia.models import Participant, Points, Award
from profile.forms import ProfileForm
from profile.models import UserProfile, CustomField
from settings.models import SettingProperties
from settings import constants


class ProfileUpdateResource(ModelResource):
Expand Down Expand Up @@ -125,6 +128,78 @@ def get_list(self, request, **kwargs):
return self.create_response(request, cohorts)


class UserProfileResource(ModelResource):

badges = fields.IntegerField(readonly=True)
course_points = fields.CharField(readonly=True)
cohorts = fields.CharField(readonly=True)
scoring = fields.BooleanField(readonly=True)
badging = fields.BooleanField(readonly=True)
metadata = fields.CharField(readonly=True)
points = fields.IntegerField(readonly=True)

class Meta:
queryset = User.objects.all()
resource_name = 'profile'
allowed_methods = ['get']
authorization = Authorization()
authentication = ApiKeyAuthentication()
always_return_data = True
include_resource_uri = False
fields = ['first_name',
'last_name',
'username',
'email',
'job_title',
'organisation']

def dispatch(self, request_type, request, **kwargs):
# Force this to be a single User object
return super().dispatch('detail', request, **kwargs)

def obj_get(self, bundle, **kwargs):
return bundle.request.user

def dehydrate(self, bundle):
bundle = super().dehydrate(bundle)

try:
profile = UserProfile.objects.get(user=bundle.obj)
bundle.data['job_title'] = profile.job_title
bundle.data['organisation'] = profile.organisation

customfields = profile.get_customfields_dict()
bundle.data.update(customfields)

except UserProfile.DoesNotExist:
bundle.data['job_title'] = ''
bundle.data['organisation'] = ''

return bundle

def dehydrate_cohorts(self, bundle):
return Participant.get_user_cohorts(bundle.request.user)

def dehydrate_metadata(self, bundle):
return settings.OPPIA_METADATA

def dehydrate_points(self, bundle):
return Points.get_userscore(bundle.request.user)

def dehydrate_badges(self, bundle):
return Award.get_userawards(bundle.request.user)

def dehydrate_scoring(self, bundle):
return SettingProperties.get_bool(
constants.OPPIA_POINTS_ENABLED,
settings.OPPIA_POINTS_ENABLED)

def dehydrate_badging(self, bundle):
return SettingProperties.get_bool(
constants.OPPIA_BADGES_ENABLED,
settings.OPPIA_BADGES_ENABLED)


class ChangePasswordResource(ModelResource):
'''
For resetting user password
Expand Down
4 changes: 3 additions & 1 deletion api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from api.resources.course import CourseResource, CourseStructureResource
from api.resources.login import UserResource as UserResource
from api.resources.points import PointsResource
from api.resources.profile import ProfileUpdateResource, ChangePasswordResource, UserCohortsResource
from api.resources.profile import ProfileUpdateResource, ChangePasswordResource, UserCohortsResource, \
UserProfileResource
from api.resources.v1.register import RegisterResource as RegisterResourceV1
from api.resources.v2.register import RegisterResource as RegisterResourceV2
from api.resources.reset_password import ResetPasswordResource
Expand Down Expand Up @@ -60,6 +61,7 @@ def get_api_v2():
api.register(DownloadDataResource())
api.register(ChangePasswordResource())
api.register(UserCohortsResource())
api.register(UserProfileResource())
return api


Expand Down
6 changes: 3 additions & 3 deletions av/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
from av.models import UploadedMedia
from helpers.mixins.AjaxTemplateResponseMixin import AjaxTemplateResponseMixin
from helpers.mixins.ListItemUrlMixin import ListItemUrlMixin

from oppia.models import Media, Course
from oppia.permissions import can_view_course
from oppia.permissions import permission_view_course

STR_UPLOAD_MEDIA = _(u'Upload Media')

Expand Down Expand Up @@ -63,8 +62,9 @@ def download_media_file(request, media_id):
return response


@permission_view_course
def download_course_media(request, course_id):
course = can_view_course(request, course_id)
course = get_object_or_404(Course, pk=course_id)
media = Media.objects.filter(course=course)
uploaded = UploadedMedia.objects.filter(
md5__in=media.values_list('digest', flat=True))
Expand Down
2 changes: 1 addition & 1 deletion oppia/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import warnings

VERSION = (0, 14, 9, 'core', '1', 'release')
VERSION = (0, 14, 10, 'core', '1', 'release')
DEFAULT_IP_ADDRESS = None
warnings.simplefilter('default')
29 changes: 28 additions & 1 deletion oppia/mixins/PermissionMixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
from oppia.models import Participant, Course


class CanViewUserDetailsPermissionMixin(LoginRequiredMixin, UserPassesTestMixin):
class ObjectPermissionRequiredMixin(LoginRequiredMixin):
"""
Checks that the current user has permission to access this object, raising a PermissionDenied if not.
For this, the object to check against is obtained from the self.object attribute from SingleObjectMixin,
so this mixin must be put **after** the view that inherits from that one
"""

def has_object_permission(self, obj):
raise NotImplementedError(
'{} is missing the implementation of the has_object_permission() method.'.format(self.__class__.__name__)
)

def get_object(self, *args, **kwargs):
obj = super().get_object(*args, **kwargs)
if not self.has_object_permission(obj):
return self.handle_no_permission()
return obj


class CanViewUserDetailsMixin(LoginRequiredMixin, UserPassesTestMixin):
"""
Verify that the current user can view details of another user. For this, the other user pk is get from
the URL kwargs. If it is not defined with the SingleObjectMixin `pk_url_kwarg`, the specific kwarg
Expand Down Expand Up @@ -31,3 +50,11 @@ def test_func(self):
coursecohort__cohort__participant__role=Participant.TEACHER) \

return courses_teached_by.exists()


class CanEditUserMixin(ObjectPermissionRequiredMixin):
"""
Verify that the current user can edit the current view user.
"""
def has_object_permission(self, obj):
return self.request.user.is_staff or (self.request.user.id == obj.pk)
23 changes: 23 additions & 0 deletions oppia/models/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,29 @@ def get_no_trackers(self):

# ------------ Permissions management ----------------

def user_can_view(self, user):
if user.is_staff:
return True

if self.status is CourseStatus.ARCHIVED:
return False

if self.status != CourseStatus.DRAFT:
return True

if user.is_anonymous:
return False

try:
Course.objects.get(
pk=self.pk,
coursepermissions__course=self,
coursepermissions__user=user,
coursepermissions__role=CoursePermissions.VIEWER)
return True
except Course.DoesNotExist:
return False

def user_can_view_detail(self, user):
if user.is_staff:
return True
Expand Down
1 change: 0 additions & 1 deletion oppia/models/points.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ def get_leaderboard_filtered(request_user,
.order_by('-points')

# check if there's going to be overlap or not
print(users_points.count())
if users_points.count() <= (count_top + above + below + 1):
return Points.get_leaderboard_top(users_points,
users_points.count())
Expand Down
Loading

0 comments on commit 1948fbb

Please sign in to comment.