This commit is contained in:
Elia Bieri 2024-09-17 15:50:53 +02:00
parent 678ea12c73
commit 6c63027834
12 changed files with 58 additions and 52 deletions

View File

@ -1,3 +1,4 @@
import colors from "@/colors.json";
import { useTranslation } from "i18next-vue"; import { useTranslation } from "i18next-vue";
export function useChosenProfileMapping() { export function useChosenProfileMapping() {
@ -11,10 +12,10 @@ export function useChosenProfileMapping() {
}; };
const CHOSEN_PROFILE_TO_COLOR: Record<string, string> = { const CHOSEN_PROFILE_TO_COLOR: Record<string, string> = {
all: "#558AED", all: colors.blue[500],
leben: "#FE955A", leben: colors.orange[500],
nichtleben: "#54CE8B", nichtleben: colors.green[500],
krankenzusatzversicherung: "#FAC852", krankenzusatzversicherung: colors.yellow[400],
}; };
return { return {

View File

@ -359,13 +359,6 @@ export type CoreUserLanguageChoices =
/** Italiano */ /** Italiano */
| 'IT'; | 'IT';
export type CostForYear = {
__typename?: 'CostForYear';
_id: Scalars['ID']['output'];
total_cost: Scalars['Int']['output'];
year: Scalars['Int']['output'];
};
export type CourseConfigurationObjectType = { export type CourseConfigurationObjectType = {
__typename?: 'CourseConfigurationObjectType'; __typename?: 'CourseConfigurationObjectType';
enable_circle_documents: Scalars['Boolean']['output']; enable_circle_documents: Scalars['Boolean']['output'];
@ -929,13 +922,6 @@ export type MutationUpsertAssignmentCompletionArgs = {
learning_content_page_id?: InputMaybe<Scalars['ID']['input']>; learning_content_page_id?: InputMaybe<Scalars['ID']['input']>;
}; };
export type ParticipantsForYear = {
__typename?: 'ParticipantsForYear';
_id: Scalars['ID']['output'];
participants: Array<Maybe<CourseSessionUserType>>;
year: Scalars['Int']['output'];
};
export type PerformanceCriteriaObjectType = CoursePageInterface & { export type PerformanceCriteriaObjectType = CoursePageInterface & {
__typename?: 'PerformanceCriteriaObjectType'; __typename?: 'PerformanceCriteriaObjectType';
competence_id: Scalars['String']['output']; competence_id: Scalars['String']['output'];
@ -1136,9 +1122,23 @@ export type TopicObjectType = CoursePageInterface & {
export type TrainingResponsibleStatisticsType = { export type TrainingResponsibleStatisticsType = {
__typename?: 'TrainingResponsibleStatisticsType'; __typename?: 'TrainingResponsibleStatisticsType';
_id: Scalars['ID']['output']; _id: Scalars['ID']['output'];
cost_per_year: Array<Maybe<CostForYear>>; cost_per_year: Array<Maybe<TrainingsResponsibleCostForYear>>;
course_session_id: Scalars['ID']['output']; course_session_id: Scalars['ID']['output'];
participants_per_year: Array<Maybe<ParticipantsForYear>>; participants_per_year: Array<Maybe<TrainingsResponsibleParticipantsForYear>>;
};
export type TrainingsResponsibleCostForYear = {
__typename?: 'TrainingsResponsibleCostForYear';
_id: Scalars['ID']['output'];
total_cost: Scalars['Int']['output'];
year: Scalars['Int']['output'];
};
export type TrainingsResponsibleParticipantsForYear = {
__typename?: 'TrainingsResponsibleParticipantsForYear';
_id: Scalars['ID']['output'];
participants: Array<Maybe<CourseSessionUserType>>;
year: Scalars['Int']['output'];
}; };
export type UpdateCourseProfileError = { export type UpdateCourseProfileError = {
@ -1489,7 +1489,7 @@ export type TrainingResponsibleStatisticsQueryVariables = Exact<{
}>; }>;
export type TrainingResponsibleStatisticsQuery = { __typename?: 'Query', training_responsible_statistics?: { __typename?: 'TrainingResponsibleStatisticsType', _id: string, cost_per_year: Array<{ __typename?: 'CostForYear', _id: string, year: number, total_cost: number } | null>, participants_per_year: Array<{ __typename?: 'ParticipantsForYear', _id: string, year: number, participants: Array<{ __typename?: 'CourseSessionUserType', id: string, chosen_profile: string } | null> } | null> } | null }; export type TrainingResponsibleStatisticsQuery = { __typename?: 'Query', training_responsible_statistics?: { __typename?: 'TrainingResponsibleStatisticsType', _id: string, cost_per_year: Array<{ __typename?: 'TrainingsResponsibleCostForYear', _id: string, year: number, total_cost: number } | null>, participants_per_year: Array<{ __typename?: 'TrainingsResponsibleParticipantsForYear', _id: string, year: number, participants: Array<{ __typename?: 'CourseSessionUserType', id: string, chosen_profile: string } | null> } | null> } | null };
export type SendFeedbackMutationMutationVariables = Exact<{ export type SendFeedbackMutationMutationVariables = Exact<{
courseSessionId: Scalars['ID']['input']; courseSessionId: Scalars['ID']['input'];

View File

@ -204,17 +204,17 @@ type BaseStatisticsType {
type TrainingResponsibleStatisticsType { type TrainingResponsibleStatisticsType {
_id: ID! _id: ID!
course_session_id: ID! course_session_id: ID!
cost_per_year: [CostForYear]! cost_per_year: [TrainingsResponsibleCostForYear]!
participants_per_year: [ParticipantsForYear]! participants_per_year: [TrainingsResponsibleParticipantsForYear]!
} }
type CostForYear { type TrainingsResponsibleCostForYear {
_id: ID! _id: ID!
year: Int! year: Int!
total_cost: Int! total_cost: Int!
} }
type ParticipantsForYear { type TrainingsResponsibleParticipantsForYear {
_id: ID! _id: ID!
year: Int! year: Int!
participants: [CourseSessionUserType]! participants: [CourseSessionUserType]!

View File

@ -26,7 +26,6 @@ export const CompetenceRecordStatisticsType = "CompetenceRecordStatisticsType";
export const CompetencesStatisticsType = "CompetencesStatisticsType"; export const CompetencesStatisticsType = "CompetencesStatisticsType";
export const ContentDocumentObjectType = "ContentDocumentObjectType"; export const ContentDocumentObjectType = "ContentDocumentObjectType";
export const CoreUserLanguageChoices = "CoreUserLanguageChoices"; export const CoreUserLanguageChoices = "CoreUserLanguageChoices";
export const CostForYear = "CostForYear";
export const CourseConfigurationObjectType = "CourseConfigurationObjectType"; export const CourseConfigurationObjectType = "CourseConfigurationObjectType";
export const CourseObjectType = "CourseObjectType"; export const CourseObjectType = "CourseObjectType";
export const CoursePageInterface = "CoursePageInterface"; export const CoursePageInterface = "CoursePageInterface";
@ -75,7 +74,6 @@ export const LearningSequenceObjectType = "LearningSequenceObjectType";
export const LearningUnitObjectType = "LearningUnitObjectType"; export const LearningUnitObjectType = "LearningUnitObjectType";
export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices"; export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices";
export const Mutation = "Mutation"; export const Mutation = "Mutation";
export const ParticipantsForYear = "ParticipantsForYear";
export const PerformanceCriteriaObjectType = "PerformanceCriteriaObjectType"; export const PerformanceCriteriaObjectType = "PerformanceCriteriaObjectType";
export const PresenceRecordStatisticsType = "PresenceRecordStatisticsType"; export const PresenceRecordStatisticsType = "PresenceRecordStatisticsType";
export const ProgressDashboardAssignmentType = "ProgressDashboardAssignmentType"; export const ProgressDashboardAssignmentType = "ProgressDashboardAssignmentType";
@ -89,6 +87,8 @@ export const StatisticsCourseSessionsSelectionMetricType = "StatisticsCourseSess
export const String = "String"; export const String = "String";
export const TopicObjectType = "TopicObjectType"; export const TopicObjectType = "TopicObjectType";
export const TrainingResponsibleStatisticsType = "TrainingResponsibleStatisticsType"; export const TrainingResponsibleStatisticsType = "TrainingResponsibleStatisticsType";
export const TrainingsResponsibleCostForYear = "TrainingsResponsibleCostForYear";
export const TrainingsResponsibleParticipantsForYear = "TrainingsResponsibleParticipantsForYear";
export const UUID = "UUID"; export const UUID = "UUID";
export const UpdateCourseProfileError = "UpdateCourseProfileError"; export const UpdateCourseProfileError = "UpdateCourseProfileError";
export const UpdateCourseProfileResult = "UpdateCourseProfileResult"; export const UpdateCourseProfileResult = "UpdateCourseProfileResult";

View File

@ -195,9 +195,9 @@ const selectedChosenProfile = ref<MenuItem>(chosenProfiles.value[0]);
const paidYears = computed(() => { const paidYears = computed(() => {
const values = _(dashboardPersons.value) const values = _(dashboardPersons.value)
.filter((cs) => dayjs(cs.paid_date).isValid()) .filter((cs) => dayjs(cs.paid_datetime).isValid())
.map((cs) => { .map((cs) => {
const paidYear = dayjs(cs.paid_date).format("YYYY"); const paidYear = dayjs(cs.paid_datetime).format("YYYY");
return Object.assign({}, cs, { return Object.assign({}, cs, {
name: paidYear, name: paidYear,
id: paidYear, id: paidYear,
@ -268,7 +268,7 @@ const filteredPersons = computed(() => {
if (selectedPaidYear.value.id === UNFILTERED) { if (selectedPaidYear.value.id === UNFILTERED) {
return true; return true;
} }
const paidYear = dayjs(person.paid_date).format("YYYY"); const paidYear = dayjs(person.paid_datetime).format("YYYY");
return paidYear == selectedPaidYear.value.id; return paidYear == selectedPaidYear.value.id;
}), }),
["last_name", "first_name"] ["last_name", "first_name"]

View File

@ -83,7 +83,7 @@ export type DashboardPersonType = {
passed_count: number; passed_count: number;
failed_count: number; failed_count: number;
}; };
paid_date?: string; paid_datetime?: string;
}; };
export type DashboardCourseConfigType = { export type DashboardCourseConfigType = {

View File

@ -22,17 +22,17 @@ def main():
berufsbildner, _ = User.objects.get_or_create( berufsbildner, _ = User.objects.get_or_create(
id="5f984be9-3024-4169-9c7b-c9e827c18fd8" id="5f984be9-3024-4169-9c7b-c9e827c18fd8"
) )
berufsbildner.username = "training-responsible-mobi@example.com" berufsbildner.username = "berufsbildner-mobi@example.com"
berufsbildner.email = "training-responsible-mobi@example.com" berufsbildner.email = "berufsbildner-mobi@example.com"
berufsbildner.language = "de" berufsbildner.language = "de"
berufsbildner.first_name = "Ausbildungsverantwortlicher" berufsbildner.first_name = "Berufsbildner"
berufsbildner.last_name = "Mobi" berufsbildner.last_name = "Mobi"
berufsbildner.password = make_password("test") berufsbildner.password = make_password("test")
berufsbildner.save() berufsbildner.save()
for csu in ( for csu in (
CourseSessionUser.objects.filter(user__username__contains="@mobi") CourseSessionUser.objects.filter(user__username__contains="@mobi")
.filter(course_session__course__configuration__is_uk=False) .filter(course_session__course__configuration__is_uk=True)
.filter(role=CourseSessionUser.Role.MEMBER.value) .filter(role=CourseSessionUser.Role.MEMBER.value)
.exclude(course_session_id__in=[4, 5, 6]) .exclude(course_session_id__in=[4, 5, 6])
): ):

View File

@ -304,11 +304,13 @@ class CourseSessionUser(models.Model):
ordering = ["user__last_name", "user__first_name", "user__email"] ordering = ["user__last_name", "user__first_name", "user__email"]
@property @property
def paid_date(self) -> datetime | None: def paid_datetime(self) -> datetime | None:
""" """
Returns the date when the user paid for the course session Returns the datetime when the user paid for the course session
""" """
checkout = self.user.checkout_information.filter(state=CheckoutState.PAID) checkout = self.user.checkout_informations.filter(
state=CheckoutState.PAID
).order_by("created_at")
if checkout: if checkout:
return checkout.first().created_at return checkout.first().created_at
return None return None

View File

@ -256,14 +256,14 @@ class CourseStatisticsType(BaseStatisticsType):
) )
class CostForYear(graphene.ObjectType): class TrainingsResponsibleCostForYear(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
year = graphene.Int(required=True) year = graphene.Int(required=True)
# In centimes CHF # In centimes CHF
total_cost = graphene.Int(required=True) total_cost = graphene.Int(required=True)
class ParticipantsForYear(graphene.ObjectType): class TrainingsResponsibleParticipantsForYear(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
year = graphene.Int(required=True) year = graphene.Int(required=True)
participants = graphene.List(CourseSessionUserType, required=True) participants = graphene.List(CourseSessionUserType, required=True)
@ -273,8 +273,10 @@ class TrainingResponsibleStatisticsType(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
course_session_id = graphene.ID(required=True) course_session_id = graphene.ID(required=True)
cost_per_year = graphene.List(CostForYear, required=True) cost_per_year = graphene.List(TrainingsResponsibleCostForYear, required=True)
participants_per_year = graphene.List(ParticipantsForYear, required=True) participants_per_year = graphene.List(
TrainingsResponsibleParticipantsForYear, required=True
)
@staticmethod @staticmethod
def resolve_cost_per_year(root, info): # noqa def resolve_cost_per_year(root, info): # noqa
@ -297,7 +299,7 @@ class TrainingResponsibleStatisticsType(graphene.ObjectType):
key=lambda x: x.created_at.year, key=lambda x: x.created_at.year,
) )
return [ return [
CostForYear( TrainingsResponsibleCostForYear(
_id=f"{root.course_session_id} {year}", _id=f"{root.course_session_id} {year}",
year=year, year=year,
total_cost=sum(c.product_price for c in checkouts), total_cost=sum(c.product_price for c in checkouts),
@ -314,17 +316,16 @@ class TrainingResponsibleStatisticsType(graphene.ObjectType):
agentparticipantrelation__participant__course_session=root.course_session_id, agentparticipantrelation__participant__course_session=root.course_session_id,
) )
.annotate( .annotate(
checkout_year=ExtractYear("user__checkout_information__created_at") checkout_year=ExtractYear("user__checkout_informations__created_at")
) )
.select_related("user") .select_related("user")
) )
grouped_course_session_users = defaultdict(list) grouped_course_session_users = defaultdict(list)
for csu in course_session_users: for csu in course_session_users:
checkout_year = csu.checkout_year if csu.checkout_year:
if checkout_year: grouped_course_session_users[csu.checkout_year].append(csu)
grouped_course_session_users[checkout_year].append(csu)
return [ return [
ParticipantsForYear( TrainingsResponsibleParticipantsForYear(
_id=f"{root.course_session_id} {year}", _id=f"{root.course_session_id} {year}",
year=year, year=year,
participants=participants, participants=participants,

View File

@ -109,7 +109,9 @@ def create_person_list_with_roles(
"avatar_url": user_object.avatar_url, "avatar_url": user_object.avatar_url,
"course_sessions": [], "course_sessions": [],
"chosen_profile": csu.chosen_profile.code if csu.chosen_profile else "all", "chosen_profile": csu.chosen_profile.code if csu.chosen_profile else "all",
"paid_date": csu.paid_date.isoformat() if csu.paid_date else None, "paid_datetime": csu.paid_datetime.isoformat()
if csu.paid_datetime
else None,
} }
if include_private_data: if include_private_data:
user_data["phone_number"] = user_object.phone_number user_data["phone_number"] = user_object.phone_number

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.13 on 2024-09-12 13:40 # Generated by Django 4.2.13 on 2024-09-17 14:00
import django.db.models.deletion import django.db.models.deletion
from django.conf import settings from django.conf import settings
@ -17,7 +17,7 @@ class Migration(migrations.Migration):
name="user", name="user",
field=models.ForeignKey( field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT, on_delete=django.db.models.deletion.PROTECT,
related_name="checkout_information", related_name="checkout_informations",
to=settings.AUTH_USER_MODEL, to=settings.AUTH_USER_MODEL,
), ),
), ),

View File

@ -44,7 +44,7 @@ class CheckoutInformation(models.Model):
) )
user = models.ForeignKey( user = models.ForeignKey(
"core.User", on_delete=models.PROTECT, related_name="checkout_information" "core.User", on_delete=models.PROTECT, related_name="checkout_informations"
) )
product_sku = models.CharField(max_length=255) product_sku = models.CharField(max_length=255)