-
Notifications
You must be signed in to change notification settings - Fork 728
Final tweaks to member enrichment #2704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 31 commits
c50ab0d
e66da1e
eace62d
8a4bb54
5473938
1175d84
ca1f8d4
8faed6c
23e6cdb
00c7dc2
21c76ba
c0abfa3
763480f
a36056c
6c19087
6c4750d
e8f7618
d59ac6b
31ee17b
98105dd
d4e7bb0
73add51
ab189a6
cf6f43a
d8b9762
4c24a9b
3b5c48a
d8a63b4
9fac18e
64aee6c
a859861
5e2fcb0
22121ea
52549a0
3a52818
82aed4f
71a297d
b8d5d2b
7318e16
47f9263
1606a84
c87f732
6ae3a91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| import axios from 'axios' | ||
| import lodash from 'lodash' | ||
|
|
||
| import { websiteNormalizer } from '@crowd/common' | ||
| import { replaceDoubleQuotes, websiteNormalizer } from '@crowd/common' | ||
| import { Logger, LoggerBase } from '@crowd/logging' | ||
| import { | ||
| MemberAttributeName, | ||
|
|
@@ -261,51 +261,80 @@ export default class EnrichmentServiceProgAI extends LoggerBase implements IEnri | |
| ): IMemberEnrichmentDataNormalized { | ||
| if (data.work_experiences) { | ||
| for (const workExperience of data.work_experiences) { | ||
| const identities = [] | ||
|
|
||
| if (workExperience.companyUrl) { | ||
| const normalizedDomain = websiteNormalizer(workExperience.companyUrl, false) | ||
| if ( | ||
| workExperience.company !== null || | ||
| workExperience.companyUrl !== null || | ||
| workExperience.companyLinkedInUrl !== null | ||
| ) { | ||
| const identities = [] | ||
| let hasPrimaryDomainIdentity = false | ||
|
|
||
| if (workExperience.companyUrl) { | ||
| const normalizedDomain = websiteNormalizer(workExperience.companyUrl, false) | ||
|
|
||
| // sometimes companyUrl is a github link, we don't want to add it as a primary domain | ||
| if ( | ||
| normalizedDomain && | ||
| !workExperience.companyUrl.toLowerCase().includes('github') && | ||
| !(workExperience.company || '').toLowerCase().includes('github') | ||
| ) { | ||
| identities.push({ | ||
| platform: PlatformType.LINKEDIN, | ||
| value: normalizedDomain, | ||
| type: OrganizationIdentityType.PRIMARY_DOMAIN, | ||
| verified: true, | ||
| }) | ||
| hasPrimaryDomainIdentity = true | ||
| } | ||
| } | ||
|
|
||
| // sometimes companyUrl is a github link, we don't want to add it as a primary domain | ||
| if ( | ||
| normalizedDomain && | ||
| !workExperience.companyUrl.toLowerCase().includes('github') && | ||
| !workExperience.company.toLowerCase().includes('github') | ||
| workExperience.companyLinkedInUrl && | ||
| this.getLinkedInProfileHandle(workExperience.companyLinkedInUrl) | ||
| ) { | ||
| identities.push({ | ||
| platform: PlatformType.LINKEDIN, | ||
| value: normalizedDomain, | ||
| type: OrganizationIdentityType.PRIMARY_DOMAIN, | ||
| verified: true, | ||
| value: this.getLinkedInProfileHandle(workExperience.companyLinkedInUrl), | ||
| type: OrganizationIdentityType.USERNAME, | ||
| verified: !hasPrimaryDomainIdentity, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| if (workExperience.companyLinkedInUrl) { | ||
| identities.push({ | ||
| platform: PlatformType.LINKEDIN, | ||
| value: `company:${workExperience.companyLinkedInUrl.split('/').pop()}`, | ||
| type: OrganizationIdentityType.USERNAME, | ||
| verified: true, | ||
| normalized.memberOrganizations.push({ | ||
| name: replaceDoubleQuotes(workExperience.company), | ||
| source: OrganizationSource.ENRICHMENT_PROGAI, | ||
| identities, | ||
| title: replaceDoubleQuotes(workExperience.title), | ||
| startDate: workExperience.startDate | ||
| ? workExperience.startDate.replace('Z', '+00:00') | ||
| : null, | ||
| endDate: workExperience.endDate ? workExperience.endDate.replace('Z', '+00:00') : null, | ||
|
Comment on lines
+308
to
+311
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Based on the code analysis, I can now provide the final response: Add date format validation for work experience dates The code assumes dates from the ProgAI API are in a format ending with 'Z' (UTC timezone marker), but there's no validation to ensure this. The type definition shows that
🔗 Analysis chainValidate date string manipulation The date string manipulation assumes a specific format. Consider adding validation or using a date manipulation library to handle different date formats safely. Let me check the input date format and validation in the codebase. Let me check if there's any date format validation or parsing before this transformation. Let me check the entire file content to understand the context better. Let me check the type definitions for the work experience data structure. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check for potential date format issues in the codebase
rg -A 2 "replace\('Z', '\+00:00'\)"
Length of output: 608 Script: #!/bin/bash
# Check the workExperience type definition and any date validation
ast-grep --pattern 'interface $_WorkExperience {
$$$
startDate$_
$$$
endDate$_
$$$
}'
# Check for any date validation or parsing logic
rg -B 2 -A 2 "workExperience\.(startDate|endDate)"
Length of output: 1053 Script: #!/bin/bash
# Check for date validation or parsing in the file
rg -B 5 -A 5 "function.*workExperience" services/apps/members_enrichment_worker/src/sources/progai/service.ts
# Check if there's any date validation utility being used
rg -l "import.*date|moment|luxon|dayjs" services/apps/members_enrichment_worker/src/sources/progai/service.ts
# Look for any date format validation
ast-grep --pattern 'function $_(workExperience$_) {
$$$
}'
Length of output: 286 Script: #!/bin/bash
# Get the content of the service file
cat services/apps/members_enrichment_worker/src/sources/progai/service.ts
# Check for any date-related types or interfaces
ast-grep --pattern 'type $_ = {
$$$
date$_
$$$
}'
# Check for any date validation functions
rg -l "validateDate|isValidDate|parseDate" services/apps/members_enrichment_worker/src/sources/progai/service.ts
Length of output: 11440 Script: #!/bin/bash
# Check the types file for work experience interface
cat services/apps/members_enrichment_worker/src/sources/progai/types.ts
# Check if there's any date format validation in the utils
rg -l "date" services/apps/members_enrichment_worker/src/utils/
# Look for any date-related imports in the types file
rg "import.*date" services/apps/members_enrichment_worker/src/sources/progai/types.ts
Length of output: 2202 |
||
| }) | ||
| } | ||
|
|
||
| normalized.memberOrganizations.push({ | ||
| name: workExperience.company, | ||
| source: OrganizationSource.ENRICHMENT_PROGAI, | ||
| identities, | ||
| title: workExperience.title, | ||
| startDate: workExperience.startDate | ||
| ? workExperience.startDate.replace('Z', '+00:00') | ||
| : null, | ||
| endDate: workExperience.endDate ? workExperience.endDate.replace('Z', '+00:00') : null, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| return normalized | ||
| } | ||
|
|
||
| private getLinkedInProfileHandle(url: string): string | null { | ||
| let regex = /company\/([^/]+)/ | ||
| let match = url.match(regex) | ||
|
|
||
| if (match) { | ||
| return `company:${match[1]}` | ||
| } | ||
|
|
||
| regex = /school\/([^/]+)/ | ||
| match = url.match(regex) | ||
|
|
||
| if (match) { | ||
| return `school:${match[1]}` | ||
| } | ||
|
|
||
| return null | ||
| } | ||
|
|
||
| async getDataUsingGitHubHandle(githubUsername: string): Promise<IMemberEnrichmentDataProgAI> { | ||
| const url = `${process.env['CROWD_ENRICHMENT_PROGAI_URL']}/get_profile` | ||
| const config = { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,13 +128,19 @@ export default class EnrichmentServiceSerpApi extends LoggerBase implements IEnr | |
| platform: PlatformType.LINKEDIN, | ||
| type: MemberIdentityType.USERNAME, | ||
| verified: false, | ||
| value: this.normalizeLinkedUrl(data.linkedinUrl).split('/').pop(), | ||
| value: this.getLinkedInProfileHandle(this.normalizeLinkedUrl(data.linkedinUrl)), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add error handling for handle extraction The normalize method should handle cases where getLinkedInProfileHandle returns null. - value: this.getLinkedInProfileHandle(this.normalizeLinkedUrl(data.linkedinUrl)),
+ value: this.getLinkedInProfileHandle(this.normalizeLinkedUrl(data.linkedinUrl)) ??
+ this.log.warn('Failed to extract LinkedIn handle', { url: data.linkedinUrl }) || null,
|
||
| }, | ||
| ], | ||
| } | ||
| return normalized | ||
| } | ||
|
|
||
| private getLinkedInProfileHandle(url: string): string | null { | ||
| const regex = /in\/([^/]+)/ | ||
| const match = url.match(regex) | ||
| return match ? match[1] : null | ||
| } | ||
|
|
||
| private normalizeLinkedUrl(url: string): string { | ||
| try { | ||
| const parsedUrl = new URL(url) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling for work experience matching.
The work experience matching logic needs better error handling:
Add error handling:
const match = orderedNewVersion.find( (e) => e.title === current.jobTitle && - e.identities && - e.identities.some((e) => e.organizationId === current.orgId), + Array.isArray(e.identities) && + e.identities.some((identity) => { + if (!identity || !identity.organizationId) { + svc.log.warn({ experience: e }, 'Invalid identity data in work experience') + return false + } + return identity.organizationId === current.orgId + }), ) + svc.log.debug( + { current, match }, + match ? 'Found matching work experience' : 'No matching work experience found' + )📝 Committable suggestion