Merged in feature/update-importer (pull request #188)
Feature/update importer
This commit is contained in:
commit
45a70f6b75
|
|
@ -269,7 +269,7 @@
|
||||||
"selfEvaluationNo": "@:selfEvaluation: Muss ich nochmals anschauen.",
|
"selfEvaluationNo": "@:selfEvaluation: Muss ich nochmals anschauen.",
|
||||||
"selfEvaluationYes": "@:selfEvaluation: Ich kann das.",
|
"selfEvaluationYes": "@:selfEvaluation: Ich kann das.",
|
||||||
"steps": "Schritt {{current}} von {{max}}",
|
"steps": "Schritt {{current}} von {{max}}",
|
||||||
"title": "@:selfEvaluation.selfEvaluation {{title}}",
|
"title": "Selbsteinschätzung {{title}}",
|
||||||
"yes": "Ja, ich kann das"
|
"yes": "Ja, ich kann das"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ const getNextStepLink = (courseSession: CourseSession) => {
|
||||||
<br />
|
<br />
|
||||||
{{ $t("uk.contact.address") }}
|
{{ $t("uk.contact.address") }}
|
||||||
<br />
|
<br />
|
||||||
<a href="mailto:uek-support@vbv-afa.ch">uk.contact.email</a>
|
<a class="link" href="mailto:uek-support@vbv-afa.ch">
|
||||||
|
uek-support@vbv-afa.ch
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</address>
|
</address>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,11 @@ from vbv_lernwelt.course.views import (
|
||||||
request_course_completion,
|
request_course_completion,
|
||||||
request_course_completion_for_user,
|
request_course_completion_for_user,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.edoniq_test.views import export_students, export_trainers
|
from vbv_lernwelt.edoniq_test.views import (
|
||||||
|
export_students,
|
||||||
|
export_students_and_trainers,
|
||||||
|
export_trainers,
|
||||||
|
)
|
||||||
from vbv_lernwelt.feedback.views import (
|
from vbv_lernwelt.feedback.views import (
|
||||||
get_expert_feedbacks_for_course,
|
get_expert_feedbacks_for_course,
|
||||||
get_feedback_for_circle,
|
get_feedback_for_circle,
|
||||||
|
|
@ -148,6 +152,8 @@ urlpatterns = [
|
||||||
# edoniq test
|
# edoniq test
|
||||||
path(r'api/core/edoniq-test/export-users/', export_students, name='edoniq_export_students'),
|
path(r'api/core/edoniq-test/export-users/', export_students, name='edoniq_export_students'),
|
||||||
path(r'api/core/edoniq-test/export-trainers/', export_trainers, name='edoniq_export_trainers'),
|
path(r'api/core/edoniq-test/export-trainers/', export_trainers, name='edoniq_export_trainers'),
|
||||||
|
path(r'api/core/edoniq-test/export-users-trainers/', export_students_and_trainers,
|
||||||
|
name='edoniq_export_students_and_trainers'),
|
||||||
|
|
||||||
# importer
|
# importer
|
||||||
path(
|
path(
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ from vbv_lernwelt.core.admin import User
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.edoniq_test.views import (
|
from vbv_lernwelt.edoniq_test.views import (
|
||||||
|
fetch_course_session_all_users,
|
||||||
fetch_course_session_users,
|
fetch_course_session_users,
|
||||||
generate_export_response,
|
generate_export_response,
|
||||||
)
|
)
|
||||||
|
|
@ -36,6 +38,37 @@ class EdoniqUserExportTestCase(TestCase):
|
||||||
users = fetch_course_session_users([COURSE_TEST_ID])
|
users = fetch_course_session_users([COURSE_TEST_ID])
|
||||||
self.assertEqual(len(users), 2)
|
self.assertEqual(len(users), 2)
|
||||||
|
|
||||||
|
def test_fetch_course_session_trainers(self):
|
||||||
|
users = fetch_course_session_users(
|
||||||
|
[COURSE_TEST_ID], role=CourseSessionUser.Role.EXPERT
|
||||||
|
)
|
||||||
|
self.assertEqual(len(users), 1)
|
||||||
|
|
||||||
|
def test_remove_eiger_versicherungen(self):
|
||||||
|
user1 = User.objects.get(email="test-student1@example.com")
|
||||||
|
user1.email = "some@eiger-versicherungen.ch"
|
||||||
|
user1.save()
|
||||||
|
users = fetch_course_session_users([COURSE_TEST_ID])
|
||||||
|
self.assertEqual(len(users), 1)
|
||||||
|
|
||||||
|
def test_export_students_and_trainers(self):
|
||||||
|
users = fetch_course_session_all_users([COURSE_TEST_ID])
|
||||||
|
self.assertEqual(len(users), 3)
|
||||||
|
|
||||||
|
def test_deduplicates_users(self):
|
||||||
|
trainer1 = User.objects.get(email="test-trainer1@example.com")
|
||||||
|
cs_zrh = CourseSession.objects.get(
|
||||||
|
title="Test Zürich 2022 a",
|
||||||
|
)
|
||||||
|
_csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs_zrh,
|
||||||
|
user=trainer1,
|
||||||
|
)
|
||||||
|
users = fetch_course_session_all_users([COURSE_TEST_ID])
|
||||||
|
for u in users:
|
||||||
|
print(u.edoniq_role, u.email)
|
||||||
|
self.assertEqual(len(users), 3)
|
||||||
|
|
||||||
def test_response_csv(self):
|
def test_response_csv(self):
|
||||||
users = fetch_course_session_users([COURSE_TEST_ID])
|
users = fetch_course_session_users([COURSE_TEST_ID])
|
||||||
response = generate_export_response(users)
|
response = generate_export_response(users)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import csv
|
import csv
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
from itertools import chain
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
|
from django.db.models import CharField, Value
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
|
@ -18,26 +20,53 @@ def export_students(request):
|
||||||
return generate_export_response(course_session_users)
|
return generate_export_response(course_session_users)
|
||||||
|
|
||||||
|
|
||||||
|
@staff_member_required
|
||||||
def export_trainers(request):
|
def export_trainers(request):
|
||||||
course_session_users = fetch_course_session_users(
|
course_session_users = fetch_course_session_users(
|
||||||
UK_COURSE_IDS, role=CourseSessionUser.Role.EXPERT
|
UK_COURSE_IDS, role=CourseSessionUser.Role.EXPERT
|
||||||
)
|
)
|
||||||
return generate_export_response(course_session_users, role="Trainer")
|
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], role=CourseSessionUser.Role.MEMBER):
|
def fetch_course_session_users(courses: List[int], role=CourseSessionUser.Role.MEMBER):
|
||||||
|
if role == CourseSessionUser.Role.EXPERT:
|
||||||
|
edoniq_role = "Trainer"
|
||||||
|
else:
|
||||||
|
edoniq_role = "Lernende"
|
||||||
|
|
||||||
# if users should be exported per course session, remove the distinct() call
|
# if users should be exported per course session, remove the distinct() call
|
||||||
return (
|
return (
|
||||||
User.objects.filter(
|
User.objects.filter(
|
||||||
coursesessionuser__course_session__course__id__in=courses,
|
coursesessionuser__course_session__course__id__in=courses,
|
||||||
coursesessionuser__role=role,
|
coursesessionuser__role=role,
|
||||||
)
|
)
|
||||||
|
.exclude(email__contains="eiger-versicherungen.ch") # exclude test users
|
||||||
|
.exclude(email__contains="assurance.ch")
|
||||||
.order_by("email")
|
.order_by("email")
|
||||||
|
.annotate(edoniq_role=Value(edoniq_role, output_field=CharField()))
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_export_response(cs_users: List[User], role="Lernende") -> HttpResponse:
|
def fetch_course_session_all_users(courses: List[int]):
|
||||||
|
course_session_users = fetch_course_session_users(courses)
|
||||||
|
course_session_trainers = fetch_course_session_users(
|
||||||
|
courses, role=CourseSessionUser.Role.EXPERT
|
||||||
|
)
|
||||||
|
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 = HttpResponse(content_type="text/csv")
|
||||||
response[
|
response[
|
||||||
"Content-Disposition"
|
"Content-Disposition"
|
||||||
|
|
@ -71,7 +100,7 @@ def generate_export_response(cs_users: List[User], role="Lernende") -> HttpRespo
|
||||||
cs_user.language,
|
cs_user.language,
|
||||||
cs_user.email,
|
cs_user.email,
|
||||||
cs_user.additional_json_data.get("Geburtsdatum", ""),
|
cs_user.additional_json_data.get("Geburtsdatum", ""),
|
||||||
role,
|
cs_user.edoniq_role,
|
||||||
cs_user.additional_json_data.get("Firmenname", ""),
|
cs_user.additional_json_data.get("Firmenname", ""),
|
||||||
cs_user.additional_json_data.get("Lehrvertragsnummer", ""),
|
cs_user.additional_json_data.get("Lehrvertragsnummer", ""),
|
||||||
cs_user.coursesessionuser_set.first().course_session.import_id,
|
cs_user.coursesessionuser_set.first().course_session.import_id,
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,7 @@ def create_or_update_user(
|
||||||
last_name: str = "",
|
last_name: str = "",
|
||||||
sso_id: str = None,
|
sso_id: str = None,
|
||||||
contract_number: str = "",
|
contract_number: str = "",
|
||||||
|
date_of_birth: str = "",
|
||||||
):
|
):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"create_or_update_user",
|
"create_or_update_user",
|
||||||
|
|
@ -200,9 +201,13 @@ def create_or_update_user(
|
||||||
if user_qs.exists():
|
if user_qs.exists():
|
||||||
user = user_qs.first()
|
user = user_qs.first()
|
||||||
|
|
||||||
|
# use the ID from DBPLAP2 (Lehrvertragsnummer, firstname, lastname, date of birth)
|
||||||
if not user and contract_number:
|
if not user and contract_number:
|
||||||
user_qs = User.objects.filter(
|
user_qs = User.objects.filter(
|
||||||
additional_json_data__Lehrvertragsnummer=contract_number
|
first_name=first_name,
|
||||||
|
last_name=last_name,
|
||||||
|
additional_json_data__Lehrvertragsnummer=contract_number,
|
||||||
|
additional_json_data__Geburtsdatum=date_of_birth,
|
||||||
)
|
)
|
||||||
if user_qs.exists():
|
if user_qs.exists():
|
||||||
user = user_qs.first()
|
user = user_qs.first()
|
||||||
|
|
@ -502,6 +507,8 @@ def import_students_from_excel(filename: str):
|
||||||
"Name",
|
"Name",
|
||||||
"Sprache",
|
"Sprache",
|
||||||
"Durchführungen",
|
"Durchführungen",
|
||||||
|
"Lehrvertragsnummer",
|
||||||
|
"Geburtsdatum",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
create_or_update_student(data)
|
create_or_update_student(data)
|
||||||
|
|
@ -514,11 +521,14 @@ def create_or_update_student(data: Dict[str, Any]):
|
||||||
label="import",
|
label="import",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
date_of_birth = _get_date_of_birth(data)
|
||||||
|
|
||||||
user = create_or_update_user(
|
user = create_or_update_user(
|
||||||
email=data["Email"].lower(),
|
email=data["Email"].lower(),
|
||||||
first_name=data["Vorname"],
|
first_name=data["Vorname"],
|
||||||
last_name=data["Name"],
|
last_name=data["Name"],
|
||||||
contract_number=data.get("Lehrvertragsnummer", ""),
|
contract_number=data.get("Lehrvertragsnummer", ""),
|
||||||
|
date_of_birth=date_of_birth,
|
||||||
)
|
)
|
||||||
|
|
||||||
user.language = data["Sprache"]
|
user.language = data["Sprache"]
|
||||||
|
|
@ -535,6 +545,16 @@ def create_or_update_student(data: Dict[str, Any]):
|
||||||
csu.save()
|
csu.save()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_date_of_birth(data: Dict[str, Any]) -> str:
|
||||||
|
date_of_birth = data.get("Geburtsdatum", None)
|
||||||
|
if date_of_birth is None:
|
||||||
|
return ""
|
||||||
|
elif date_of_birth is date or date_of_birth is datetime:
|
||||||
|
return date_of_birth.strftime("%d.%m.%Y")
|
||||||
|
elif type(date_of_birth) is str:
|
||||||
|
return date_of_birth
|
||||||
|
|
||||||
|
|
||||||
def sync_students_from_t2l_excel(filename: str):
|
def sync_students_from_t2l_excel(filename: str):
|
||||||
workbook = load_workbook(filename=filename)
|
workbook = load_workbook(filename=filename)
|
||||||
sheet = workbook.active
|
sheet = workbook.active
|
||||||
|
|
@ -546,10 +566,13 @@ def sync_students_from_t2l_excel(filename: str):
|
||||||
|
|
||||||
|
|
||||||
def sync_students_from_t2l(data):
|
def sync_students_from_t2l(data):
|
||||||
# ignore errors
|
date_of_birth = _get_date_of_birth(data)
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(
|
user = User.objects.get(
|
||||||
additional_json_data__Lehrvertragsnummer=data["Lehrvertragsnummer"]
|
first_name=data["Vorname"],
|
||||||
|
last_name=data["Name"],
|
||||||
|
additional_json_data__Lehrvertragsnummer=data["Lehrvertragsnummer"],
|
||||||
|
additional_json_data__Geburtsdatum=date_of_birth,
|
||||||
)
|
)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -51,6 +51,7 @@ class CreateOrUpdateStudentTestCase(TestCase):
|
||||||
"Durchführungen": "DE 2023 A",
|
"Durchführungen": "DE 2023 A",
|
||||||
"Lehrvertragsnummer": "1234",
|
"Lehrvertragsnummer": "1234",
|
||||||
"Tel. Privat": "079 593 83 43",
|
"Tel. Privat": "079 593 83 43",
|
||||||
|
"Geburtsdatum": "01.01.2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_create_student(self):
|
def test_create_student(self):
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class SyncT2lTestCase(TestCase):
|
||||||
"Durchführungen": "DE 2023 A",
|
"Durchführungen": "DE 2023 A",
|
||||||
"Lehrvertragsnummer": "1234",
|
"Lehrvertragsnummer": "1234",
|
||||||
"Tel. Privat": "079 593 83 43",
|
"Tel. Privat": "079 593 83 43",
|
||||||
|
"Geburtsdatum": "01.01.2000",
|
||||||
}
|
}
|
||||||
create_or_update_student(self.user_dict)
|
create_or_update_student(self.user_dict)
|
||||||
|
|
||||||
|
|
@ -44,6 +45,7 @@ class SyncT2lTestCase(TestCase):
|
||||||
"Durchführungen": "DE 2023 A",
|
"Durchführungen": "DE 2023 A",
|
||||||
"Lehrvertragsnummer": "1234",
|
"Lehrvertragsnummer": "1234",
|
||||||
"Tel. Privat": "079 593 83 65",
|
"Tel. Privat": "079 593 83 65",
|
||||||
|
"Geburtsdatum": "01.01.2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_students_from_t2l(user_dict)
|
sync_students_from_t2l(user_dict)
|
||||||
|
|
@ -65,6 +67,7 @@ class SyncT2lTestCase(TestCase):
|
||||||
"Lehrvertragsnummer": "1234",
|
"Lehrvertragsnummer": "1234",
|
||||||
"Tel. Privat": "079 593 83 43",
|
"Tel. Privat": "079 593 83 43",
|
||||||
"Firma": "VBV",
|
"Firma": "VBV",
|
||||||
|
"Geburtsdatum": "01.01.2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_students_from_t2l(user_dict)
|
sync_students_from_t2l(user_dict)
|
||||||
|
|
@ -84,6 +87,7 @@ class SyncT2lTestCase(TestCase):
|
||||||
"Durchführungen": "DE 2023 B",
|
"Durchführungen": "DE 2023 B",
|
||||||
"Lehrvertragsnummer": "1234",
|
"Lehrvertragsnummer": "1234",
|
||||||
"Tel. Privat": "079 593 83 43",
|
"Tel. Privat": "079 593 83 43",
|
||||||
|
"Geburtsdatum": "01.01.2000",
|
||||||
}
|
}
|
||||||
|
|
||||||
sync_students_from_t2l(user_dict)
|
sync_students_from_t2l(user_dict)
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,13 @@
|
||||||
<h2>Export Edoniq Teilnehmer</h2>
|
<h2>Export Edoniq Teilnehmer</h2>
|
||||||
<a href="{% url 'edoniq_export_students' %}" class="btn btn-primary">Teilnehmer exportieren</a>
|
<a href="{% url 'edoniq_export_students' %}" class="btn btn-primary">Teilnehmer exportieren</a>
|
||||||
|
|
||||||
<h2>Export Edoniq Trainer</h2>
|
<h2>Export Edoniq Trainer</h2>
|
||||||
<a href="{% url 'edoniq_export_trainers' %}" class="btn btn-primary">Trainer exportieren</a>
|
<a href="{% url 'edoniq_export_trainers' %}" class="btn btn-primary">Trainer exportieren</a>
|
||||||
|
|
||||||
|
<h2>Export Edoniq Teilnehmer und Trainer</h2>
|
||||||
|
<a href="{% url 'edoniq_export_students_and_trainers' %}" class="btn btn-primary">Teilnehmer und Trainer exportieren</a>
|
||||||
|
|
||||||
|
|
||||||
<h1>Reset</h1>
|
<h1>Reset</h1>
|
||||||
<form action="/api/core/cypressreset/" method="post">
|
<form action="/api/core/cypressreset/" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue