Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified NLP_Model/actual/__pycache__/DataProcess.cpython-314.pyc
Binary file not shown.
Binary file modified NLP_Model/actual/__pycache__/MongoConnect.cpython-314.pyc
Binary file not shown.
Binary file modified NLP_Model/actual/__pycache__/Summarizer.cpython-314.pyc
Binary file not shown.
131 changes: 131 additions & 0 deletions bolt_slack/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
HOME_CHANNEL_SELECT_ACTION_ID,
HOME_REFRESH_ACTION_ID,
HOME_SUMMARY_WEEK_SELECT_ACTION_ID,
HOME_GENERATE_SELECTED_WEEK_SUMMARY_ACTION_ID,
HOME_USER_SUMMARY_SELECT_ACTION_ID,
HOME_GENERATE_USER_SUMMARIES_ACTION_ID,
HOME_GENERATE_SINGLE_USER_SUMMARY_ACTION_ID,
Expand Down Expand Up @@ -143,6 +144,35 @@ async function getWorkspaceToken(teamId) {
return null;
}

function formatWeekRangeForMessage(weekKey) {
const normalizedWeekKey = typeof weekKey === 'string' ? weekKey.trim() : '';
if (!normalizedWeekKey.startsWith('ws:')) {
return normalizedWeekKey || 'Unknown week';
}

const startIso = normalizedWeekKey.slice(3);
const start = new Date(startIso);
if (Number.isNaN(start.getTime())) {
return startIso || 'Unknown week';
}

const end = new Date(start);
end.setUTCDate(end.getUTCDate() + 6);

const startText = start.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric'
});
const endText = end.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric'
});

return `${startText} - ${endText}`;
}

async function authorizeWorkspace({ teamId }) {
const botToken = await getWorkspaceToken(teamId);

Expand Down Expand Up @@ -870,6 +900,107 @@ app.action(HOME_SUMMARY_WEEK_SELECT_ACTION_ID, async ({ ack, body, client, logge
});
});

// Home tab selected-week summary button: run weekly model for the selected week.
app.action(HOME_GENERATE_SELECTED_WEEK_SUMMARY_ACTION_ID, async ({ ack, body, client, logger }) => {
await ack();
const { teamId, workspaceName } = getHomeWorkspaceContext(body);

try {
const actionValue = body.actions && body.actions[0] && body.actions[0].value
? body.actions[0].value
: encodeDashboardState({ channelName: '', selectedWeek: null, selectedUser: null });
const {
channelName: selectedChannelName,
selectedWeek,
selectedUser
} = parseDashboardState(actionValue);

if (!selectedChannelName) {
const dm = await client.conversations.open({ users: body.user.id });
await client.chat.postMessage({
channel: dm.channel.id,
text: 'Select a channel in Home first, then click *Generate Summary For Selected Week*.'
});
return;
}

const selectedWeekRaw = selectedWeek || getSelectedOptionValueFromViewState(body.view && body.view.state, HOME_SUMMARY_WEEK_SELECT_ACTION_ID);
if (!selectedWeekRaw || !selectedWeekRaw.startsWith('ws:')) {
const dm = await client.conversations.open({ users: body.user.id });
await client.chat.postMessage({
channel: dm.channel.id,
text: 'Select a week in Home first, then click *Generate Summary For Selected Week*.'
});
return;
}

const weekStart = selectedWeekRaw.slice(3);
const weekLabel = formatWeekRangeForMessage(selectedWeekRaw);
const databaseKey = await buildChannelKey(selectedChannelName, { client });

const dm = await client.conversations.open({ users: body.user.id });
await client.chat.postMessage({
channel: dm.channel.id,
text: `⏳ Starting weekly summarization for *#${selectedChannelName}* (${weekLabel})... I’ll send a follow-up when it finishes.`
});

void (async () => {
try {
const response = await apiClient.post(
`/api/summaries/${encodeURIComponent(databaseKey)}?weekStart=${encodeURIComponent(weekStart)}`,
null,
{ timeout: 0 }
);

const savedCount = Number(response && response.data && response.data.savedCount);
await client.chat.postMessage({
channel: dm.channel.id,
text: Number.isFinite(savedCount)
? `✅ Weekly summarization completed for *#${selectedChannelName}* (${weekLabel}). Saved ${savedCount} summaries.`
: `✅ Weekly summarization completed for *#${selectedChannelName}* (${weekLabel}).`
});

publishHomeTab({
client,
userId: body.user.id,
teamId,
workspaceName,
logger,
apiClient,
selectedChannelName,
selectedWeek: selectedWeekRaw,
selectedUser
}).catch((refreshError) => {
logger.error('Error refreshing Home after selected-week summary generation:', refreshError);
});
} catch (generationError) {
logger.error('Background selected-week summary generation failed:', generationError);

try {
await client.chat.postMessage({
channel: dm.channel.id,
text: `❌ Failed weekly summarization for *#${selectedChannelName}* (${weekLabel}): ${generationError.message}`
});
} catch (dmError) {
logger.error('Failed sending selected-week generation error via DM:', dmError);
}
}
})();
} catch (error) {
logger.error('Error handling selected-week summary action:', error);

try {
const dm = await client.conversations.open({ users: body.user.id });
await client.chat.postMessage({
channel: dm.channel.id,
text: `❌ Failed to generate selected-week summary: ${error.message}`
});
} catch (dmError) {
logger.error('Failed sending selected-week handler error via DM:', dmError);
}
}
});

// Home tab user summary dropdown: republish with selected user summary updates.
app.action(HOME_USER_SUMMARY_SELECT_ACTION_ID, async ({ ack, body, client, logger }) => {
await ack();
Expand Down
130 changes: 108 additions & 22 deletions bolt_slack/homeDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { buildChannelKey } = require('../shared-utils/channelUtils');
const HOME_CHANNEL_SELECT_ACTION_ID = 'home_channel_select';
const HOME_REFRESH_ACTION_ID = 'home_refresh_button';
const HOME_SUMMARY_WEEK_SELECT_ACTION_ID = 'home_summary_week_select';
const HOME_GENERATE_SELECTED_WEEK_SUMMARY_ACTION_ID = 'home_generate_selected_week_summary';
const HOME_USER_SUMMARY_SELECT_ACTION_ID = 'home_user_summary_select';
const HOME_GENERATE_USER_SUMMARIES_ACTION_ID = 'home_generate_user_summaries';
const HOME_GENERATE_SINGLE_USER_SUMMARY_ACTION_ID = 'home_generate_single_user_summary';
Expand Down Expand Up @@ -234,6 +235,64 @@ function getAvailableWeeks(summaries) {
.map((bucket) => bucket.key);
}

function getAvailableWeeksFromChannelCreation(channelCreatedAtTs) {
const now = new Date();
if (Number.isNaN(now.getTime())) {
return [];
}

const normalizedCreatedTs = Number(channelCreatedAtTs);
const createdAt = Number.isFinite(normalizedCreatedTs) && normalizedCreatedTs > 0
? new Date(normalizedCreatedTs * 1000)
: null;
const createdAtOrNow = createdAt && !Number.isNaN(createdAt.getTime())
? createdAt
: now;

const startWeekIso = normalizeToUtcSundayStartIso(createdAtOrNow.toISOString());
const currentWeekIso = normalizeToUtcSundayStartIso(now.toISOString());
if (!startWeekIso || !currentWeekIso) {
return [];
}

const startWeekMs = Date.parse(startWeekIso);
const currentWeekMs = Date.parse(currentWeekIso);
if (!Number.isFinite(startWeekMs) || !Number.isFinite(currentWeekMs)) {
return [];
}

if (currentWeekMs < startWeekMs) {
return [`ws:${currentWeekIso}`];
}

const weekKeys = [];
const cursor = new Date(currentWeekIso);
while (cursor.getTime() >= startWeekMs) {
weekKeys.push(`ws:${cursor.toISOString().replace('.000Z', 'Z')}`);
cursor.setUTCDate(cursor.getUTCDate() - 7);
}

return weekKeys;
}

function formatWeekRangeForDisplay(weekKey) {
const normalizedWeekKey = typeof weekKey === 'string' ? weekKey.trim() : '';
if (!normalizedWeekKey) {
return 'Unknown week';
}

if (normalizedWeekKey.startsWith('ws:')) {
return formatWeekRangeLabel(normalizedWeekKey.slice(3)) || normalizedWeekKey.slice(3);
}

const weekInfo = getWeekInfo({
week_start_utc: normalizedWeekKey.startsWith('ws:') ? normalizedWeekKey.slice(3) : null,
week_of: normalizedWeekKey.startsWith('wk:') ? normalizedWeekKey.slice(3) : null
});

return weekInfo.label || 'Unknown week';
}

function buildWeekOptions(availableWeeks) {
return availableWeeks.slice(0, MAX_STATIC_SELECT_OPTIONS).map((weekKey) => {
const weekInfo = getWeekInfo({
Expand Down Expand Up @@ -372,7 +431,11 @@ async function getBotChannels(client, teamId) {
const pageChannels = Array.isArray(response.channels) ? response.channels : [];
for (const channel of pageChannels) {
if (channel && channel.id && channel.name) {
channels.push({ id: channel.id, name: channel.name });
channels.push({
id: channel.id,
name: channel.name,
createdAtTs: Number(channel.created)
});
}
}

Expand Down Expand Up @@ -1030,7 +1093,7 @@ function buildUserSummaryBlocks({ selectedChannelName, userBuckets, selectedUser
type: 'section',
text: {
type: 'mrkdwn',
text: `:bust_in_silhouette: *${selectedUserLabel}*\n\n:hourglass_flowing_sand: No summary available yet for this user. Click "Generate Summaries for All Members" to create one.`
text: `:bust_in_silhouette: *${selectedUserLabel}*\n\n:hourglass_flowing_sand: No summary available yet for this user.\nSelect "Generate Summary" to create one, or select "Generate Summaries for All Members" to structure summaries for everyone!`
}
});
} else {
Expand Down Expand Up @@ -1078,7 +1141,7 @@ function buildUserSummaryBlocks({ selectedChannelName, userBuckets, selectedUser
return blocks;
}

function buildWeeklySummaryBlocks({ selectedChannelName, dbName, summaries, selectedWeek }) {
function buildWeeklySummaryBlocks({ selectedChannelName, dbName, summaries, selectedWeek, selectedChannelCreatedAtTs }) {
if (!selectedChannelName) {
return [
{
Expand All @@ -1091,18 +1154,6 @@ function buildWeeklySummaryBlocks({ selectedChannelName, dbName, summaries, sele
];
}

if (!summaries.length) {
return [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `No weekly summaries found yet for *#${selectedChannelName}*.`
}
}
];
}

const sortedSummaries = summaries
.slice()
.sort((left, right) => {
Expand All @@ -1111,7 +1162,7 @@ function buildWeeklySummaryBlocks({ selectedChannelName, dbName, summaries, sele
return rightDate - leftDate;
});

const availableWeeks = getAvailableWeeks(sortedSummaries);
const availableWeeks = getAvailableWeeksFromChannelCreation(selectedChannelCreatedAtTs);
const selectedWeekKey = typeof selectedWeek === 'string' ? selectedWeek : '';
const resolvedSelectedWeek = availableWeeks.includes(selectedWeekKey)
? selectedWeekKey
Expand Down Expand Up @@ -1152,19 +1203,48 @@ function buildWeeklySummaryBlocks({ selectedChannelName, dbName, summaries, sele
}
});

blocks.push({
type: 'actions',
elements: [
{
type: 'button',
text: {
type: 'plain_text',
text: 'Generate Summaries For This Week',
emoji: true
},
...(!visibleSummaries.length ? { style: 'primary' } : {}),
action_id: HOME_GENERATE_SELECTED_WEEK_SUMMARY_ACTION_ID,
value: encodeDashboardState({
channelName: selectedChannelName || '',
selectedWeek: resolvedSelectedWeek,
selectedUser: null
})
}
]
});

blocks.push({ type: 'divider' });

if (!visibleSummaries.length) {
blocks.push({
type: 'section',
text: {
type: 'mrkdwn',
text: `No weekly summaries found for *${formatWeekRangeForDisplay(resolvedSelectedWeek)}* in *#${selectedChannelName}*.`
}
});
return blocks;
}

blocks.push({
type: 'context',
elements: [
{
type: 'mrkdwn',
text: resolvedSelectedWeek == null
? `Showing all ${visibleSummaries.length} daily updates.`
: `Showing ${visibleSummaries.length} daily updates for *${(getWeekInfo({
week_start_utc: resolvedSelectedWeek.startsWith('ws:') ? resolvedSelectedWeek.slice(3) : null,
week_of: resolvedSelectedWeek.startsWith('wk:') ? resolvedSelectedWeek.slice(3) : null
}).label)}*.`
: `Showing ${visibleSummaries.length} daily updates for *${formatWeekRangeForDisplay(resolvedSelectedWeek)}*.`
}
]
});
Expand Down Expand Up @@ -1202,6 +1282,7 @@ function buildSampleHomeView({
workspaceName,
channelOptions,
selectedChannelName,
selectedChannelCreatedAtTs,
selectedWeek,
selectedUser,
selectedUserSummary,
Expand Down Expand Up @@ -1329,7 +1410,8 @@ function buildSampleHomeView({
selectedChannelName,
dbName,
summaries,
selectedWeek
selectedWeek,
selectedChannelCreatedAtTs
}));

if (activeChannels > selectableChannels) {
Expand Down Expand Up @@ -1495,6 +1577,8 @@ async function publishHomeTab({ client, userId, teamId, workspaceName, logger, a
console.log(`[publishHomeTab] Loaded channels for Home dropdown: ${channels.length} total | First 3: ${first3Channels}`);

const resolvedChannelName = selectedChannelName || (channels[0] && channels[0].name) || '';
const resolvedChannel = channels.find((channel) => channel.name === resolvedChannelName) || null;
const selectedChannelCreatedAtTs = resolvedChannel ? resolvedChannel.createdAtTs : null;
console.log(`[publishHomeTab] Selected channel: ${resolvedChannelName || '(none)'}`);

if (logger) {
Expand Down Expand Up @@ -1544,7 +1628,6 @@ async function publishHomeTab({ client, userId, teamId, workspaceName, logger, a
apiStatus,
dbName,
summaries,
availableWeeks,
summaryRecords,
messagesSummarized,
errorMessage: weeklySummaryErrorMessage
Expand All @@ -1557,6 +1640,7 @@ async function publishHomeTab({ client, userId, teamId, workspaceName, logger, a
errorMessage: userSummaryErrorMessage
} = userSummaryResult;
const errorMessage = weeklySummaryErrorMessage || userSummaryErrorMessage;
const availableWeeks = getAvailableWeeksFromChannelCreation(selectedChannelCreatedAtTs);

if (logger) {
logger.info(`[publishHomeTab] Summaries fetched - Records: ${summaryRecords}, Messages: ${messagesSummarized}, Status: ${apiStatus}`);
Expand All @@ -1569,6 +1653,7 @@ async function publishHomeTab({ client, userId, teamId, workspaceName, logger, a
workspaceName: resolvedWorkspaceName,
channelOptions,
selectedChannelName: resolvedChannelName,
selectedChannelCreatedAtTs,
selectedWeek: availableWeeks.includes(selectedWeek) ? selectedWeek : availableWeeks[0],
selectedUser: selectedUserId || selectedUser,
selectedUserSummary,
Expand Down Expand Up @@ -1631,6 +1716,7 @@ module.exports = {
HOME_CHANNEL_SELECT_ACTION_ID,
HOME_REFRESH_ACTION_ID,
HOME_SUMMARY_WEEK_SELECT_ACTION_ID,
HOME_GENERATE_SELECTED_WEEK_SUMMARY_ACTION_ID,
HOME_USER_SUMMARY_SELECT_ACTION_ID,
HOME_GENERATE_USER_SUMMARIES_ACTION_ID,
HOME_GENERATE_SINGLE_USER_SUMMARY_ACTION_ID,
Expand Down
Loading
Loading