80 lines
3.9 KiB
Go
80 lines
3.9 KiB
Go
package storage
|
||
|
||
import "context"
|
||
|
||
// DoctorQuality — то, чего нет в списке врачей админки: записи биографии по видам,
|
||
// отзывы и заявки по каждому врачу. Полноту карточки считает страница «Дашборд врачей».
|
||
type DoctorQuality struct {
|
||
ID int64 `json:"id"`
|
||
Education int `json:"education"`
|
||
Experience int `json:"experience"`
|
||
Training int `json:"training"`
|
||
Awards int `json:"awards"`
|
||
Conferences int `json:"conferences"`
|
||
ReviewsPending int `json:"reviews_pending"`
|
||
ReviewsLive int `json:"reviews_published"`
|
||
RatingSum int `json:"rating_sum"` // сумма оценок всех отзывов с оценкой
|
||
RatingCount int `json:"rating_count"` // сколько отзывов с оценкой
|
||
Leads int `json:"leads"` // заявки к врачу или со страницы врача, без спама
|
||
KidsFrom bool `json:"kids_from"`
|
||
Languages bool `json:"languages"`
|
||
PrimarySpecialty *string `json:"primary_specialty"`
|
||
}
|
||
|
||
type DoctorQualityReport struct {
|
||
Doctors []DoctorQuality `json:"doctors"`
|
||
ReviewsPendingTotal int `json:"reviews_pending_total"`
|
||
ReviewsPendingNoDoc int `json:"reviews_pending_clinic"` // отзывы о клинике в целом
|
||
ReviewsLiveTotal int `json:"reviews_published_total"`
|
||
LeadsTotal int `json:"leads_total"`
|
||
}
|
||
|
||
func (s *Storage) DoctorQuality(ctx context.Context) (*DoctorQualityReport, error) {
|
||
rep := &DoctorQualityReport{Doctors: []DoctorQuality{}}
|
||
rows, err := s.pool.Query(ctx, `
|
||
SELECT d.id,
|
||
count(*) FILTER (WHERE e.kind = 'education'),
|
||
count(*) FILTER (WHERE e.kind = 'experience'),
|
||
count(*) FILTER (WHERE e.kind = 'training'),
|
||
count(*) FILTER (WHERE e.kind = 'award'),
|
||
count(*) FILTER (WHERE e.kind = 'conference'),
|
||
(SELECT count(*) FROM reviews r WHERE r.doctor_id = d.id AND r.status = 'pending'),
|
||
(SELECT count(*) FROM reviews r WHERE r.doctor_id = d.id AND r.status = 'published'),
|
||
(SELECT COALESCE(sum(r.rating), 0) FROM reviews r WHERE r.doctor_id = d.id AND r.rating IS NOT NULL AND r.status <> 'rejected'),
|
||
(SELECT count(r.rating) FROM reviews r WHERE r.doctor_id = d.id AND r.status <> 'rejected'),
|
||
(SELECT count(*) FROM leads l WHERE l.status <> 'spam' AND (l.doctor_id = d.id OR
|
||
(l.doctor_id IS NULL AND l.page_url LIKE '%/company/staff/%/' || d.slug || '/%'))),
|
||
d.accepts_kids_from IS NOT NULL,
|
||
cardinality(d.languages) > 0,
|
||
(SELECT sp.name_plural FROM doctor_specialties ds JOIN specialties sp ON sp.id = ds.specialty_id
|
||
WHERE ds.doctor_id = d.id ORDER BY ds.is_primary DESC, sp.sort, sp.id LIMIT 1)
|
||
FROM doctors d
|
||
LEFT JOIN doctor_bio_entries e ON e.doctor_id = d.id
|
||
WHERE d.deleted_at IS NULL
|
||
GROUP BY d.id
|
||
ORDER BY d.id`)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
for rows.Next() {
|
||
var q DoctorQuality
|
||
if err := rows.Scan(&q.ID, &q.Education, &q.Experience, &q.Training, &q.Awards, &q.Conferences,
|
||
&q.ReviewsPending, &q.ReviewsLive, &q.RatingSum, &q.RatingCount, &q.Leads,
|
||
&q.KidsFrom, &q.Languages, &q.PrimarySpecialty); err != nil {
|
||
return nil, err
|
||
}
|
||
rep.Doctors = append(rep.Doctors, q)
|
||
}
|
||
if err := rows.Err(); err != nil {
|
||
return nil, err
|
||
}
|
||
err = s.pool.QueryRow(ctx, `
|
||
SELECT count(*) FILTER (WHERE status = 'pending'),
|
||
count(*) FILTER (WHERE status = 'pending' AND doctor_id IS NULL),
|
||
count(*) FILTER (WHERE status = 'published'),
|
||
(SELECT count(*) FROM leads WHERE status <> 'spam')
|
||
FROM reviews`).Scan(&rep.ReviewsPendingTotal, &rep.ReviewsPendingNoDoc, &rep.ReviewsLiveTotal, &rep.LeadsTotal)
|
||
return rep, err
|
||
}
|