import structlog from django.core.exceptions import PermissionDenied from django.http import JsonResponse from django.shortcuts import get_object_or_404 from rest_framework.decorators import api_view from vbv_lernwelt.course.permissions import has_course_access_by_page_request from vbv_lernwelt.edoniq_test.edoniq_sso import create_token from vbv_lernwelt.learnpath.models import LearningContentEdoniqTest logger = structlog.get_logger(__name__) import csv from datetime import date from itertools import chain from typing import List from django.contrib.admin.views.decorators import staff_member_required from django.db.models import CharField, Q, Value from django.http import HttpResponse from vbv_lernwelt.core.models import User from vbv_lernwelt.course.consts import COURSE_UK, COURSE_UK_FR, COURSE_UK_IT from vbv_lernwelt.course.models import CourseSessionUser UK_COURSE_IDS = [COURSE_UK, COURSE_UK_FR, COURSE_UK_IT] DEFAULT_EXCLUDED_DOMAINS = ["eiger-versicherung.ch", "assurance.ch", "example.com"] @api_view(["POST"]) def get_edoniq_token_redirect(request): page_id = request.data.get("learning_content_id", "") extended_time_test = request.data.get("extended_time_test", False) logger.debug( "get_edoniq_test_redirect", label="edoniq_test_api", page_id=page_id, user_id=request.user.id, extended_time_test=extended_time_test, ) if not page_id: return JsonResponse({"error": "learning_content_id is required"}, status=400) try: test_page = get_object_or_404(LearningContentEdoniqTest, id=page_id) if not has_course_access_by_page_request(request, test_page): logger.debug( "get_edoniq_test_redirect_permission_denied", label="edoniq_test_api", page_id=page_id, page_slug=test_page.slug, page_title=test_page.title, user_id=request.user.id, ) raise PermissionDenied() url = ( test_page.test_url if not extended_time_test else test_page.extended_time_test_url ) token = create_token( str(request.user.id), ) redirect_url = f"{url}&ssoToken={token}" response_data = {"redirect_url": redirect_url} logger.debug( "get_edoniq_test_redirect_permission_success", label="edoniq_test_api", page_id=page_id, page_slug=test_page.slug, page_title=test_page.title, user_id=request.user.id, ) return JsonResponse(response_data, status=200) except Exception as e: logger.error(e, exc_info=True) return JsonResponse({"error": str(e)}, status=404) @staff_member_required def export_students(request): course_session_users = fetch_course_session_users(UK_COURSE_IDS) return generate_export_response(course_session_users) @staff_member_required def export_trainers(request): course_session_users = fetch_course_session_users( UK_COURSE_IDS, role=CourseSessionUser.Role.EXPERT ) return generate_export_response(course_session_users) @staff_member_required def export_students_and_trainers(request): course_session_users = fetch_course_session_all_users(UK_COURSE_IDS) return generate_export_response(course_session_users) def fetch_course_session_users(courses: List[int]): # if a user is in multiple courses, he should be exported multiple times # todo: check if this is the case otherwise use .distinct("user") return CourseSessionUser.objects.filter( course_session__course__id__in=courses, role=CourseSessionUser.Role.MEMBER ).order_by("user__email") def fetch_course_session_users( courses: List[int], role=CourseSessionUser.Role.MEMBER, excluded_domains=None ): if role == CourseSessionUser.Role.EXPERT: edoniq_role = "Trainer" else: edoniq_role = "Lernende" # exclude test users if required if excluded_domains is None: excluded_domains = DEFAULT_EXCLUDED_DOMAINS exclude_q = Q() for condition in excluded_domains: exclude_q |= Q(email__contains=condition) # if users should be exported per course session, remove the distinct() call return ( User.objects.filter( coursesessionuser__course_session__course__id__in=courses, coursesessionuser__role=role, ) .exclude(exclude_q) .order_by("email") .annotate(edoniq_role=Value(edoniq_role, output_field=CharField())) .distinct() ) def fetch_course_session_all_users(courses: List[int], excluded_domains=None): course_session_users = fetch_course_session_users( courses, excluded_domains=excluded_domains ) course_session_trainers = fetch_course_session_users( courses, role=CourseSessionUser.Role.EXPERT, excluded_domains=excluded_domains ) combined_queryset = chain(course_session_users, course_session_trainers) # use email as key to remove duplicates. Maybe we can use the sso id in the future, but it is not set for all users unique_dict = {obj.email: obj for obj in combined_queryset} return list(unique_dict.values()) def generate_export_response(cs_users: List[User]) -> HttpResponse: response = HttpResponse(content_type="text/csv") response[ "Content-Disposition" ] = f"attachment; filename=edoniq_user_export_{date.today().strftime('%Y%m%d')}.csv" writer = csv.writer(response) writer.writerow( [ "login", "firstname", "lastname", "gender", "preferred_language", "email", "birthday", "group", "region", "division", "oe", "superior_login", ] ) for cs_user in cs_users: writer.writerow( [ cs_user.id, cs_user.first_name, cs_user.last_name, "m", cs_user.language, cs_user.email, cs_user.additional_json_data.get("Geburtsdatum", ""), cs_user.edoniq_role, cs_user.additional_json_data.get("Firmenname", ""), cs_user.additional_json_data.get("Lehrvertragsnummer", ""), cs_user.coursesessionuser_set.first().course_session.import_id, "", ] ) return response