瀏覽代碼

:construction: new filter service to preprocess a users match pool before scoring

tags/0.0.3^2
juancarbajal98 3 年之前
父節點
當前提交
0f3212ab87

+ 24
- 0
backend/lib/services/filter.js 查看文件

1
+const zipcoder = require ('./profile/zipcoder')
2
+
3
+const byProfileType = (profileList, userProfile) => {
4
+    const isUserOpposite = userProfile.is_poster == 1 ? 0 : 1
5
+    return profileList.filter(profile => (profile.user.is_poster == isUserOpposite))
6
+}
7
+const byNullZip = (profileList) => {
8
+    return profileList.filter(profile => {
9
+        return zipcoder.getZipCodeFromProfile(profile) ? true : false
10
+    })
11
+}
12
+const byMaxDistance = (profileList, max) => {
13
+    return profileList.filter(profile => {
14
+        const profileDistance = Math.floor(parseFloat(profile.distance) * 100)
15
+        const adjustedMaxDistance = Math.floor(parseFloat(max) * 100)
16
+        return profileDistance <= adjustedMaxDistance
17
+    })
18
+}
19
+
20
+module.exports = {
21
+    byProfileType,
22
+    byNullZip,
23
+    byMaxDistance,
24
+}

+ 32
- 35
backend/lib/services/profile/index.js 查看文件

4
 const profiler = require('./profiler')
4
 const profiler = require('./profiler')
5
 const scoring = require('./scorer')
5
 const scoring = require('./scorer')
6
 const zipcoder = require('./zipcoder')
6
 const zipcoder = require('./zipcoder')
7
+<<<<<<< HEAD
7
 const { response } = require('@hapi/hapi/lib/validation')
8
 const { response } = require('@hapi/hapi/lib/validation')
9
+=======
10
+const tagger = require('./tagger')
11
+const filter = require('./filter')
12
+>>>>>>> 154d171 (:construction: new filter service to preprocess a users match pool before scoring)
8
 
13
 
9
 module.exports = class ProfileService extends Schmervice.Service {
14
 module.exports = class ProfileService extends Schmervice.Service {
10
     constructor(...args) {
15
     constructor(...args) {
253
             .findOne('profile_id', profileId)
258
             .findOne('profile_id', profileId)
254
             .withGraphFetched('responses')
259
             .withGraphFetched('responses')
255
             .withGraphFetched('user')
260
             .withGraphFetched('user')
256
-
257
-        // Move unneeded responses
261
+        
258
         const userZip = zipcoder.getZipCodeFromProfile(userProfile)
262
         const userZip = zipcoder.getZipCodeFromProfile(userProfile)
259
 
263
 
260
-        // Find all Profiles that are NOT of our userProfile.type
261
-        // ie. If userProfile.type == seeker, then find: poster
262
-        let profileIdsOfOppositeType = await Profile.query()
264
+        // preprocess potential match pool with filter service methods
265
+        let matchPool = await Profile.query()
263
             .withGraphFetched('responses')
266
             .withGraphFetched('responses')
264
             .withGraphFetched('user')
267
             .withGraphFetched('user')
265
-        // TODO: Let Objection optimize this
266
-        const isPosterOpposite = userProfile.user.is_poster == 1 ? 0 : 1
267
-        profileIdsOfOppositeType = profileIdsOfOppositeType
268
-            .filter(profile => {
269
-                return profile.user.is_poster == isPosterOpposite
270
-            })
271
-            .filter(profile => {
272
-                // Only include profiles that included zipcode response
273
-                return zipcoder.getZipCodeFromProfile(profile) ? true : false
274
-            })
275
 
268
 
276
-        const profilePlusDistance = await Promise.all(
277
-            profileIdsOfOppositeType.map(async profile => {
278
-                const targetZip = zipcoder.getZipCodeFromProfile(profile)
269
+        matchPool = filter.byProfileType(matchPool, userProfile.user)
270
+        matchPool = filter.byNullZip(matchPool)
271
+        // attach distance to pool profiles for max distance filter 
272
+        matchPool = await this.calcProfileDistances(matchPool, distanceUnit, userZip)
273
+        // filter with matchPool profiles that have distance
274
+        matchPool = filter.byMaxDistance(matchPool, maxDistance)
275
+
276
+        const scoredProfilesWithDistance = scoring.scoreAll(
277
+            matchPool,
278
+            userProfile,
279
+            this.scoreLookup,
280
+        )
281
+        // Order by score
282
+        return scoredProfilesWithDistance.sort(
283
+            (a, b) => b.score.total - a.score.total,
284
+        )
285
+    }
279
 
286
 
287
+    async calcProfileDistances(matchPool, distanceUnit, userZip){
288
+        await Promise.all(
289
+            matchPool.map(async profile => {
290
+                const targetZip = zipcoder.getZipCodeFromProfile(profile)
280
                 if (!userZip || !targetZip)
291
                 if (!userZip || !targetZip)
281
                     return { ...profile, distance: [9999, distanceUnit] }
292
                     return { ...profile, distance: [9999, distanceUnit] }
282
 
293
 
289
                     ...profile,
300
                     ...profile,
290
                     distance: [distance.toFixed(2), distanceUnit],
301
                     distance: [distance.toFixed(2), distanceUnit],
291
                 }
302
                 }
292
-            }),
293
-        )
294
-
295
-        const distanceFilteredProfiles = zipcoder.filterByDistance(
296
-            profilePlusDistance,
297
-            maxDistance,
298
-        )
299
-        const scoredProfilesWithDistance = scoring.scoreAll(
300
-            distanceFilteredProfiles,
301
-            userProfile,
302
-            this.scoreLookup,
303
-        )
304
-        // Order by score
305
-        return scoredProfilesWithDistance.sort(
306
-            (a, b) => b.score.total - a.score.total,
303
+            })
307
         )
304
         )
308
     }
305
     }
309
 
306
 
351
         await this._setTagLookup()
348
         await this._setTagLookup()
352
         let associations = groupingId
349
         let associations = groupingId
353
             ? await TagAssociation.query()
350
             ? await TagAssociation.query()
354
-                  .where('grouping_id', groupingId)
355
-                  .andWhere('profile_id', profileId)
351
+                .where('grouping_id', groupingId)
352
+                .andWhere('profile_id', profileId)
356
             : await TagAssociation.query().andWhere('profile_id', profileId)
353
             : await TagAssociation.query().andWhere('profile_id', profileId)
357
         return associations
354
         return associations
358
             .map(assoc => ({
355
             .map(assoc => ({

+ 0
- 9
backend/lib/services/profile/zipcoder.js 查看文件

11
     return zipRes.val
11
     return zipRes.val
12
 }
12
 }
13
 
13
 
14
-const filterByDistance = (profileList, max) => {
15
-    return profileList.filter(profile => {
16
-        const profileDistance = Math.floor(parseFloat(profile.distance) * 100)
17
-        const adjustedMaxDistance = Math.floor(parseFloat(max) * 100)
18
-        return profileDistance <= adjustedMaxDistance
19
-    })
20
-}
21
-
22
 module.exports = {
14
 module.exports = {
23
     getZipCodeFromProfile,
15
     getZipCodeFromProfile,
24
-    filterByDistance,
25
 }
16
 }

+ 0
- 51
frontend/src/views/OnboardingView.vue 查看文件

2
 main.view--onboarding
2
 main.view--onboarding
3
     article(
3
     article(
4
         style='display: flex; flex-direction: column; align-items: center'
4
         style='display: flex; flex-direction: column; align-items: center'
5
-<<<<<<< HEAD
6
         v-if='currentStep !== survey.steps.length'
5
         v-if='currentStep !== survey.steps.length'
7
     )
6
     )
8
         .answers(v-for='(value, key) in answered')
7
         .answers(v-for='(value, key) in answered')
32
 
31
 
33
     article(v-else)
32
     article(v-else)
34
         SurveyCompleteView(:answers='answered' :surveySteps='survey.steps')
33
         SurveyCompleteView(:answers='answered' :surveySteps='survey.steps')
35
-=======
36
-        v-if='survey'
37
-    )
38
-        .step(v-for='(step, i) in survey.steps')
39
-            component(
40
-                :aspect-questions='step.component == "Aspects" ? survey.aspectQuestions : null'
41
-                :is='step.component'
42
-                :question='step'
43
-                @handle-submit='onSubmit'
44
-                @update-answers='updateAnswers'
45
-                v-if='step && currentStep == i'
46
-            )
47
->>>>>>> b0c2120 (another pre-release (#53))
48
 </template>
34
 </template>
49
 
35
 
50
 <script>
36
 <script>
51
 import { Authenticator } from '../services/auth.service.js'
37
 import { Authenticator } from '../services/auth.service.js'
52
 import { surveyFactory } from '@/utils'
38
 import { surveyFactory } from '@/utils'
53
-<<<<<<< HEAD
54
 import stepViews from '@/components/onboarding'
39
 import stepViews from '@/components/onboarding'
55
 import SurveyCompleteView from './SurveyCompleteView.vue'
40
 import SurveyCompleteView from './SurveyCompleteView.vue'
56
 let hashedAccessToken = null
41
 let hashedAccessToken = null
57
 let currentProfileId = null
42
 let currentProfileId = null
58
-=======
59
-import { allSteps } from '@/utils/lang'
60
-import stepViews from '@/components/onboarding'
61
->>>>>>> b0c2120 (another pre-release (#53))
62
 
43
 
63
 // import savesurveybyprfileid - call it on submit
44
 // import savesurveybyprfileid - call it on submit
64
 // paginate to save every steps answers
45
 // paginate to save every steps answers
66
     name: 'OnboardingView',
47
     name: 'OnboardingView',
67
     components: {
48
     components: {
68
         ...stepViews,
49
         ...stepViews,
69
-<<<<<<< HEAD
70
         SurveyCompleteView,
50
         SurveyCompleteView,
71
     },
51
     },
72
     data: () => ({
52
     data: () => ({
93
             this.goToStep(0)
73
             this.goToStep(0)
94
         }
74
         }
95
     },
75
     },
96
-=======
97
-    },
98
-    data: () => ({
99
-        answered: {},
100
-        aspectQuestions: [],
101
-        currentStep: 0,
102
-        survey: null,
103
-    }),
104
-    async created() {
105
-        this.survey = await surveyFactory.createSurvey(allSteps['usa'])
106
-    },
107
->>>>>>> b0c2120 (another pre-release (#53))
108
     methods: {
76
     methods: {
109
         onSubmit() {
77
         onSubmit() {
110
             console.log(JSON.stringify(this.answered))
78
             console.log(JSON.stringify(this.answered))
111
         },
79
         },
112
-<<<<<<< HEAD
113
         async goToStep(num) {
80
         async goToStep(num) {
114
             this.currentStep = num
81
             this.currentStep = num
115
         },
82
         },
175
             } else {
142
             } else {
176
                 this.goToStep(this.currentStep + 1)
143
                 this.goToStep(this.currentStep + 1)
177
             }
144
             }
178
-=======
179
-        goToStep(num) {
180
-            this.currentStep = num
181
-        },
182
-        updateAnswers(payload) {
183
-            // null payload is passed on splash page
184
-            if (payload) {
185
-                const k = payload.question.response_key_prompt
186
-                this.answered[k] = payload.answer
187
-                console.log(`${k}:`, this.answered[k])
188
-                console.log(`Updated answers: ${JSON.stringify(this.answered)}`)
189
-                if (k === 'aspects') return
190
-            }
191
-            this.goToStep(this.currentStep + 1)
192
->>>>>>> b0c2120 (another pre-release (#53))
193
         },
145
         },
194
     },
146
     },
195
 }
147
 }
207
     article
159
     article
208
         height: 100vh
160
         height: 100vh
209
 
161
 
210
-<<<<<<< HEAD
211
     .answers
162
     .answers
212
         text-align: center
163
         text-align: center
213
 
164
 
214
-=======
215
->>>>>>> b0c2120 (another pre-release (#53))
216
     .w-button
165
     .w-button
217
             display: flex
166
             display: flex
218
             width: 315px
167
             width: 315px

Loading…
取消
儲存