import { Injectable } from '@nestjs/common' import { isEmpty, pick } from 'lodash' import { plainToClass, classToPlain } from 'class-transformer' import { getDate, getMonth } from 'date-fns' import { HttpService } from '../services/http' import { LanguageCountryCode, isEmail, reduceToList } from '../utils' import { Patient } from '../models/patient.model' import { BaseRepository } from './base.repository' enum METHOD_BY_FIELD { EMAIL = 'findByEmail', DOCUMENT_NUMBER = 'findByDocumentNumber', } const INSERT = ` mutation insertPatient($patient: patients_insert_input!) { patient: insert_patients_one(object: $patient) { id } } ` const UPDATE = ` mutation updatePatient($id: String!, $auth_token: String!, $auth_token_created_at: timestamptz!, $menu_id: uuid, $telegram_id: String, $user_id: String, $sign_up_at: timestamptz, $tenant: tenant_enum) { update_patients_by_pk( pk_columns: {id: $id}, _set: { auth_token: $auth_token, auth_token_created_at: $auth_token_created_at, menu_id: $menu_id, telegram_id: $telegram_id, user_id: $user_id, sign_up_at: $sign_up_at, tenant: $tenant } ) { id } } ` const UPDATE_SMARTFIT_UNIT_ACRONYM = ` mutation updatePatient($id: String!, $smartfit_unit_acronym: String!) { update_patients_by_pk( pk_columns: {id: $id}, _set: { smartfit_unit_acronym: $smartfit_unit_acronym }) { id } } ` const UPDATE_SMARTFIT_DATA = ` mutation updatePatient($id: String!, $smartfit_id: Int!, $smartfit_main_purchase_id: Int!, $smartfit_unit_id: Int!) { update_patients_by_pk( pk_columns: {id: $id}, _set: { smartfit_id: $smartfit_id, smartfit_main_purchase_id: $smartfit_main_purchase_id, smartfit_unit_id: $smartfit_unit_id }) { id } } ` const UPDATE_BASIC_DATA = ` mutation updatePatient($id: String!, $tenant: tenant_enum, $email: String, $cell_phone: String) { update_patients_by_pk(pk_columns: {id: $id}, _set: {tenant: $tenant, email: $email, cell_phone: $cell_phone}) { id } } ` const UPDATE_SUBSCRIPTION = ` mutation updatePatient($id: String!, $subscriptionId: uuid!) { update_patients_by_pk(pk_columns: {id: $id}, _set: { subscription_id: $subscriptionId }){ id } } ` export const GENDER_SELECTION = ` gender { id male } ` export const FIND_ATTRIBUTES = ` id name email document_number cell_phone country_code birth_date auth_token auth_token_created_at created_at updated_at goal_id telegram_id menu_id user_id tenant sign_up_at collaborator device_id internal_collaborator smartfit_id smartfit_main_purchase_id smartfit_unit_id smartfit_unit_acronym smartfit_token omie_code ${GENDER_SELECTION} ` export const SUBSCRIPTION_ATTRIBUTES = ` cancellation_at cancellation_request_at created_at expires_at id patient_id plan_id trial_expires_at teleconsultations_credit payment_provider original_transaction omie_contract_code omie_os_code subscribed_at suspended_at ` export const SUBSCRIPTION_SELECTION = ` subscription { ${SUBSCRIPTION_ATTRIBUTES} } ` export const FIND_WITH_SUBSCRIPTION_ATTRIBUTES = ` ${FIND_ATTRIBUTES} ${SUBSCRIPTION_SELECTION} ` const FIND_BY_DOCUMENT_NUMBER = (properties = FIND_WITH_SUBSCRIPTION_ATTRIBUTES) => ` query patient($documentNumber: String!) { patients(where: {document_number: {_eq: $documentNumber}}) { ${properties} } } ` const FIND_BY_DOCUMENT_NUMBER_AND_TENANT = ` query patient($document_number: String!, $tenant: tenant_enum!) { patients(where: {document_number: {_eq: $document_number}, tenant: {_eq: $tenant}}) { id } } ` const FIND_BY_EMAIL = (properties = FIND_WITH_SUBSCRIPTION_ATTRIBUTES) => ` query patient($email: String!) { patients(where: {email: {_ilike: $email}}) { ${properties} } } ` const ALL = ` query patient($id: String!) { patients() { id user_id document_number } } ` const FIND_COLLABORATORS_BY_CREATION_ANNIVERSARY = ` query collaborators($day: float8!, $month: float8!) { collaborators: collaborators_created_day_view(where: {day: {_eq: $day}, month: {_eq: $month}}) { id } } ` const GET = ` query patient($id: String!) { patients(where: {id: {_eq: $id}}) { ${FIND_ATTRIBUTES} patient_restrictions { restriction_id } subscription { ${SUBSCRIPTION_ATTRIBUTES} } } } ` const GET_MANY = ` query MyQuery($ids: [String!] = "") { patients(where: {id: {_in: $ids}}) { ${FIND_ATTRIBUTES} patient_restrictions { restriction_id } subscription { ${SUBSCRIPTION_ATTRIBUTES} } } } ` const FIND_BY_USER = ` query patients($userId: String!) { patients(where: {user_id: {_eq: $userId}}) { id goal_id patient_restrictions { restriction_id } } } ` const FIND_ENABLED_USERS_FOR_BOOK_RETURN_SQL = ` WITH last_anamnesis AS ( SELECT a2.patient_id ,MAX(a2.done_at) AS done_at FROM anamnesis a2 GROUP BY a2.patient_id ), last_campaign_message AS ( SELECT cm2.patient_id ,MAX(cm2.sent_at) AS sent_at FROM campaign_message cm2 WHERE cm2.campaign_type = 'appointment_return_push' GROUP BY cm2.patient_id ) SELECT p2.id ,p2.cell_phone ,p2.device_id ,p2.country_code FROM patients p2 INNER JOIN last_anamnesis la ON la.patient_id = p2.id LEFT JOIN last_campaign_message lcm ON lcm.patient_id = p2.id WHERE la.done_at < (NOW() - interval '1 month') AND ( SELECT patients_teleconsultations_credit_SUM(p2)) > 0 AND (lcm.sent_at < (NOW() - interval '15 day') or lcm.sent_at is null) LIMIT 1000 ` const FIND_PENDING_EXTRA_DATA = (countryCode: string) => ` query patients($from: timestamptz, $until: timestamptz) { patients(where: {created_at: {_gte: $from, _lte: $until}, smartfit_id: {_is_null: true}, is_smartfit: {_eq: true}, country_code: {_eq: ${countryCode}}}) { id document_number } } ` @Injectable() export class PatientRepository extends BaseRepository { constructor(protected readonly httpService: HttpService) { super(httpService, 'patients', 'String') } async findByLogin(login: string, countryCode?: LanguageCountryCode, attributes?: Array): Promise { const attributesStr = attributes ? reduceToList(attributes) : FIND_WITH_SUBSCRIPTION_ATTRIBUTES let field = isEmail(login) ? 'email' : 'document_number' field = countryCode ? Patient.loginField(countryCode) : field return this[METHOD_BY_FIELD[field.toLocaleUpperCase()]](login, attributesStr) } async create(patient: Patient): Promise { const qv = { patient: classToPlain(patient) } const { data } = await this.fetchGraphQL({ body: { query: INSERT, variables: qv }, }) return data.patient.id } async update(patient: Patient): Promise { await this.fetchGraphQL({ body: { query: UPDATE, variables: pick(patient, [ 'id', 'auth_token', 'auth_token_created_at', 'menu_id', 'telegram_id', 'user_id', 'tenant', 'sign_up_at', ]), }, }) } async updateSmartfitUnitAcronym(id: string, smartfit_unit_acronym: string): Promise { await this.fetchGraphQL({ body: { query: UPDATE_SMARTFIT_UNIT_ACRONYM, variables: { id, smartfit_unit_acronym }, }, }) } async updateBasicData(patient: Patient): Promise { await this.fetchGraphQL({ body: { query: UPDATE_BASIC_DATA, variables: pick(patient, ['id', 'tenant', 'cell_phone', 'email']), }, }) } async updateSmartfitData(patient: Patient): Promise { await this.fetchGraphQL({ body: { query: UPDATE_SMARTFIT_DATA, variables: pick(patient, ['id', 'smartfit_id', 'smartfit_main_purchase_id', 'smartfit_unit_id']), }, }) } async updateSubscription(patient: Patient, subscriptionId: string): Promise { await this.fetchGraphQL({ body: { query: UPDATE_SUBSCRIPTION, variables: { id: patient.id, subscriptionId }, }, }) } async updateUser(patient: Patient, userId: string): Promise { patient.user_id = userId await this.save(patient, [{ field: 'user_id', type: 'String!' }]) } async findByDocumentNumber(documentNumber: string, attributes = FIND_WITH_SUBSCRIPTION_ATTRIBUTES): Promise { const { data } = await this.fetchGraphQL({ body: { query: FIND_BY_DOCUMENT_NUMBER(attributes), variables: { documentNumber } }, }) return isEmpty(data.patients) ? null : plainToClass(Patient, data.patients[0]) } async findPendingExtraDataBy(from: string, until: string, countryCode: string): Promise> { const { data } = await this.fetchGraphQL({ body: { query: FIND_PENDING_EXTRA_DATA(countryCode), variables: { from, until } }, }) return data?.patients?.map(row => plainToClass(Patient, row)) } async findByDocumentNumberAndTenant(documentNumber: string, tenant: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: FIND_BY_DOCUMENT_NUMBER_AND_TENANT, variables: { document_number: documentNumber, tenant }, }, }) return isEmpty(data.patients) ? null : plainToClass(Patient, data.patients[0]) } async findByEmail(email: string, attributes = FIND_WITH_SUBSCRIPTION_ATTRIBUTES): Promise { const { data } = await this.fetchGraphQL({ body: { query: FIND_BY_EMAIL(attributes), variables: { email } }, }) return isEmpty(data.patients) ? null : plainToClass(Patient, data.patients[0]) } async get(id: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET, variables: { id } }, }) return isEmpty(data.patients) ? null : plainToClass(Patient, data.patients[0]) } async getMany(ids: Array): Promise> { const { data } = await this.fetchGraphQL({ body: { query: GET_MANY, variables: { ids } }, }) return (data.patients || []).map((patient: Patient) => plainToClass(Patient, patient)) } async all(): Promise> { const { data } = await this.fetchGraphQL({ body: { query: ALL }, }) return (data.patients || []).map((patient: Patient) => plainToClass(Patient, patient)) } async findCollaboratorsByCreationAnniversary(date: Date): Promise { const { data } = await this.fetchGraphQL({ body: { query: FIND_COLLABORATORS_BY_CREATION_ANNIVERSARY, variables: { day: getDate(date), month: getMonth(date) + 1 }, }, }) return data?.collaborators?.map(row => plainToClass(Patient, row)) } async findByUser(userId: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: FIND_BY_USER, variables: { userId }, }, }) return data?.patients?.map(row => plainToClass(Patient, row)) || [] } async findEnabledForBookReturn(): Promise { const result = await this.fetchSQL(FIND_ENABLED_USERS_FOR_BOOK_RETURN_SQL) return result?.map((row: Patient) => Patient.toInstance(row)) } }