Skip to content

Commit

Permalink
feat: finish the discourse network graph api
Browse files Browse the repository at this point in the history
  • Loading branch information
Behzad-rabiei committed Sep 18, 2024
1 parent 4925ab9 commit c7f584c
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 112 deletions.
12 changes: 6 additions & 6 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ export default {
env: envVars.NODE_ENV,
port: envVars.PORT,
mongoose: {
// serverURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}/${envVars.DB_NAME}?authSource=admin`,
// botURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`,
// dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`,
serverURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/Core?directConnection=true`,
botURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/?directConnection=true`,
dbURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/?directConnection=true`,
serverURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}/${envVars.DB_NAME}?authSource=admin`,
botURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`,
dbURL: `mongodb://${envVars.DB_USER}:${envVars.DB_PASSWORD}@${envVars.DB_HOST}:${envVars.DB_PORT}?authSource=admin`,
// serverURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/Core?directConnection=true`,
// botURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/?directConnection=true`,
// dbURL: `mongodb://tcmongo:T0g3th3rCr3wM0ng0P55@3.122.238.21:37017/?directConnection=true`,
},
redis: {
host: envVars.REDIS_HOST,
Expand Down
7 changes: 2 additions & 5 deletions src/controllers/discourse.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ const lineGraph = catchAsync(async function (req: IAuthAndPlatform, res: Respons
});

const membersInteractionsNetworkGraph = catchAsync(async function (req: IAuthAndPlatform, res: Response) {
const platform = await platformService.getPlatformById(new Types.ObjectId(req.params.platformId));
console.log(platform);

const networkGraphData = await discourseService.memberActivityService.getMembersInteractionsNetworkGraph(
platform?.id,
platform?.name as SupportedNeo4jPlatforms,
req.platform.id,
req.platform?.name as SupportedNeo4jPlatforms,
);
res.send(networkGraphData);
});
Expand Down
2 changes: 1 addition & 1 deletion src/routes/v1/discourse.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ router.post(

router.post(
'/member-activity/:platformId/members-interactions-network-graph',
// auth('admin', 'view'),
auth('admin', 'view'),
validate(discourseValidation.membersInteractionsNetworkGraph),
discourseController.membersInteractionsNetworkGraph,
);
Expand Down
208 changes: 111 additions & 97 deletions src/services/discourse/memberActivity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import parentLogger from '../../config/logger';
import { NEO4J_PLATFORM_INFO } from '../../constants/neo4j.constant';
import { SupportedNeo4jPlatforms } from '../../types/neo4j.type';
import { DatabaseManager } from '@togethercrew.dev/db';

import { ApiError } from '../../utils';
import httpStatus from 'http-status';
const logger = parentLogger.child({ module: 'DiscourseMemberActivityService' });

type networkGraphUserInformationType = {
Expand All @@ -14,13 +15,22 @@ type networkGraphUserInformationType = {
roles: [];
ngu: string;
};

function getNgu(user: any): string {
if (user.options.name !== '') {
return user.options.name;
} else {
return user.options.username;
}
}

function getUserInformationForNetworkGraph(user: any): networkGraphUserInformationType {
return {
username: user.options.username,
avatar: user.options.avatar,
joinedAt: user.joined_at,
roles: [],
ngu: user.options.name,
ngu: getNgu(user),
};
}

Expand All @@ -30,115 +40,119 @@ async function getMembersInteractionsNetworkGraph(
platformId: string,
platformName: SupportedNeo4jPlatforms,
): Promise<memberInteractionsGraphResponseType> {
const platformConnection = await DatabaseManager.getInstance().getPlatformDb(platformId);
const usersInNetworkGraph: string[] = [];
console.log(platformId, NEO4J_PLATFORM_INFO[platformName].member, NEO4J_PLATFORM_INFO[platformName].member);
// userInteraction
const usersInteractionsQuery = `
try {
const platformConnection = await DatabaseManager.getInstance().getPlatformDb(platformId);
const usersInNetworkGraph: string[] = [];
// userInteraction
const usersInteractionsQuery = `
MATCH () -[r:INTERACTED_WITH {platformId: "${platformId}"}]-()
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member})-[r:INTERACTED_WITH {platformId: "${platformId}", date: latest_date}]->(b:${NEO4J_PLATFORM_INFO[platformName].member})
RETURN a, r, b`;

console.log(usersInteractionsQuery);
const neo4jUsersInteractionsData = await Neo4j.read(usersInteractionsQuery);
const { records: neo4jUsersInteractions } = neo4jUsersInteractionsData;
const usersInteractions = neo4jUsersInteractions.map((usersInteraction) => {
// @ts-ignore
const { _fieldLookup, _fields } = usersInteraction;
const a = _fields[_fieldLookup['a']];
const r = _fields[_fieldLookup['r']];
const b = _fields[_fieldLookup['b']];

const aUserId = a?.properties?.id as string;
const rWeeklyInteraction = r?.properties?.weight as number;
const bUserId = b?.properties?.id as string;

usersInNetworkGraph.push(aUserId);
usersInNetworkGraph.push(bUserId);
const interaction = {
aUserId,
bUserId,
rWeeklyInteraction,
};

return interaction;
});
console.log('usersInteractions', usersInteractions);
// userRadius
const userRadiusQuery = `
const neo4jUsersInteractionsData = await Neo4j.read(usersInteractionsQuery);
const { records: neo4jUsersInteractions } = neo4jUsersInteractionsData;
const usersInteractions = neo4jUsersInteractions.map((usersInteraction) => {
// @ts-ignore
const { _fieldLookup, _fields } = usersInteraction;
const a = _fields[_fieldLookup['a']];
const r = _fields[_fieldLookup['r']];
const b = _fields[_fieldLookup['b']];

const aUserId = a?.properties?.id as string;
const rWeeklyInteraction = r?.properties?.weight as number;
const bUserId = b?.properties?.id as string;

usersInNetworkGraph.push(aUserId);
usersInNetworkGraph.push(bUserId);
const interaction = {
aUserId,
bUserId,
rWeeklyInteraction,
};

return interaction;
});

// userRadius
const userRadiusQuery = `
MATCH () -[r:INTERACTED_WITH {platformId: "${platformId}"}]-()
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member}) -[r:INTERACTED_WITH {date: latest_date, platformId :"${platformId}"}]-(:${NEO4J_PLATFORM_INFO[platformName].member})
WITH a, r
RETURN a.id as userId, SUM(r.weight) as radius`;
const neo4jUserRadiusData = await Neo4j.read(userRadiusQuery);
const { records: neo4jUserRadius } = neo4jUserRadiusData;
const userRadius = neo4jUserRadius.map((userRadius) => {
// @ts-ignore
const { _fieldLookup, _fields } = userRadius;
const userId = _fields[_fieldLookup['userId']] as string;
const radius = _fields[_fieldLookup['radius']] as number;

return { userId, radius };
});
// userStatus
const userStatusQuery = `
const neo4jUserRadiusData = await Neo4j.read(userRadiusQuery);
const { records: neo4jUserRadius } = neo4jUserRadiusData;
const userRadius = neo4jUserRadius.map((userRadius) => {
// @ts-ignore
const { _fieldLookup, _fields } = userRadius;
const userId = _fields[_fieldLookup['userId']] as string;
const radius = _fields[_fieldLookup['radius']] as number;

return { userId, radius };
});
// userStatus
const userStatusQuery = `
MATCH () -[r:INTERACTED_IN]-(g:${NEO4J_PLATFORM_INFO[platformName].platform} {id: "${platformId}"})
WITH max(r.date) as latest_date
MATCH (a:${NEO4J_PLATFORM_INFO[platformName].member})-[r:INTERACTED_IN {date: latest_date}]->(g:${NEO4J_PLATFORM_INFO[platformName].platform} {id: "${platformId}"})
RETURN a.id as userId, r.status as status`;
const neo4jUserStatusData = await Neo4j.read(userStatusQuery);
const { records: neo4jUserStatus } = neo4jUserStatusData;
const userStatus = neo4jUserStatus.map((userStatus) => {
// @ts-ignore
const { _fieldLookup, _fields } = userStatus;
const userId = _fields[_fieldLookup['userId']] as string;
const status = _fields[_fieldLookup['status']] as number;
const stats =
status == 0 ? NodeStats.SENDER : status == 1 ? NodeStats.RECEIVER : status == 2 ? NodeStats.BALANCED : null;

return { userId, stats };
});

console.log('usersInNetworkGraph', usersInNetworkGraph);
// usersInfo
const usersInfo = await platformConnection.models.rawmembers.find({ id: { $in: usersInNetworkGraph } });
console.log(usersInfo);

// prepare data
const response = usersInteractions.flatMap((interaction) => {
const { aUserId, bUserId, rWeeklyInteraction } = interaction;
// Radius
const aUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == aUserId);
const aUserRadius = aUserRadiusObj?.radius as number;
const bUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == bUserId);
const bUserRadius = bUserRadiusObj?.radius as number;
// Status
const aUserStatsObj = userStatus.find((userStatus) => userStatus.userId == aUserId);
const aUserStats = aUserStatsObj?.stats;
const bUserStatsObj = userStatus.find((userStatus) => userStatus.userId == bUserId);
const bUserStats = bUserStatsObj?.stats;
// userInfo
const aUser = usersInfo.find((user) => user.discordId === aUserId);
const bUser = usersInfo.find((user) => user.discordId === bUserId);
if (!aUser || !bUser) return [];

const aInfo = getUserInformationForNetworkGraph(aUser);
const bInfo = getUserInformationForNetworkGraph(bUser);

if (!aUserStats || !bUserStats) {
return [];
}

return {
from: { id: aUserId, radius: aUserRadius, stats: aUserStats, ...aInfo },
to: { id: bUserId, radius: bUserRadius, stats: bUserStats, ...bInfo },
width: rWeeklyInteraction,
};
});

return response;
const neo4jUserStatusData = await Neo4j.read(userStatusQuery);
const { records: neo4jUserStatus } = neo4jUserStatusData;
const userStatus = neo4jUserStatus.map((userStatus) => {
// @ts-ignore
const { _fieldLookup, _fields } = userStatus;
const userId = _fields[_fieldLookup['userId']] as string;
const status = _fields[_fieldLookup['status']] as number;
const stats =
status == 0 ? NodeStats.SENDER : status == 1 ? NodeStats.RECEIVER : status == 2 ? NodeStats.BALANCED : null;

return { userId, stats };
});

const usersInfo = await platformConnection.db
.collection('rawmembers')
.find({ id: { $in: usersInNetworkGraph } })
.toArray();

// prepare data
const response = usersInteractions.flatMap((interaction) => {
const { aUserId, bUserId, rWeeklyInteraction } = interaction;
// Radius
const aUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == aUserId);
const aUserRadius = aUserRadiusObj?.radius as number;
const bUserRadiusObj = userRadius.find((userRadius) => userRadius.userId == bUserId);
const bUserRadius = bUserRadiusObj?.radius as number;
// Status
const aUserStatsObj = userStatus.find((userStatus) => userStatus.userId == aUserId);
const aUserStats = aUserStatsObj?.stats;
const bUserStatsObj = userStatus.find((userStatus) => userStatus.userId == bUserId);
const bUserStats = bUserStatsObj?.stats;

// userInfo
const aUser = usersInfo.find((user) => user.id === aUserId);
const bUser = usersInfo.find((user) => user.id === bUserId);
if (!aUser || !bUser) return [];

const aInfo = getUserInformationForNetworkGraph(aUser);
const bInfo = getUserInformationForNetworkGraph(bUser);

if (!aUserStats || !bUserStats) {
return [];
}

return {
from: { id: aUserId, radius: aUserRadius, stats: aUserStats, ...aInfo },
to: { id: bUserId, radius: bUserRadius, stats: bUserStats, ...bInfo },
width: rWeeklyInteraction,
};
});

return response;
} catch (error) {
logger.error(error, 'Failed to get discourse members interaction network graph');
throw new ApiError(httpStatus.INTERNAL_SERVER_ERROR, 'Failed to get discourse members interaction network graph');
}
}

export default {
Expand Down
3 changes: 0 additions & 3 deletions src/services/memberActivity.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -910,23 +910,20 @@ function getActivityComposition(guildMember: IGuildMember, memberActivity: any,
}

type networkGraphUserInformationType = {
discordId: Snowflake;
username: string;
avatar: string | null | undefined;
joinedAt: Date | null;
roles: any;
ngu: string;
};
function getUserInformationForNetworkGraph(user: IGuildMember, guildRoles: IRole[]): networkGraphUserInformationType {
const discordId = user?.discordId;
const fullUsername = guildMemberService.getUsername(user);
const avatar = user?.avatar;
const joinedAt = user?.joinedAt;
const roles = roleService.getRolesForGuildMember(user, guildRoles);
const ngu = guildMemberService.getNgu(user);

return {
discordId,
username: fullUsername,
avatar,
joinedAt,
Expand Down

0 comments on commit c7f584c

Please sign in to comment.