wip: Add views
This commit is contained in:
parent
b16016b34c
commit
6244e02489
|
|
@ -4,6 +4,7 @@ from django.conf.urls.static import static
|
|||
from django.contrib import admin
|
||||
from django.contrib.auth.decorators import user_passes_test
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
from django.http import HttpResponse
|
||||
from django.urls import include, path, re_path, register_converter
|
||||
from django.urls.converters import IntConverter
|
||||
from django.views import defaults as default_views
|
||||
|
|
@ -40,6 +41,7 @@ from vbv_lernwelt.course.views import (
|
|||
)
|
||||
from vbv_lernwelt.course_session.views import get_course_session_documents
|
||||
from vbv_lernwelt.dashboard.views import (
|
||||
export_attendance_as_xsl,
|
||||
get_dashboard_config,
|
||||
get_dashboard_due_dates,
|
||||
get_dashboard_persons,
|
||||
|
|
@ -130,6 +132,7 @@ urlpatterns = [
|
|||
path(r"api/dashboard/course/<str:course_id>/mentees/", get_mentee_count, name="get_mentee_count"),
|
||||
path(r"api/dashboard/course/<str:course_id>/open_tasks/", get_mentor_open_tasks_count,
|
||||
name="get_mentor_open_tasks_count"),
|
||||
path(r"api/dashboard/export/attendance", export_attendance_as_xsl, name="export_attendance_as_xsl"),
|
||||
|
||||
# course
|
||||
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ logger = structlog.get_logger(__name__)
|
|||
)
|
||||
def command(course_session_id, save_as_file):
|
||||
# using the output from call_command was a bit cumbersome, so this is just a wrapper for the actual function
|
||||
export_competence_certificates([course_session_id], save_as_file)
|
||||
export_competence_certificates([course_session_id], save_as_file=save_as_file)
|
||||
|
|
|
|||
|
|
@ -306,7 +306,9 @@ def _remove_unknown_entries(assignment, completion_data):
|
|||
|
||||
|
||||
def export_competence_certificates(
|
||||
course_session_ids: list[str], save_as_file: bool, circle_ids: list[int] = None
|
||||
course_session_ids: list[str],
|
||||
circle_ids: list[int] = None,
|
||||
save_as_file: bool = False,
|
||||
):
|
||||
if len(course_session_ids) == 0:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -15,4 +15,4 @@ logger = structlog.get_logger(__name__)
|
|||
)
|
||||
def command(course_session_id, save_as_file):
|
||||
# using the output from call_command was a bit cumbersome, so this is just a wrapper for the actual function
|
||||
export_attendance([course_session_id], save_as_file)
|
||||
export_attendance([course_session_id], save_as_file=save_as_file)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ logger = structlog.get_logger(__name__)
|
|||
|
||||
|
||||
def export_attendance(
|
||||
course_session_ids: list[str], save_as_file: bool, circle_ids: list[int] = None
|
||||
course_session_ids: list[str],
|
||||
save_as_file: bool = False,
|
||||
circle_ids: list[int] = None,
|
||||
):
|
||||
wb = Workbook()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
|||
|
||||
|
||||
@admin.register(CourseSessionGroup)
|
||||
class CourseSessionAssignmentAdmin(admin.ModelAdmin): ...
|
||||
class CourseSessionAssignmentAdmin(admin.ModelAdmin):
|
||||
...
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ from datetime import date
|
|||
from enum import Enum
|
||||
from typing import List, Set
|
||||
|
||||
from django.http import HttpResponse
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
|
|
@ -11,6 +13,7 @@ from vbv_lernwelt.assignment.models import (
|
|||
AssignmentCompletion,
|
||||
AssignmentCompletionStatus,
|
||||
)
|
||||
from vbv_lernwelt.assignment.services import export_competence_certificates
|
||||
from vbv_lernwelt.competence.services import (
|
||||
query_competence_course_session_assignments,
|
||||
query_competence_course_session_edoniq_tests,
|
||||
|
|
@ -22,6 +25,10 @@ from vbv_lernwelt.course.models import (
|
|||
CourseSessionUser,
|
||||
)
|
||||
from vbv_lernwelt.course.views import logger
|
||||
from vbv_lernwelt.course_session.services.export import (
|
||||
export_attendance,
|
||||
make_export_filename,
|
||||
)
|
||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||
from vbv_lernwelt.duedate.models import DueDate
|
||||
from vbv_lernwelt.duedate.serializers import DueDateSerializer
|
||||
|
|
@ -494,7 +501,7 @@ def get_mentor_open_tasks_count(request, course_id: str):
|
|||
raise e
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return Response({"error": str(e)}, status=404)
|
||||
return Response({"error": str(e)}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
def _get_mentor_open_tasks_count(course_id: str, mentor: User) -> int:
|
||||
|
|
@ -526,3 +533,48 @@ def _get_mentor_open_tasks_count(course_id: str, mentor: User) -> int:
|
|||
)
|
||||
|
||||
return open_assigment_count + open_feedback_count
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def export_attendance_as_xsl(request):
|
||||
return _generate_xls_export(request, export_attendance)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def export_competence_certificate_as_xsl(request):
|
||||
return _generate_xls_export(request, export_competence_certificates)
|
||||
|
||||
|
||||
def _generate_xls_export(request, export_fn) -> HttpResponse:
|
||||
requested_course_session_ids = request.data.get("courseSessionIds", [])
|
||||
circle_ids = request.data.get("circleIds", None)
|
||||
|
||||
if not requested_course_session_ids:
|
||||
return Response({"error": "no_cs_ids"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
course_session_ids = _get_allowed_course_session_ids_for_user(
|
||||
request.user, requested_course_session_ids
|
||||
) # noqa
|
||||
|
||||
data = export_fn(course_session_ids, circle_ids=circle_ids)
|
||||
response = HttpResponse(
|
||||
data,
|
||||
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
)
|
||||
response["Content-Disposition"] = f'attachment; filename="{make_export_filename()}"'
|
||||
return response
|
||||
|
||||
|
||||
def _get_allowed_course_session_ids_for_user(
|
||||
user: User, requested_cs_ids: List[str]
|
||||
) -> List[str]:
|
||||
ALLOWED_ROLES = ["TRAINER", "SUPERVISOR"]
|
||||
# 1. get course sessions for user with allowed roles
|
||||
# 2. get overlapping course sessions with given course_session_ids
|
||||
# Note: We don't care about the circle_ids as it's ok-ish that trainers could export other data
|
||||
all_cs_ids_for_user = [
|
||||
csr._original.id
|
||||
for csr in get_course_sessions_with_roles_for_user(user)
|
||||
if any(allowed_role in ALLOWED_ROLES for role in csr.roles)
|
||||
] # noqa
|
||||
return list(set(requested_cs_ids) & set(all_cs_ids_for_user))
|
||||
|
|
|
|||
|
|
@ -153,9 +153,9 @@ def fetch_course_session_all_users(courses: List[int], excluded_domains=None):
|
|||
|
||||
def generate_export_response(cs_users: List[User]) -> HttpResponse:
|
||||
response = HttpResponse(content_type="text/csv; charset=utf-8")
|
||||
response["Content-Disposition"] = (
|
||||
f"attachment; filename=edoniq_user_export_{date.today().strftime('%Y%m%d')}.csv"
|
||||
)
|
||||
response[
|
||||
"Content-Disposition"
|
||||
] = f"attachment; filename=edoniq_user_export_{date.today().strftime('%Y%m%d')}.csv"
|
||||
|
||||
response.write("\ufeff".encode("utf8")) # UTF-8 BOM
|
||||
|
||||
|
|
|
|||
|
|
@ -251,9 +251,9 @@ def _handle_feedback_export_action(course_seesions, file_name):
|
|||
response = HttpResponse(
|
||||
content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
)
|
||||
response["Content-Disposition"] = (
|
||||
f"attachment; filename={make_export_filename(file_name)}"
|
||||
)
|
||||
response[
|
||||
"Content-Disposition"
|
||||
] = f"attachment; filename={make_export_filename(file_name)}"
|
||||
response.write(excel_bytes)
|
||||
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ class TestNotificationService(TestCase):
|
|||
self.assertFalse(notification.emailed)
|
||||
|
||||
def test_send_notification_with_email(self):
|
||||
self.recipient.additional_json_data["email_notification_categories"] = (
|
||||
json.dumps(["USER_INTERACTION"])
|
||||
)
|
||||
self.recipient.additional_json_data[
|
||||
"email_notification_categories"
|
||||
] = json.dumps(["USER_INTERACTION"])
|
||||
self.recipient.save()
|
||||
|
||||
verb = "Anne hat deinen Auftrag bewertet"
|
||||
|
|
@ -146,9 +146,9 @@ class TestNotificationService(TestCase):
|
|||
self.assertFalse(notification.emailed)
|
||||
|
||||
# when the email was not sent, yet it will still send it afterwards...
|
||||
self.recipient.additional_json_data["email_notification_categories"] = (
|
||||
json.dumps(["USER_INTERACTION"])
|
||||
)
|
||||
self.recipient.additional_json_data[
|
||||
"email_notification_categories"
|
||||
] = json.dumps(["USER_INTERACTION"])
|
||||
self.recipient.save()
|
||||
|
||||
result = self.notification_service._send_notification(
|
||||
|
|
@ -188,9 +188,9 @@ class TestNotificationService(TestCase):
|
|||
self.assertFalse(self._has_sent_emails())
|
||||
|
||||
# Assert mail is sent if corresponding email notification type is enabled
|
||||
self.recipient.additional_json_data["email_notification_categories"] = (
|
||||
json.dumps(["USER_INTERACTION"])
|
||||
)
|
||||
self.recipient.additional_json_data[
|
||||
"email_notification_categories"
|
||||
] = json.dumps(["USER_INTERACTION"])
|
||||
self.recipient.save()
|
||||
self.notification_service._send_notification(
|
||||
sender=self.sender,
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@ class SelfEvaluationFeedbackSerializer(serializers.ModelSerializer):
|
|||
return obj.learning_unit.get_circle().title
|
||||
|
||||
def get_criteria(self, obj):
|
||||
performance_criteria: List[PerformanceCriteria] = (
|
||||
obj.learning_unit.performancecriteria_set.all()
|
||||
)
|
||||
performance_criteria: List[
|
||||
PerformanceCriteria
|
||||
] = obj.learning_unit.performancecriteria_set.all()
|
||||
|
||||
criteria = []
|
||||
|
||||
|
|
|
|||
|
|
@ -67,15 +67,15 @@ class AbacusInvoiceCreator(InvoiceCreator):
|
|||
)
|
||||
|
||||
SubElement(sales_order_header_fields, "CustomerNumber").text = customer_number
|
||||
SubElement(sales_order_header_fields, "PurchaseOrderDate").text = (
|
||||
order_date.isoformat()
|
||||
)
|
||||
SubElement(sales_order_header_fields, "DeliveryDate").text = (
|
||||
order_date.isoformat()
|
||||
)
|
||||
SubElement(sales_order_header_fields, "ReferencePurchaseOrder").text = (
|
||||
reference_purchase_order
|
||||
)
|
||||
SubElement(
|
||||
sales_order_header_fields, "PurchaseOrderDate"
|
||||
).text = order_date.isoformat()
|
||||
SubElement(
|
||||
sales_order_header_fields, "DeliveryDate"
|
||||
).text = order_date.isoformat()
|
||||
SubElement(
|
||||
sales_order_header_fields, "ReferencePurchaseOrder"
|
||||
).text = reference_purchase_order
|
||||
SubElement(sales_order_header_fields, "UnicId").text = unic_id
|
||||
|
||||
for index, item in enumerate(items, start=1):
|
||||
|
|
|
|||
Loading…
Reference in New Issue