diff --git a/app/analytics/UserAnalytics.py b/app/analytics/UserAnalytics.py index 7f0b60a..74a9e97 100644 --- a/app/analytics/UserAnalytics.py +++ b/app/analytics/UserAnalytics.py @@ -11,6 +11,11 @@ def __init__(self): self.assets_path = os.environ.get("ASSETS_PATH") self.user_file_path = os.environ.get("USER_FILE_PATH") self.geo_lite_db_path = os.environ.get("GEOLITE_DB_PATH") + + # TODO: come back and fix this + # if self.geo_lite_db_path is None: + # raise Exception("GEOLITE_DB_PATH environment variable is not set.") + if self.user_file_path is None: raise Exception("USER_FILE_PATH environment variable is not set.") @@ -103,6 +108,27 @@ def profile_preference_selections(self): preference_values = [preference_data[field] for field in preference_fields if field in preference_data] return profile_values, preference_values + + def count_stringeny_attributes(self): + preferences = self.get_preferences_data() + + dealbreaker_cats = { + "physical": ["age_dealbreaker", "height_dealbreaker"], + "identity": ["ethnicity_dealbreaker", "religion_dealbreaker", "politics_dealbreaker"], + "lifestyle": ["smoking_dealbreaker", "drinking_dealbreaker", "marijuana_dealbreaker", "drugs_dealbreaker"], + "career": ["education_attained_dealbreaker"], + "future_plans": ["children_dealbreaker", "family_plans_dealbreaker"] + } + # initialize counters + display_counts = defaultdict(lambda: {"true": 0, "false": 0}) + + for category, fields in dealbreaker_cats.items(): + for field in fields: + if field in preferences: + display_value = preferences[field] + display_counts[category]["true" if display_value else "false"] += 1 + return dict(display_counts) + def count_displayed_attributes(self): profile_data = self.get_profile_data() diff --git a/app/pages/UserPage.py b/app/pages/UserPage.py index ef917ce..c705529 100644 --- a/app/pages/UserPage.py +++ b/app/pages/UserPage.py @@ -6,6 +6,63 @@ from analytics.UserAnalytics import UserAnalytics +BLUE = "#3BAAC4" +REDISH = "#C4553B" + +def stringency_vs_flexibility(): + dealbreaker_counts = UserAnalytics().count_stringeny_attributes() + + category_labels = { + "physical": "Age & Height", + "identity": "Identity & Demographics", + "career": "Work & Education", + "lifestyle": "Lifestyle & Habits", + "future_plans": "Dating Preferences & Intentions" + } + + # extract the category keys and T/F counts to pass to the graphs + cat_keys = [category_labels[cat] for cat in dealbreaker_counts.keys()] + true_counts = [dealbreaker_counts[cat]['true'] for cat in dealbreaker_counts] + false_counts = [dealbreaker_counts[cat]['false'] for cat in dealbreaker_counts] + + fig = go.Figure() + + fig.add_trace(go.Bar( + x=cat_keys, + y=true_counts, + name="Dealbreaker (True)", + marker_color=REDISH + )) + + fig.add_trace(go.Bar( + x=cat_keys, + y=false_counts, + name="Open (False)", + marker_color=BLUE + )) + + fig.update_layout( + title="Dating Preferences: Dealbreakers vs. Open Choices", + xaxis_title="Preference Category", + yaxis_title="Number of Dealbreakers", + barmode='group', + template="plotly_white" + ) + return dmc.Card( + children=[ + dmc.Space(h=10), + dmc.Text("How strict or open is this person in their dating preferences?", weight=700, size="xl"), + dmc.Space(h=10), + dmc.Text("This bar chart compares the number of 'dealbreakers' versus 'open' preferences across different dating categories, highlighting which factors are most important or flexible in the user's online dating criteria.", size="md"), + dmc.Space(h=10), + dcc.Graph(figure=fig) + ], + withBorder=True, + shadow="sm", + radius="md", + style={"height": "520px"}, + ) + def geolocation(): df = UserAnalytics().collect_location_from_ip() fig = px.scatter_geo( @@ -93,14 +150,14 @@ def disclosure_vs_privacy(): x=cat_keys, y=true_counts, name="Displayed (True)", - marker_color='blue' + marker_color=BLUE )) fig.add_trace(go.Bar( x=cat_keys, y=false_counts, name="Not Displayed (False)", - marker_color='red' + marker_color=REDISH )) fig.update_layout( @@ -237,5 +294,6 @@ def create_user_summary_card(): disclosure_vs_privacy(), potential_misalignments(), geolocation(), + stringency_vs_flexibility(), dmc.Space(h=50) ]) diff --git a/tests/analytics/test_UserAnalytics.py b/tests/analytics/test_UserAnalytics.py index 15e0a18..df23e2b 100644 --- a/tests/analytics/test_UserAnalytics.py +++ b/tests/analytics/test_UserAnalytics.py @@ -83,6 +83,8 @@ "distance_miles_max": 50, "age_min": 98, "age_max": 99, + "age_dealbreaker": true, + "height_dealbreaker": false, "ethnicity_preference": "[Open to All]", "ethnicity_dealbreaker": false, "religion_preference": "[Open to All]", @@ -115,6 +117,8 @@ } ''' COUNT_DISPLAYED_ATTRIB_OUTPUT = {'identity': {'true': 2, 'false': 4}, 'lifestyle': {'true': 2, 'false': 3}, 'career': {'true': 2, 'false': 1}, 'future_plans': {'true': 1, 'false': 3}} +STRINGENCY_COUNTS = {'physical': {'true': 1, 'false': 1}, 'identity': {'true': 0, 'false': 3}, 'lifestyle': {'true': 0, 'false': 4}, 'career': {'true': 0, 'false': 1}, 'future_plans': {'true': 0, 'false': 2}} +GEOLITE_DB_PATH = 'data/db_path.mmdb' ######################################################################################### # pytest fixtures @@ -122,6 +126,7 @@ @pytest.fixture def user_analytics(monkeypatch): monkeypatch.setenv("USER_FILE_PATH", USER_FILE_PATH) + monkeypatch.setenv("GEOLITE_DB_PATH", GEOLITE_DB_PATH) with patch("builtins.open", mock_open(read_data=USER_DATA)) as mock_file, \ patch("json.load", return_value=json.loads(USER_DATA)) as mock_json_load: @@ -219,4 +224,11 @@ def test_profile_preference_selections(user_analytics): # TODO: this needs to be mocked out and better tests added # def test_collect_location_from_ip(user_analytics): # result = user_analytics.collect_location_from_ip() -# assert result is not None \ No newline at end of file +# assert result is not None + +def test_count_stringeny_attributes(user_analytics): + results = user_analytics.count_stringeny_attributes() + print(results) + + assert list(results.keys()) == ['physical', 'identity', 'lifestyle', 'career', 'future_plans'] + assert results == STRINGENCY_COUNTS \ No newline at end of file