MicProtocol / apps /api /dist /analytics /analytics.service.js
Benaah's picture
Upload folder using huggingface_hub
303c85f verified
Raw History Blame Contribute Delete
13.6 kB
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var AnalyticsService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyticsService = void 0;
const common_1 = require("@nestjs/common");
const prisma_service_1 = require("../prisma/prisma.service");
const authorization_service_1 = require("../common/authorization.service");
let AnalyticsService = AnalyticsService_1 = class AnalyticsService {
prisma;
authorizationService;
logger = new common_1.Logger(AnalyticsService_1.name);
cache = new Map();
cacheTTL = 30_000;
constructor(prisma, authorizationService) {
this.prisma = prisma;
this.authorizationService = authorizationService;
}
getCached(key) {
const entry = this.cache.get(key);
if (entry && entry.expiresAt > Date.now())
return entry.data;
this.cache.delete(key);
return null;
}
setCache(key, data) {
this.cache.set(key, { data, expiresAt: Date.now() + this.cacheTTL });
if (this.cache.size > 100) {
const oldest = this.cache.keys().next().value;
if (oldest)
this.cache.delete(oldest);
}
}
async getDashboardMetrics(podcastId, userId) {
await this.authorizationService.checkPodcastAccess(podcastId, userId);
const cached = this.getCached(`dashboard:${podcastId}`);
if (cached)
return cached;
const [totalGuests, guestsByStatus, totalEpisodes, episodesByStatus, upcomingBookings, recentGuests, topPriorityGuests,] = await Promise.all([
this.prisma.guest.count({ where: { podcastId } }),
this.prisma.guest.groupBy({
by: ["status"],
where: { podcastId },
_count: { id: true },
}),
this.prisma.episode.count({ where: { podcastId } }),
this.prisma.episode.groupBy({
by: ["status"],
where: { podcastId },
_count: { id: true },
}),
this.prisma.booking.count({
where: {
episode: { podcastId },
startTime: { gte: new Date() },
},
}),
this.prisma.guest.findMany({
where: { podcastId },
orderBy: { createdAt: "desc" },
take: 5,
select: {
id: true,
fullName: true,
email: true,
company: true,
status: true,
priorityScore: true,
createdAt: true,
},
}),
this.prisma.guest.findMany({
where: { podcastId, status: { in: ["prospect", "contacted"] } },
orderBy: { priorityScore: "desc" },
take: 5,
select: {
id: true,
fullName: true,
company: true,
title: true,
priorityScore: true,
},
}),
]);
const guestStatusMap = {};
for (const item of guestsByStatus) {
guestStatusMap[item.status] = item._count.id;
}
const episodeStatusMap = {};
for (const item of episodesByStatus) {
episodeStatusMap[item.status] = item._count.id;
}
const result = {
totalGuests,
guestsByStatus: guestStatusMap,
totalEpisodes,
episodesByStatus: episodeStatusMap,
upcomingBookings,
recentGuests,
topPriorityGuests,
};
this.setCache(`dashboard:${podcastId}`, result);
return result;
}
async getGuestPipeline(podcastId, userId) {
await this.authorizationService.checkPodcastAccess(podcastId, userId);
const cached = this.getCached(`pipeline:${podcastId}`);
if (cached)
return cached;
const pipeline = await this.prisma.guest.groupBy({
by: ["status"],
where: { podcastId },
_count: { id: true },
_avg: { priorityScore: true },
});
const result = pipeline.map((stage) => ({
status: stage.status,
count: stage._count.id,
avgScore: Math.round(stage._avg.priorityScore || 0),
}));
this.setCache(`pipeline:${podcastId}`, result);
return result;
}
async getOutreachMetrics(podcastId, userId) {
await this.authorizationService.checkPodcastAccess(podcastId, userId);
const cached = this.getCached(`outreach:${podcastId}`);
if (cached)
return cached;
const guests = await this.prisma.guest.findMany({
where: { podcastId },
select: { id: true },
});
const guestIds = guests.map((g) => g.id);
if (guestIds.length === 0) {
return {
totalSequences: 0,
activeSequences: 0,
totalMessagesSent: 0,
openRate: 0,
clickRate: 0,
replyRate: 0,
};
}
const [totalSequences, activeSequences, messageStats] = await Promise.all([
this.prisma.outreachSequence.count({
where: { guestId: { in: guestIds } },
}),
this.prisma.outreachSequence.count({
where: { guestId: { in: guestIds }, status: "active" },
}),
this.prisma.outreachMessage.aggregate({
where: { sequence: { guestId: { in: guestIds } } },
_count: { id: true, sentAt: true, openedAt: true, clickedAt: true },
}),
]);
const sent = messageStats._count.sentAt;
const opened = messageStats._count.openedAt;
const clicked = messageStats._count.clickedAt;
const result = {
totalSequences,
activeSequences,
totalMessagesSent: sent,
openRate: sent > 0 ? Math.round((opened / sent) * 100) : 0,
clickRate: sent > 0 ? Math.round((clicked / sent) * 100) : 0,
};
this.setCache(`outreach:${podcastId}`, result);
return result;
}
async getSchedulingMetrics(podcastId, userId, daysBack = 90) {
await this.authorizationService.checkPodcastAccess(podcastId, userId);
const cacheKey = `scheduling:${podcastId}:${daysBack}`;
const cached = this.getCached(cacheKey);
if (cached)
return cached;
const since = new Date(Date.now() - daysBack * 24 * 60 * 60 * 1000);
const [scheduledCount, recordedCount, publishedCount, avgDuration] = await Promise.all([
this.prisma.episode.count({
where: {
podcastId,
status: "scheduled",
createdAt: { gte: since },
},
}),
this.prisma.episode.count({
where: {
podcastId,
status: "recorded",
createdAt: { gte: since },
},
}),
this.prisma.episode.count({
where: {
podcastId,
status: "published",
createdAt: { gte: since },
},
}),
this.prisma.episode.aggregate({
where: { podcastId, durationMinutes: { not: null } },
_avg: { durationMinutes: true },
}),
]);
const result = {
scheduledCount,
recordedCount,
publishedCount,
conversionRate: scheduledCount > 0
? Math.round((recordedCount / scheduledCount) * 100)
: 0,
avgDurationMinutes: Math.round(avgDuration._avg.durationMinutes || 0),
};
this.setCache(cacheKey, result);
return result;
}
async getRecentActivity(podcastId, userId, limit = 20) {
await this.authorizationService.checkPodcastAccess(podcastId, userId);
const cacheKey = `recent:${podcastId}:${limit}`;
const cached = this.getCached(cacheKey);
if (cached)
return cached;
const [recentGuests, recentEpisodes, recentBookings, recentOutreach] = await Promise.all([
this.prisma.guest.findMany({
where: { podcastId },
orderBy: { createdAt: "desc" },
take: limit,
select: {
id: true,
fullName: true,
status: true,
createdAt: true,
_count: {
select: {
outreachSequences: true,
},
},
},
}),
this.prisma.episode.findMany({
where: { podcastId },
orderBy: { createdAt: "desc" },
take: limit,
select: {
id: true,
title: true,
status: true,
scheduledAt: true,
createdAt: true,
guest: {
select: {
fullName: true,
},
},
},
}),
this.prisma.booking.findMany({
where: { episode: { podcastId } },
orderBy: { createdAt: "desc" },
take: limit,
select: {
id: true,
startTime: true,
isConfirmed: true,
createdAt: true,
episode: {
select: {
title: true,
guest: {
select: {
fullName: true,
},
},
},
},
},
}),
this.prisma.outreachMessage.findMany({
where: { sequence: { guest: { podcastId } } },
orderBy: { sentAt: "desc" },
take: limit,
select: {
id: true,
type: true,
sentAt: true,
openedAt: true,
sequence: {
select: {
guest: {
select: {
fullName: true,
},
},
},
},
},
}),
]);
const activities = [
...recentGuests.map((g) => ({
type: "guest_added",
id: g.id,
title: `New guest: ${g.fullName}`,
status: g.status,
timestamp: g.createdAt,
})),
...recentEpisodes.map((e) => ({
type: "episode_created",
id: e.id,
title: `Episode: ${e.title}`,
subtitle: `With ${e.guest.fullName}`,
status: e.status,
timestamp: e.createdAt,
scheduledAt: e.scheduledAt,
})),
...recentBookings.map((b) => ({
type: "booking_created",
id: b.id,
title: `Booking: ${b.episode.title}`,
subtitle: `With ${b.episode.guest.fullName}`,
timestamp: b.createdAt,
startTime: b.startTime,
isConfirmed: b.isConfirmed,
})),
...recentOutreach.map((o) => ({
type: "message_sent",
id: o.id,
title: `Email sent to ${o.sequence.guest.fullName}`,
subtitle: o.type,
timestamp: o.sentAt,
openedAt: o.openedAt,
})),
];
const result = activities
.filter((a) => a.timestamp != null)
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
.slice(0, limit);
this.setCache(cacheKey, result);
return result;
}
};
exports.AnalyticsService = AnalyticsService;
exports.AnalyticsService = AnalyticsService = AnalyticsService_1 = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
authorization_service_1.AuthorizationService])
], AnalyticsService);
//# sourceMappingURL=analytics.service.js.map