From 0d2b72af1edf236f4d511178762f19652a893df3 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 17 Apr 2024 16:41:05 +0000 Subject: [PATCH 01/16] feat: Add site data and menu loading functionality This commit includes the creation of new functions that will support loading essential initial site data and site menu items. It also includes the addition of extra verification in the handling section to ensure community_id and subdomain are provided. Various utilities to fetch viable menu items and feature flags for a community were added. --- src/api/constants.py | 6 +++ src/api/handlers/misc.py | 38 +++++++++++++ src/api/services/misc.py | 17 ++++++ src/api/store/misc.py | 96 ++++++++++++++++++++++++++++++++- src/api/utils/api_utils.py | 106 ++++++++++++++++++++++++++++++++++++- 5 files changed, 260 insertions(+), 3 deletions(-) diff --git a/src/api/constants.py b/src/api/constants.py index acd12799a..b7c565d9a 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -37,3 +37,9 @@ COMMUNITY_NOTIFICATION_TYPES = [USER_EVENTS_NUDGES_FF] +PAGE_SETUP_ESSENTIALS = { + "homepage":{ + + } +} + diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index 8ed1ecb19..8fdd2549d 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -32,6 +32,8 @@ def registerRoutes(self) -> None: self.add("/settings.list", self.fetch_available_preferences) self.add("/what.happened", self.fetch_footages) self.add("/actions.report", self.actions_report) + self.add("/site.load", self.load_essential_initial_site_data) + self.add("/menu.load", self.load_menu_items) @admins_only def fetch_footages(self, request): @@ -157,3 +159,39 @@ def authenticateFrontendInTestMode(self, request): "token", value=token, max_age=24 * 60 * 60, samesite="Strict" ) return response + + + def load_essential_initial_site_data(self, request): + context: Context = request.context + args: dict = context.args + + self.validator.expect("community_id", is_required=False) + self.validator.expect("subdomain", is_required=False) + self.validator.expect("user_id", is_required=False) + self.validator.expect("page", str, is_required=True) + + args, err = self.validator.verify(args, strict=True) + if err: + return MassenergizeResponse(error=err) + + data, err = self.service.load_essential_initial_site_data(context, args) + if err: + return MassenergizeResponse(error=err) + return MassenergizeResponse(data=data) + + + def load_menu_items(self, request): + context: Context = request.context + args: dict = context.args + + self.validator.expect("community_id", is_required=False) + self.validator.expect("subdomain", is_required=False) + + args, err = self.validator.verify(args, strict=True) + if err: + return MassenergizeResponse(error=err) + + data, err = self.service.load_menu_items(context, args) + if err: + return err + return MassenergizeResponse(data=data) diff --git a/src/api/services/misc.py b/src/api/services/misc.py index f1fa894f6..bf61f339f 100644 --- a/src/api/services/misc.py +++ b/src/api/services/misc.py @@ -141,3 +141,20 @@ def authenticateFrontendInTestMode(self, args): return None, CustomMassenergizeError(str(err)) client = Client() return signinAs(client, user), None + + + def load_essential_initial_site_data(self,context, args): + res, err = self.store.load_essential_initial_site_data(context,args) + if err: + return None, err + + return res, None + + def load_menu_items(self,context, args): + res, err = self.store.load_menu_items(context,args) + if err: + return None, err + + return res, None + + diff --git a/src/api/store/misc.py b/src/api/store/misc.py index 81088471e..acb9aa82d 100644 --- a/src/api/store/misc.py +++ b/src/api/store/misc.py @@ -1,3 +1,4 @@ +from _main_.utils.common import serialize_all from _main_.utils.footage.spy import Spy from api.tests.common import createUsers from database.models import ( @@ -28,10 +29,12 @@ from _main_.utils.context import Context from database.utils.common import json_loader from database.models import CarbonEquivalency -from .utils import find_reu_community, split_location_string, check_location +from .utils import find_reu_community, get_community, split_location_string, check_location from sentry_sdk import capture_message from typing import Tuple +from ..utils.api_utils import get_enabled_feature_flags_for_community, get_viable_menu_items + class MiscellaneousStore: def __init__(self): @@ -492,3 +495,94 @@ def list_commonly_used_icons(self): sorted_keys = sorted(common_icons, key=common_icons.get, reverse=True) for key in sorted_keys: print(str(key) + ": " + str(common_icons[key])) + + + def load_essential_initial_site_data(self, context, args): + try: + page = args.get("page", None) + subdonain = args.get("subdomain", None) + community_id = args.get("community_id", None) + user_id = args.get("user_id", None) + + if not subdonain and not community_id: + return None, CustomMassenergizeError("No community or subdomain provided") + + if not page: + return None, CustomMassenergizeError("No page provided") + + community, _ = get_community(community_id=community_id, subdomain=subdonain) + if not community: + return None, CustomMassenergizeError("Community not found") + + menu = get_viable_menu_items(community) + + + data = {} + + if page == "homepage": + pass + data["page_settings"] = HomePageSettings.objects.filter(community=community).first().simple_json() + data["menu"] = menu + # get feature flags + data["feature_flags"] = serialize_all(get_enabled_feature_flags_for_community(community)) + # get impact page settings + elif page == "actions": + pass + # get action page settings + # get all tag collections + # get all actions + elif page == "events": + pass + # get event page settings + # get all events + # get all tag collections + # get event exceptions for the community + elif page == "vendors": + pass + # get vendor page settings + # get all vendors + # get all tag collections + elif page == "about": + pass + # get about us page settings + # get donate page settings + elif page == "testimonials": + pass + # get testimonials page settings + # get all testimonials + # get all tag collections + + elif page == "one_vendor": + pass + # get vendor info + # get all testimonials for the vendor + + elif page == "one_action": + pass + # get action info + + return data, None + except Exception as e: + return None, CustomMassenergizeError(e) + + + + def load_menu_items(self, context, args): + try: + page = args.get("page", None) + subdonain = args.get("subdomain", None) + community_id = args.get("community_id", None) + user_id = args.get("user_id", None) + + if not subdonain and not community_id: + return None, CustomMassenergizeError("No community or subdomain provided") + + community, _ = get_community(community_id=community_id, subdomain=subdonain) + if not community: + return None, CustomMassenergizeError("Community not found") + + menu = get_viable_menu_items(community) + + return menu, None + except Exception as e: + return None, CustomMassenergizeError(e) diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index 14e7500e4..a7ddcac79 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -1,6 +1,13 @@ +from datetime import datetime from math import atan2, cos, radians, sin, sqrt -from database.models import Community, CommunityAdminGroup, Media, UserProfile -import pyshorteners + +from django.db.models import Q + +from _main_.utils.feature_flags.FeatureFlagConstants import FeatureFlagConstants +from database.models import AboutUsPageSettings, ActionsPageSettings, Community, CommunityAdminGroup, \ + ContactUsPageSettings, EventsPageSettings, FeatureFlag, ImpactPageSettings, Media, Menu, \ + TeamsPageSettings, TestimonialsPageSettings, UserProfile, \ + VendorsPageSettings def is_admin_of_community(context, community_id): @@ -98,3 +105,98 @@ def create_media_file(file, name): media = Media.objects.create(name=name, file=file) media.save() return media + + +class DonationPageSettings: + pass + +def prependPrefixToLinks(menu_item, prefix): + if not menu_item: + return None + if "link" in menu_item: + menu_item["link"] = prefix + menu_item["link"] + if "children" in menu_item: + for child in menu_item["children"]: + prependPrefixToLinks(child, prefix) + return menu_item + +def modify_menu_items_if_published(menu_items, page_settings, prefix): + if not menu_items or not page_settings or not prefix: + return [] + + main_menu = [] + + for item in menu_items: + if not item.get("children"): + name = item.get("link", "").strip("/") + if name in page_settings and not page_settings[name]: + main_menu.remove(item) + else: + for child in item["children"]: + name = child.get("link", "").strip("/") + if name in page_settings and not page_settings[name]: + item["children"].remove(child) + + for item in menu_items: + f = prependPrefixToLinks(item,prefix) + main_menu.append(f) + + return main_menu + +def get_viable_menu_items(community): + + about_us_page_settings = AboutUsPageSettings.objects.filter(community=community).first() + events_page_settings = EventsPageSettings.objects.filter(community=community).first() + impact_page_settings = ImpactPageSettings.objects.filter(community=community).first() + actions_page_settings = ActionsPageSettings.objects.filter(community=community).first() + contact_us_page_settings = ContactUsPageSettings.objects.filter(community=community).first() + teams_page_settings = TeamsPageSettings.objects.filter(community=community).first() + testimonial_page_settings = TestimonialsPageSettings.objects.filter(community=community).first() + vendors_page_settings = VendorsPageSettings.objects.filter(community=community).first() + + menu_items = {} + all_menu = Menu.objects.all() + + nav_menu = all_menu.get(name="PortalMainNavLinks") + + portal_main_nav_links = modify_menu_items_if_published(nav_menu.content, { + "impact": impact_page_settings.is_published, + "aboutus": about_us_page_settings.is_published, + "contactus": contact_us_page_settings.is_published, + "actions": actions_page_settings.is_published, + "services": vendors_page_settings.is_published, + "testimonials": testimonial_page_settings.is_published, + "teams": teams_page_settings.is_published, + "events": events_page_settings.is_published + }, community.subdomain) + + footer_menu_content = all_menu.get(name='PortalFooterQuickLinks') + + portal_footer_quick_links = [ + {**item, "link": community.subdomain + "/" + item["link"]} + if not item.get("children") and item.get("navItemId", None) != "footer-report-a-bug-id" + else item + for item in footer_menu_content.content["links"] + ] + portal_footer_contact_info = all_menu.get(name='PortalFooterContactInfo') + return [ + {**nav_menu.simple_json(), "content": portal_main_nav_links}, + {**footer_menu_content.simple_json(), "content": {"links": portal_footer_quick_links}}, + portal_footer_contact_info.simple_json() + + ] + + + + + +# .......................... site setup utils .......................... +def get_enabled_feature_flags_for_community(community): + feature_flags = FeatureFlag.objects.filter( + Q(audience=FeatureFlagConstants().for_everyone()) | + Q(audience=FeatureFlagConstants().for_specific_audience(), communities=community) | + (Q(audience=FeatureFlagConstants().for_all_except()) & ~Q(communities=community)) + ).exclude(expires_on__lt=datetime.now()).prefetch_related('communities') + + return feature_flags + From 11cec21c6167958a3eca194a6e6d11e8dbdf6616 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 17 Apr 2024 16:41:30 +0000 Subject: [PATCH 02/16] feat: Remove confirm.sh and streamline environment handling This commit removes the redundant 'confirm.sh' script and enhances the handling of environment variables in 'settings.py'. It introduces standardized environment variable checks and configurations in Makefile, and removes the previously hard coded environment variables. --- src/Makefile | 6 ++++++ src/_main_/settings.py | 24 ++++++++++++------------ src/confirm.sh | 17 ----------------- 3 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 src/confirm.sh diff --git a/src/Makefile b/src/Makefile index 88e864468..bd6041cd9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -24,6 +24,8 @@ build-and-push-prod: docker build -t massenergize/api . docker tag massenergize/api:latest 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api:$(VERSION) docker push 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api:$(VERSION) + + eb setenv DJANGO_ENV=prod eb deploy --label $(VERSION) git tag prod@$(VERSION) @@ -41,6 +43,8 @@ build-and-push-canary: docker build -t massenergize/api-canary . docker tag massenergize/api-canary:latest 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api-canary:$(CANARY_VERSION) docker push 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api-canary:$(CANARY_VERSION) + + eb setenv DJANGO_ENV=canary eb deploy --label $(CANARY_VERSION) git tag canary@$(CANARY_VERSION) @@ -58,6 +62,8 @@ build-and-push-dev: docker build -t massenergize/api-dev . docker tag massenergize/api-dev:latest 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api-dev:$(DEV_VERSION) docker push 202758212688.dkr.ecr.us-east-2.amazonaws.com/massenergize/api-dev:$(DEV_VERSION) + + eb setenv DJANGO_ENV=dev eb deploy --label $(DEV_VERSION) git tag dev@$(DEV_VERSION) diff --git a/src/_main_/settings.py b/src/_main_/settings.py index b02128712..1b1407f2c 100644 --- a/src/_main_/settings.py +++ b/src/_main_/settings.py @@ -27,12 +27,12 @@ # ******** LOAD CONFIG DATA ***********# # DJANGO_ENV can be passed in through the makefile, with "make start env=local" -DJANGO_ENV = os.environ.get("DJANGO_ENV","remote") +DJANGO_ENV = os.environ.get("DJANGO_ENV", "dev").lower() # Database selection, development DB unless one of these chosen -IS_PROD = False -IS_CANARY = False -IS_LOCAL = False +IS_PROD = DJANGO_ENV == 'prod' +IS_CANARY = DJANGO_ENV == 'canary' +IS_LOCAL = DJANGO_ENV == 'local' RUN_SERVER_LOCALLY = IS_LOCAL RUN_CELERY_LOCALLY = IS_LOCAL @@ -40,15 +40,15 @@ if is_test_mode(): RUN_CELERY_LOCALLY = True +valid_envs = {"prod", "canary", "local", "dev"} + try: - if IS_PROD: - env_path = Path('.') / 'prod.env' - elif IS_CANARY: - env_path = Path('.') / 'canary.env' - elif IS_LOCAL: - env_path = Path('.') / 'local.env' - else: - env_path = Path('.') / 'dev.env' + + if DJANGO_ENV not in valid_envs: + print("Raising ValueError") + raise ValueError(f"Invalid DJANGO_ENV: {DJANGO_ENV}.") + + env_path = Path('.') / f'{DJANGO_ENV}.env' load_dotenv(dotenv_path=env_path, verbose=True) except Exception: load_dotenv() diff --git a/src/confirm.sh b/src/confirm.sh deleted file mode 100644 index a4f4cba0b..000000000 --- a/src/confirm.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# Ask for the user's confirmation -echo "Are you sure you want to execute the make command? (y/n)" -read confirm - -# Convert the input to lower case -confirm=$(echo $confirm | tr '[:upper:]' '[:lower:]') - -# Check if the user confirmed -if [[ $confirm == 'y' || $confirm == 'yes' ]]; then - # Execute the make command - make $@ -else - # Inform the user that the operation was cancelled - echo "Operation cancelled." -fi \ No newline at end of file From 3fc00105ee48355545986c82d64d18f6ba633340 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Sat, 20 Apr 2024 12:42:35 +0000 Subject: [PATCH 03/16] Refactor: :sparkles: :recycle: API utils and related components for efficiency This commit restructures API utilities and associated components, particularly in the context of loading site data. The changes introduce a more streamlined condition branching for page settings with the introduction of function maps, resulting in cleaner and more readable code. It also updates imported models and enhances data loading methods for different types of content like actions, vendors, testimonials, and others. Additionally, some naming changes and paths adjustments are made for accuracy and compatibility. --- src/api/handlers/misc.py | 7 +- src/api/store/misc.py | 90 +++++++--------- src/api/utils/api_utils.py | 209 +++++++++++++++++++++++++++++++++++-- 3 files changed, 240 insertions(+), 66 deletions(-) diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index 8fdd2549d..ea06f1b0b 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -17,7 +17,7 @@ def __init__(self): def registerRoutes(self) -> None: self.add("/menus.remake", self.remake_navigation_menu) - self.add("/menus.list", self.navigation_menu_list) + self.add("/menus.list", self.load_menu_items) self.add("/data.backfill", self.backfill) self.add("/data.carbonEquivalency.create", self.create_carbon_equivalency) self.add("/data.carbonEquivalency.update", self.update_carbon_equivalency) @@ -33,7 +33,6 @@ def registerRoutes(self) -> None: self.add("/what.happened", self.fetch_footages) self.add("/actions.report", self.actions_report) self.add("/site.load", self.load_essential_initial_site_data) - self.add("/menu.load", self.load_menu_items) @admins_only def fetch_footages(self, request): @@ -167,7 +166,7 @@ def load_essential_initial_site_data(self, request): self.validator.expect("community_id", is_required=False) self.validator.expect("subdomain", is_required=False) - self.validator.expect("user_id", is_required=False) + self.validator.expect("id", is_required=False) #when viewing item details self.validator.expect("page", str, is_required=True) args, err = self.validator.verify(args, strict=True) @@ -176,7 +175,7 @@ def load_essential_initial_site_data(self, request): data, err = self.service.load_essential_initial_site_data(context, args) if err: - return MassenergizeResponse(error=err) + return err return MassenergizeResponse(data=data) diff --git a/src/api/store/misc.py b/src/api/store/misc.py index acb9aa82d..38b3aaa7e 100644 --- a/src/api/store/misc.py +++ b/src/api/store/misc.py @@ -1,9 +1,8 @@ -from _main_.utils.common import serialize_all from _main_.utils.footage.spy import Spy from api.tests.common import createUsers from database.models import ( - Action, - Vendor, + AboutUsPageSettings, Action, + ActionsPageSettings, ContactUsPageSettings, EventsPageSettings, ImpactPageSettings, TeamsPageSettings, TestimonialsPageSettings, Vendor, Subdomain, Event, Community, @@ -19,7 +18,7 @@ UserActionRel, Data, Location, - HomePageSettings, + HomePageSettings, VendorsPageSettings, ) from _main_.utils.massenergize_errors import ( CustomMassenergizeError, @@ -33,7 +32,7 @@ from sentry_sdk import capture_message from typing import Tuple -from ..utils.api_utils import get_enabled_feature_flags_for_community, get_viable_menu_items +from api.utils.api_utils import get_viable_menu_items, load_about_data, load_aboutus_data, load_actions_data, load_contactus_data, load_events_data, load_homepage_data, load_impact_data, load_one_action_data, load_one_event_data, load_one_team_data, load_one_testimonial_data, load_one_vendor_data, load_policies_data, load_profile_data, load_settings_data, load_teams_data, load_testimonials_data, load_vendors_data class MiscellaneousStore: @@ -502,7 +501,7 @@ def load_essential_initial_site_data(self, context, args): page = args.get("page", None) subdonain = args.get("subdomain", None) community_id = args.get("community_id", None) - user_id = args.get("user_id", None) + id = args.get("id", None) if not subdonain and not community_id: return None, CustomMassenergizeError("No community or subdomain provided") @@ -514,59 +513,48 @@ def load_essential_initial_site_data(self, context, args): if not community: return None, CustomMassenergizeError("Community not found") - menu = get_viable_menu_items(community) - + page_settings = { + "home_page_settings": HomePageSettings.objects.filter(community=community).first(), + "actions_page_settings": ActionsPageSettings.objects.filter(community=community).first(), + "events_page_settings": EventsPageSettings.objects.filter(community=community).first(), + "vendors_page_settings": VendorsPageSettings.objects.filter(community=community).first(), + "about_us_page_settings": AboutUsPageSettings.objects.filter(community=community).first(), + "testimonials_page_settings": TestimonialsPageSettings.objects.filter(community=community).first(), + "teams_page_settings": TeamsPageSettings.objects.filter(community=community).first(), + "contact_us_page_settings": ContactUsPageSettings.objects.filter(community=community).first(), + "impact_page_settings": ImpactPageSettings.objects.filter(community=community).first(), + } data = {} - if page == "homepage": - pass - data["page_settings"] = HomePageSettings.objects.filter(community=community).first().simple_json() - data["menu"] = menu - # get feature flags - data["feature_flags"] = serialize_all(get_enabled_feature_flags_for_community(community)) - # get impact page settings - elif page == "actions": - pass - # get action page settings - # get all tag collections - # get all actions - elif page == "events": - pass - # get event page settings - # get all events - # get all tag collections - # get event exceptions for the community - elif page == "vendors": - pass - # get vendor page settings - # get all vendors - # get all tag collections - elif page == "about": - pass - # get about us page settings - # get donate page settings - elif page == "testimonials": - pass - # get testimonials page settings - # get all testimonials - # get all tag collections - - elif page == "one_vendor": - pass - # get vendor info - # get all testimonials for the vendor - - elif page == "one_action": - pass - # get action info + page_func_map = { + "homepage": load_homepage_data, + "actions": load_actions_data, + "events": load_events_data, + "vendors": load_vendors_data, + "about": load_about_data, + "testimonials": load_testimonials_data, + "one_vendor": load_one_vendor_data, + "impact": load_impact_data, + "aboutus": load_aboutus_data, + "contactus": load_contactus_data, + "teams": load_teams_data, + "one_team": load_one_team_data, + "one_action": load_one_action_data, + "one_event": load_one_event_data, + "one_testimonial": load_one_testimonial_data, + "profile": load_profile_data, + "settings": load_settings_data, + "policies": load_policies_data, + } + func = page_func_map.get(page) + if func: + data = func(context, args, community, id, page_settings) return data, None except Exception as e: return None, CustomMassenergizeError(e) - - def load_menu_items(self, context, args): try: page = args.get("page", None) diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index a7ddcac79..564597630 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -1,13 +1,16 @@ -from datetime import datetime +from datetime import datetime,timedelta from math import atan2, cos, radians, sin, sqrt from django.db.models import Q +from _main_.utils.common import serialize_all from _main_.utils.feature_flags.FeatureFlagConstants import FeatureFlagConstants -from database.models import AboutUsPageSettings, ActionsPageSettings, Community, CommunityAdminGroup, \ - ContactUsPageSettings, EventsPageSettings, FeatureFlag, ImpactPageSettings, Media, Menu, \ - TeamsPageSettings, TestimonialsPageSettings, UserProfile, \ - VendorsPageSettings +from database.models import AboutUsPageSettings, Action, ActionsPageSettings, Community, CommunityAdminGroup, \ + ContactUsPageSettings, Event, EventsPageSettings, FeatureFlag, ImpactPageSettings, Media, Menu, \ + Policy, TagCollection, Team, TeamsPageSettings, Testimonial, TestimonialsPageSettings, UserProfile, \ + Vendor, VendorsPageSettings +from database.utils.settings.admin_settings import AdminPortalSettings +from database.utils.settings.user_settings import UserPortalSettings def is_admin_of_community(context, community_id): @@ -114,7 +117,7 @@ def prependPrefixToLinks(menu_item, prefix): if not menu_item: return None if "link" in menu_item: - menu_item["link"] = prefix + menu_item["link"] + menu_item["link"] = "/" + prefix + menu_item["link"] if "children" in menu_item: for child in menu_item["children"]: prependPrefixToLinks(child, prefix) @@ -173,7 +176,7 @@ def get_viable_menu_items(community): footer_menu_content = all_menu.get(name='PortalFooterQuickLinks') portal_footer_quick_links = [ - {**item, "link": community.subdomain + "/" + item["link"]} + {**item, "link": "/"+community.subdomain + "/" + item["link"]} if not item.get("children") and item.get("navItemId", None) != "footer-report-a-bug-id" else item for item in footer_menu_content.content["links"] @@ -186,10 +189,6 @@ def get_viable_menu_items(community): ] - - - - # .......................... site setup utils .......................... def get_enabled_feature_flags_for_community(community): feature_flags = FeatureFlag.objects.filter( @@ -199,4 +198,192 @@ def get_enabled_feature_flags_for_community(community): ).exclude(expires_on__lt=datetime.now()).prefetch_related('communities') return feature_flags +def get_actions_for_community(community, context): + actions = Action.objects.select_related('image', 'community').prefetch_related('tags', 'vendors').filter(community=community) + if not context.is_sandbox: + if context.user_is_logged_in and not context.user_is_admin(): + actions = actions.filter(Q(user__id=context.user_id) | Q(is_published=True)) + else: + actions = actions.filter(is_published=True) + return actions.distinct() + + +def get_events_for_community(community, context): + today = datetime.now() + shared_months = 2 + hosted_months = 6 + earliest_shared = today - timedelta(weeks=4 * shared_months) + earliest_hosted = today - timedelta(weeks=4 * hosted_months) + + events = Event.objects.select_related('image', 'community').prefetch_related('tags', 'invited_communities').filter( + community__id=community.id, start_date_and_time__gte=earliest_hosted) + + shared = community.events_from_others.filter(is_published=True, start_date_and_time__gte=earliest_shared) + if not context.is_sandbox and events: + if context.user_is_logged_in and not context.user_is_admin(): + events = events.filter(Q(user__id=context.user_id) | Q(is_published=True)) + else: + events = events.filter(is_published=True) + all_events = [*events, *shared] + + return all_events + + +def get_teams_for_community(community): + pass + +def get_testimonials_for_community(community, context): + + testimonials = Testimonial.objects.filter( + community=community, is_deleted=False).prefetch_related('tags__tag_collection', 'action__tags', 'vendor', + 'community') + + if not context.is_sandbox: + if context.user_is_logged_in and not context.user_is_admin(): + testimonials = testimonials.filter(Q(user__id=context.user_id) | Q(is_published=True)) + else: + testimonials = testimonials.filter(is_published=True) + + return testimonials.distinct() + +def get_vendors_for_community(community, context): + vendors = community.community_vendors.filter(is_deleted=False) + + if not context.is_sandbox: + if context.user_is_logged_in and not context.user_is_admin(): + vendors = vendors.filter(Q(user__id=context.user_id) | Q(is_published=True)) + else: + vendors = vendors.filter(is_published=True) + return vendors.distinct() + +def get_tags_collections(): + return TagCollection.objects.filter(is_deleted=False) + +# .......................... site setup utils .......................... +def load_homepage_data(context, args, community, id, page_settings): + data = {} + data["home_page_settings"] = page_settings["home_page_settings"].simple_json() if page_settings["home_page_settings"] else None + data["feature_flags"] = serialize_all(get_enabled_feature_flags_for_community(community)) + data["impact_page_settings"] = page_settings["impact_page_settings"].simple_json() if page_settings["impact_page_settings"] else None + return data + +def load_actions_data(context, args, community, id, page_settings): + data = {} + data["actions_page_settings"] = page_settings["actions_page_settings"].simple_json() if page_settings["actions_page_settings"] else None + data["actions"] = serialize_all(get_actions_for_community(community, context)) + data["tag_collections"] = serialize_all(get_tags_collections()) + return data + +def load_events_data(context, args, community, id, page_settings): + data = {} + data["events_page_settings"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None + data["events"] = serialize_all(get_events_for_community(community, context)) + data["tag_collections"] = serialize_all(get_tags_collections()) + return data + +def load_vendors_data(context, args, community, id, page_settings): + data = {} + data["vendors_page_settings"] = page_settings["vendors_page_settings"].simple_json() if page_settings["vendors_page_settings"] else None + data["vendors"] = serialize_all(get_vendors_for_community(community, context)) + data["tag_collections"] = serialize_all(get_tags_collections()) + return data + +def load_about_data(context, args, community, id, page_settings): + data = {} + data["about_us_page_settings"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None + data["donate_page_settings"] = {} + return data + +def load_testimonials_data(context, args, community, id, page_settings): + data = {} + data["testimonials_page_settings"] = page_settings["testimonials_page_settings"].simple_json() if page_settings["testimonials_page_settings"] else None + data["testimonials"] = serialize_all(get_testimonials_for_community(community, context)) + data["tag_collections"] = serialize_all(get_tags_collections()) + return data + +def load_one_vendor_data(context, args, community, id, page_settings): + data = {} + vendor = Vendor.objects.filter(id=id).first() + data["vendor"] = vendor.simple_json() + data["testimonials"] = serialize_all(Testimonial.objects.filter(vendor=vendor)) + return data + +def load_impact_data(context, args, community, id, page_settings): + from api.store.graph import GraphStore + data = {} + completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) + communities_impact_graph, _ = GraphStore().graph_communities_impact(context, args) + data["tag_collections"] = serialize_all(get_tags_collections()) + data["graph_actions_completed"] = completed_action_graph_data + data["graph_communities_impact"] = communities_impact_graph + return data + +def load_aboutus_data(context, args, community, id, page_settings): + data = {} + data["about_us_page_settings"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None + return data + +def load_contactus_data(context, args, community, id, page_settings): + data = {} + data["contact_us_page_settings"] = page_settings["contact_us_page_settings"].simple_json() if page_settings["contact_us_page_settings"] else None + return data + +def load_teams_data(context, args, community, id, page_settings): + from api.store.team import TeamStore + from api.store.graph import GraphStore + data = {} + completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) + teams_stats, _ = TeamStore().team_stats(context, args) + data["teams_page_settings"] = page_settings["teams_page_settings"].simple_json() if page_settings["teams_page_settings"] else None + data["graph_actions_completed"] = completed_action_graph_data + data["teams_stats"] = teams_stats + return data + +def load_one_team_data(context, args, community, id, page_settings): + from api.store.team import TeamStore + data = {} + teams_stats, _ = TeamStore().team_stats(context, args) + data["team"] = Team.objects.filter(id=id).first().simple_json() + data["teams_stats"] = teams_stats + return data + +def load_one_action_data(context, args, community, id, page_settings): + from api.store.graph import GraphStore + data = {} + completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) + data["graph_actions_completed"] = completed_action_graph_data + data["action"] = Action.objects.filter(id=id).first().simple_json() + return data + +def load_one_event_data(context, args, community, id, page_settings): + data = {} + data["events_page_settings"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None + data["event"] = Event.objects.filter(id=id).first().simple_json() + return data + +def load_one_testimonial_data(context, args, community, id, page_settings): + data = {} + data["testimonial"] = Testimonial.objects.filter(id=id).first().simple_json() + return data + +def load_profile_data(context, args, community, id, page_settings): + from api.store.team import TeamStore + data = {} + teams_stats, _ = TeamStore().team_stats(context, args) + data["teams_stats"] = teams_stats + return data + +def load_settings_data(context, args, community, id, page_settings): + data = {} + if context.user_is_admin(): + data["preferences"] = UserPortalSettings.Preferences if args.get("subdomain") else AdminPortalSettings.Preferences + else: + data["preferences"] = UserPortalSettings.Preferences + return data + +def load_policies_data(context, args, community, id, page_settings): + data = {} + policies = Policy.objects.filter(is_deleted=False) + data["policies"] = policies + return data From 0ae1853dc85678a0214edbdeb5a74a862058850a Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Sat, 20 Apr 2024 13:16:00 +0000 Subject: [PATCH 04/16] Refactor page settings data key in api_utils.py Page settings data keys in the api utils have been refactored for standardization. Specific keys for homepage, actions, events, vendors, about, testimonials, about us and contact us page settings have all been changed to 'page_data'. This change simplifies data access and highlights the shared structure of these pages. --- src/api/utils/api_utils.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index 564597630..f54c8d739 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -262,41 +262,41 @@ def get_tags_collections(): # .......................... site setup utils .......................... def load_homepage_data(context, args, community, id, page_settings): data = {} - data["home_page_settings"] = page_settings["home_page_settings"].simple_json() if page_settings["home_page_settings"] else None + data["page_data"] = page_settings["home_page_settings"].simple_json() if page_settings["home_page_settings"] else None data["feature_flags"] = serialize_all(get_enabled_feature_flags_for_community(community)) data["impact_page_settings"] = page_settings["impact_page_settings"].simple_json() if page_settings["impact_page_settings"] else None return data def load_actions_data(context, args, community, id, page_settings): data = {} - data["actions_page_settings"] = page_settings["actions_page_settings"].simple_json() if page_settings["actions_page_settings"] else None + data["page_data"] = page_settings["actions_page_settings"].simple_json() if page_settings["actions_page_settings"] else None data["actions"] = serialize_all(get_actions_for_community(community, context)) data["tag_collections"] = serialize_all(get_tags_collections()) return data def load_events_data(context, args, community, id, page_settings): data = {} - data["events_page_settings"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None + data["page_data"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None data["events"] = serialize_all(get_events_for_community(community, context)) data["tag_collections"] = serialize_all(get_tags_collections()) return data def load_vendors_data(context, args, community, id, page_settings): data = {} - data["vendors_page_settings"] = page_settings["vendors_page_settings"].simple_json() if page_settings["vendors_page_settings"] else None + data["page_data"] = page_settings["vendors_page_settings"].simple_json() if page_settings["vendors_page_settings"] else None data["vendors"] = serialize_all(get_vendors_for_community(community, context)) data["tag_collections"] = serialize_all(get_tags_collections()) return data def load_about_data(context, args, community, id, page_settings): data = {} - data["about_us_page_settings"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None + data["page_data"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None data["donate_page_settings"] = {} return data def load_testimonials_data(context, args, community, id, page_settings): data = {} - data["testimonials_page_settings"] = page_settings["testimonials_page_settings"].simple_json() if page_settings["testimonials_page_settings"] else None + data["page_data"] = page_settings["testimonials_page_settings"].simple_json() if page_settings["testimonials_page_settings"] else None data["testimonials"] = serialize_all(get_testimonials_for_community(community, context)) data["tag_collections"] = serialize_all(get_tags_collections()) return data @@ -320,12 +320,12 @@ def load_impact_data(context, args, community, id, page_settings): def load_aboutus_data(context, args, community, id, page_settings): data = {} - data["about_us_page_settings"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None + data["page_data"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None return data def load_contactus_data(context, args, community, id, page_settings): data = {} - data["contact_us_page_settings"] = page_settings["contact_us_page_settings"].simple_json() if page_settings["contact_us_page_settings"] else None + data["page_data"] = page_settings["contact_us_page_settings"].simple_json() if page_settings["contact_us_page_settings"] else None return data def load_teams_data(context, args, community, id, page_settings): @@ -334,7 +334,7 @@ def load_teams_data(context, args, community, id, page_settings): data = {} completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) teams_stats, _ = TeamStore().team_stats(context, args) - data["teams_page_settings"] = page_settings["teams_page_settings"].simple_json() if page_settings["teams_page_settings"] else None + data["page_data"] = page_settings["teams_page_settings"].simple_json() if page_settings["teams_page_settings"] else None data["graph_actions_completed"] = completed_action_graph_data data["teams_stats"] = teams_stats return data @@ -357,7 +357,7 @@ def load_one_action_data(context, args, community, id, page_settings): def load_one_event_data(context, args, community, id, page_settings): data = {} - data["events_page_settings"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None + data["page_data"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None data["event"] = Event.objects.filter(id=id).first().simple_json() return data From 49865f5b968d19be9b3279253a8bd95eb6e4a1ce Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Tue, 23 Apr 2024 09:58:12 +0000 Subject: [PATCH 05/16] fix: Update user event eligibility checking logic Modified the eligibility check logic in user_event_nudge module to compare only the dates instead of the full datetime. This ensures that the comparison between last run time and event published time is done only on the basis of date, disregarding the time part. --- src/task_queue/nudges/user_event_nudge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/task_queue/nudges/user_event_nudge.py b/src/task_queue/nudges/user_event_nudge.py index 4cf76f356..f8d86d5e7 100644 --- a/src/task_queue/nudges/user_event_nudge.py +++ b/src/task_queue/nudges/user_event_nudge.py @@ -107,7 +107,7 @@ def update_last_notification_dates(email): def is_event_eligible(event, community_id, task=None): - now = timezone.now() + now = timezone.now().date() settings = event.nudge_settings.filter(communities__id=community_id).first() if settings.never: @@ -126,7 +126,7 @@ def is_event_eligible(event, community_id, task=None): if freq: last_last_run = now - freq_to_delta.get(freq, relativedelta(days=0)) - if settings.when_first_posted and event.published_at and last_last_run < event.published_at <= now: + if settings.when_first_posted and event.published_at and last_last_run < event.published_at.date() <= now: return True elif settings.within_30_days and event.start_date_and_time - now <= timezone.timedelta(days=30): return True From 565d4d7f620dfa02d5b502940161a23087531ea7 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 24 Apr 2024 11:11:33 +0000 Subject: [PATCH 06/16] :recycle: refactor: Update field renaming, data fetching, and concurrent requests Modified several files to improve data fetching, field renaming, and handling of concurrent requests. Updated 'vendor', 'event', 'misc', 'subscriber', 'policy' and 'goal' handlers to adjust field renaming from 'id' to either 'vendor_id', 'event_id', 'subscriber_id', 'policy_id' or 'goal_id'. Optimized 'misc' handler to fetch data from multiple endpoints concurrently using ThreadPoolExecutor. These changes provide more consistent naming across handlers and increases data processing efficiency. --- src/api/handlers/event.py | 3 ++- src/api/handlers/goal.py | 2 +- src/api/handlers/misc.py | 26 ++++++++++++++++++++------ src/api/handlers/policy.py | 2 +- src/api/handlers/subscriber.py | 2 +- src/api/handlers/vendor.py | 4 ++++ src/api/utils/api_utils.py | 1 + 7 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/api/handlers/event.py b/src/api/handlers/event.py index b8bb93742..c840b49f1 100644 --- a/src/api/handlers/event.py +++ b/src/api/handlers/event.py @@ -64,7 +64,8 @@ def info(self, request): def copy(self, request): context: Context = request.context args: dict = context.args - + + self.validator.rename("id", "event_id") self.validator.expect("event_id", int, is_required=True) args, err = self.validator.verify(args, strict=True) diff --git a/src/api/handlers/goal.py b/src/api/handlers/goal.py index dbea7a487..817f74871 100644 --- a/src/api/handlers/goal.py +++ b/src/api/handlers/goal.py @@ -37,7 +37,7 @@ def info(self, request): context: Context = request.context args: dict = context.args - goal_id = args.get('goal_id') + goal_id = args.get('goal_id', args.get('id', None)) goal_info, err = self.service.get_goal_info(goal_id) if err: return err diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index ea06f1b0b..86256142d 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -7,6 +7,9 @@ from api.decorators import admins_only, super_admins_only from database.utils.settings.admin_settings import AdminPortalSettings from database.utils.settings.user_settings import UserPortalSettings +from concurrent.futures import ThreadPoolExecutor +import requests +from django.urls import reverse class MiscellaneousHandler(RouteHandler): @@ -166,16 +169,27 @@ def load_essential_initial_site_data(self, request): self.validator.expect("community_id", is_required=False) self.validator.expect("subdomain", is_required=False) - self.validator.expect("id", is_required=False) #when viewing item details - self.validator.expect("page", str, is_required=True) + self.validator.expect("data", 'str_list', is_required=True) + self.validator.expect("id", is_required=False) args, err = self.validator.verify(args, strict=True) - if err: - return MassenergizeResponse(error=err) - - data, err = self.service.load_essential_initial_site_data(context, args) if err: return err + + endpoints = args.pop("data", []) + + def fetch_data(endpoint): + endpoint = request.build_absolute_uri(endpoint) + response = requests.post(endpoint,data=args) + return response.json() + + # Use a ThreadPoolExecutor to make requests to all endpoints concurrently + with ThreadPoolExecutor(max_workers=5) as executor: + results = executor.map(fetch_data, endpoints) + + # Convert the results to a dictionary in the format {endpoint: data} + data = {endpoint: result for endpoint, result in zip(endpoints, results)} + return MassenergizeResponse(data=data) diff --git a/src/api/handlers/policy.py b/src/api/handlers/policy.py index e013b3c4f..59d8b7222 100644 --- a/src/api/handlers/policy.py +++ b/src/api/handlers/policy.py @@ -32,7 +32,7 @@ def registerRoutes(self): def info(self, request): context: Context = request.context args: dict = context.args - policy_id = args.pop('policy_id', None) + policy_id = args.pop('policy_id', args.pop('id', None)) policy_info, err = self.service.get_policy_info(policy_id) if err: return err diff --git a/src/api/handlers/subscriber.py b/src/api/handlers/subscriber.py index 7018c0e99..57eed7444 100644 --- a/src/api/handlers/subscriber.py +++ b/src/api/handlers/subscriber.py @@ -35,7 +35,7 @@ def registerRoutes(self): def info(self, request): context: Context = request.context args: dict = context.args - subscriber_id = args.pop('subscriber_id', None) + subscriber_id = args.pop('subscriber_id', args.pop('id', None)) if subscriber_id and not isinstance(subscriber_id, int): subscriber_id = parse_int(subscriber_id) subscriber_info, err = self.service.get_subscriber_info(subscriber_id) diff --git a/src/api/handlers/vendor.py b/src/api/handlers/vendor.py index 62946ff80..2c5a30671 100644 --- a/src/api/handlers/vendor.py +++ b/src/api/handlers/vendor.py @@ -41,6 +41,10 @@ def registerRoutes(self): def info(self, request): context: Context = request.context args = context.get_request_body() + + self.validator.rename("id", "vendor_id") + self.validator.expect("vendor_id", int, is_required=False) + args = rename_field(args, 'vendor_id', 'id') vendor_info, err = self.service.get_vendor_info(context, args) if err: diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index f54c8d739..baeab06f4 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -113,6 +113,7 @@ def create_media_file(file, name): class DonationPageSettings: pass +# -------------------------- Menu Utils -------------------------- def prependPrefixToLinks(menu_item, prefix): if not menu_item: return None From b1e8e6fa4fa10eeae18d8ad020e0ff61276b78de Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 24 Apr 2024 11:52:21 +0000 Subject: [PATCH 07/16] :recycle: Ensure distinct feature flags are returned The code modification ensures that duplicate feature flags are eliminated when retrieved. This correction enhances data consistency and prevents any potential confusion or errors due to duplicate flags in the system. --- src/api/store/community.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/store/community.py b/src/api/store/community.py index c4c1204f3..7bc1db2a7 100644 --- a/src/api/store/community.py +++ b/src/api/store/community.py @@ -1366,7 +1366,7 @@ def list_communities_feature_flags(self, context, args) -> Tuple[list, MassEnerg (Q(audience=FeatureFlagConstants().for_all_except()) & ~Q(communities__in=communities)) ).exclude(expires_on__lt=datetime.now()).prefetch_related('communities') - return feature_flags, None + return feature_flags.distinct(), None except Exception as e: return None, CustomMassenergizeError(str(e)) From 1e04e5c581083c89997efbfc8102089faa315a41 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 24 Apr 2024 12:05:22 +0000 Subject: [PATCH 08/16] :recycle: Set IS_LOCAL to True and refactor environment path selection IS_LOCAL has been hardcoded as True to ensure local environment setting. Additionally, the method for env_path selection has been refactored for clarity and simplicity. Now, the path is directly set with corresponding '.env' based on whether IS_PROD, IS_CANARY, or IS_LOCAL is True, defaulting to 'dev.env' otherwise. --- src/_main_/settings.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/_main_/settings.py b/src/_main_/settings.py index 1b1407f2c..53ed3f0f0 100644 --- a/src/_main_/settings.py +++ b/src/_main_/settings.py @@ -32,7 +32,7 @@ # Database selection, development DB unless one of these chosen IS_PROD = DJANGO_ENV == 'prod' IS_CANARY = DJANGO_ENV == 'canary' -IS_LOCAL = DJANGO_ENV == 'local' +IS_LOCAL = True RUN_SERVER_LOCALLY = IS_LOCAL RUN_CELERY_LOCALLY = IS_LOCAL @@ -40,15 +40,17 @@ if is_test_mode(): RUN_CELERY_LOCALLY = True -valid_envs = {"prod", "canary", "local", "dev"} + try: - - if DJANGO_ENV not in valid_envs: - print("Raising ValueError") - raise ValueError(f"Invalid DJANGO_ENV: {DJANGO_ENV}.") - - env_path = Path('.') / f'{DJANGO_ENV}.env' + if IS_PROD: + env_path = Path('.') / 'prod.env' + elif IS_CANARY: + env_path = Path('.') / 'canary.env' + elif IS_LOCAL: + env_path = Path('.') / 'local.env' + else: + env_path = Path('.') / 'dev.env' load_dotenv(dotenv_path=env_path, verbose=True) except Exception: load_dotenv() From 0bf6a7ceca3b036f1cbd664d9b6e65f94495cce4 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 24 Apr 2024 12:06:52 +0000 Subject: [PATCH 09/16] :recycle: Update environment condition for LOCAL setup The environment condition for LOCAL setup has been updated in settings.py. Instead of setting IS_LOCAL as a boolean constant true, it now compares the DJANGO_ENV with 'local'. This allows more flexibility in managing different environments. --- src/_main_/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_main_/settings.py b/src/_main_/settings.py index 53ed3f0f0..3d4a52943 100644 --- a/src/_main_/settings.py +++ b/src/_main_/settings.py @@ -32,7 +32,7 @@ # Database selection, development DB unless one of these chosen IS_PROD = DJANGO_ENV == 'prod' IS_CANARY = DJANGO_ENV == 'canary' -IS_LOCAL = True +IS_LOCAL = DJANGO_ENV == 'local' RUN_SERVER_LOCALLY = IS_LOCAL RUN_CELERY_LOCALLY = IS_LOCAL From 75c72be583ad08d6a5d4fae13ce0d48be0caeadf Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 11:00:53 +0000 Subject: [PATCH 10/16] Refactor import statements and comment out unused code The import statements in the file src/api/store/misc.py have been updated to increase readability. Also, the 'load_essential_initial_site_data' method in src/api/store/misc.py and src/api/services/misc.py has been commented out due to being currently unused. This action is taken as part of code clean up and to improve the maintainability of the codebase. --- src/api/services/misc.py | 12 +-- src/api/store/misc.py | 157 +++++++++++++++++---------------------- 2 files changed, 76 insertions(+), 93 deletions(-) diff --git a/src/api/services/misc.py b/src/api/services/misc.py index bf61f339f..bf9642e08 100644 --- a/src/api/services/misc.py +++ b/src/api/services/misc.py @@ -143,12 +143,12 @@ def authenticateFrontendInTestMode(self, args): return signinAs(client, user), None - def load_essential_initial_site_data(self,context, args): - res, err = self.store.load_essential_initial_site_data(context,args) - if err: - return None, err - - return res, None + # def load_essential_initial_site_data(self,context, args): + # res, err = self.store.load_essential_initial_site_data(context,args) + # if err: + # return None, err + # + # return res, None def load_menu_items(self,context, args): res, err = self.store.load_menu_items(context,args) diff --git a/src/api/store/misc.py b/src/api/store/misc.py index 38b3aaa7e..77f10dedb 100644 --- a/src/api/store/misc.py +++ b/src/api/store/misc.py @@ -1,38 +1,21 @@ +from typing import Tuple + +from sentry_sdk import capture_message + +from _main_.utils.context import Context from _main_.utils.footage.spy import Spy -from api.tests.common import createUsers -from database.models import ( - AboutUsPageSettings, Action, - ActionsPageSettings, ContactUsPageSettings, EventsPageSettings, ImpactPageSettings, TeamsPageSettings, TestimonialsPageSettings, Vendor, - Subdomain, - Event, - Community, - Menu, - Team, - TeamMember, - CommunityMember, - RealEstateUnit, - CommunityAdminGroup, - UserProfile, - Data, - TagCollection, - UserActionRel, - Data, - Location, - HomePageSettings, VendorsPageSettings, -) from _main_.utils.massenergize_errors import ( CustomMassenergizeError, InvalidResourceError, MassEnergizeAPIError, ) -from _main_.utils.context import Context +from api.tests.common import createUsers +from api.utils.api_utils import get_viable_menu_items +from database.models import Action, CarbonEquivalency, Community, CommunityAdminGroup, CommunityMember, Data, Event, \ + HomePageSettings, Location, Menu, RealEstateUnit, Subdomain, TagCollection, Team, TeamMember, UserActionRel, \ + UserProfile, Vendor from database.utils.common import json_loader -from database.models import CarbonEquivalency -from .utils import find_reu_community, get_community, split_location_string, check_location -from sentry_sdk import capture_message -from typing import Tuple - -from api.utils.api_utils import get_viable_menu_items, load_about_data, load_aboutus_data, load_actions_data, load_contactus_data, load_events_data, load_homepage_data, load_impact_data, load_one_action_data, load_one_event_data, load_one_team_data, load_one_testimonial_data, load_one_vendor_data, load_policies_data, load_profile_data, load_settings_data, load_teams_data, load_testimonials_data, load_vendors_data +from .utils import check_location, find_reu_community, get_community, split_location_string class MiscellaneousStore: @@ -496,65 +479,65 @@ def list_commonly_used_icons(self): print(str(key) + ": " + str(common_icons[key])) - def load_essential_initial_site_data(self, context, args): - try: - page = args.get("page", None) - subdonain = args.get("subdomain", None) - community_id = args.get("community_id", None) - id = args.get("id", None) - - if not subdonain and not community_id: - return None, CustomMassenergizeError("No community or subdomain provided") - - if not page: - return None, CustomMassenergizeError("No page provided") - - community, _ = get_community(community_id=community_id, subdomain=subdonain) - if not community: - return None, CustomMassenergizeError("Community not found") - - page_settings = { - "home_page_settings": HomePageSettings.objects.filter(community=community).first(), - "actions_page_settings": ActionsPageSettings.objects.filter(community=community).first(), - "events_page_settings": EventsPageSettings.objects.filter(community=community).first(), - "vendors_page_settings": VendorsPageSettings.objects.filter(community=community).first(), - "about_us_page_settings": AboutUsPageSettings.objects.filter(community=community).first(), - "testimonials_page_settings": TestimonialsPageSettings.objects.filter(community=community).first(), - "teams_page_settings": TeamsPageSettings.objects.filter(community=community).first(), - "contact_us_page_settings": ContactUsPageSettings.objects.filter(community=community).first(), - "impact_page_settings": ImpactPageSettings.objects.filter(community=community).first(), - } - - data = {} - - page_func_map = { - "homepage": load_homepage_data, - "actions": load_actions_data, - "events": load_events_data, - "vendors": load_vendors_data, - "about": load_about_data, - "testimonials": load_testimonials_data, - "one_vendor": load_one_vendor_data, - "impact": load_impact_data, - "aboutus": load_aboutus_data, - "contactus": load_contactus_data, - "teams": load_teams_data, - "one_team": load_one_team_data, - "one_action": load_one_action_data, - "one_event": load_one_event_data, - "one_testimonial": load_one_testimonial_data, - "profile": load_profile_data, - "settings": load_settings_data, - "policies": load_policies_data, - } - func = page_func_map.get(page) - if func: - data = func(context, args, community, id, page_settings) - - return data, None - except Exception as e: - return None, CustomMassenergizeError(e) - + # def load_essential_initial_site_data(self, context, args): + # try: + # page = args.get("page", None) + # subdonain = args.get("subdomain", None) + # community_id = args.get("community_id", None) + # id = args.get("id", None) + # + # if not subdonain and not community_id: + # return None, CustomMassenergizeError("No community or subdomain provided") + # + # if not page: + # return None, CustomMassenergizeError("No page provided") + # + # community, _ = get_community(community_id=community_id, subdomain=subdonain) + # if not community: + # return None, CustomMassenergizeError("Community not found") + # + # page_settings = { + # "home_page_settings": HomePageSettings.objects.filter(community=community).first(), + # "actions_page_settings": ActionsPageSettings.objects.filter(community=community).first(), + # "events_page_settings": EventsPageSettings.objects.filter(community=community).first(), + # "vendors_page_settings": VendorsPageSettings.objects.filter(community=community).first(), + # "about_us_page_settings": AboutUsPageSettings.objects.filter(community=community).first(), + # "testimonials_page_settings": TestimonialsPageSettings.objects.filter(community=community).first(), + # "teams_page_settings": TeamsPageSettings.objects.filter(community=community).first(), + # "contact_us_page_settings": ContactUsPageSettings.objects.filter(community=community).first(), + # "impact_page_settings": ImpactPageSettings.objects.filter(community=community).first(), + # } + # + # data = {} + # + # page_func_map = { + # "homepage": load_homepage_data, + # "actions": load_actions_data, + # "events": load_events_data, + # "vendors": load_vendors_data, + # "about": load_about_data, + # "testimonials": load_testimonials_data, + # "one_vendor": load_one_vendor_data, + # "impact": load_impact_data, + # "aboutus": load_aboutus_data, + # "contactus": load_contactus_data, + # "teams": load_teams_data, + # "one_team": load_one_team_data, + # "one_action": load_one_action_data, + # "one_event": load_one_event_data, + # "one_testimonial": load_one_testimonial_data, + # "profile": load_profile_data, + # "settings": load_settings_data, + # "policies": load_policies_data, + # } + # func = page_func_map.get(page) + # if func: + # data = func(context, args, community, id, page_settings) + # + # return data, None + # except Exception as e: + # return None, CustomMassenergizeError(e) + def load_menu_items(self, context, args): try: page = args.get("page", None) From c34976e40f6dc49e1a7dbe4ecd94dd80ef0d93a0 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 11:36:05 +0000 Subject: [PATCH 11/16] Remove obsolete site setup utilities The commit involves removing extensive site setup utility code from api_utils.py and other miscellaneous functions from misc.py and services/misc.py. This clean-up greatly simplifies the codebase, removing unused and outdated functions related to loading data for various community features and pages. --- src/api/services/misc.py | 8 -- src/api/store/misc.py | 60 ----------- src/api/utils/api_utils.py | 200 +------------------------------------ 3 files changed, 1 insertion(+), 267 deletions(-) diff --git a/src/api/services/misc.py b/src/api/services/misc.py index bf9642e08..bd8d54d31 100644 --- a/src/api/services/misc.py +++ b/src/api/services/misc.py @@ -142,14 +142,6 @@ def authenticateFrontendInTestMode(self, args): client = Client() return signinAs(client, user), None - - # def load_essential_initial_site_data(self,context, args): - # res, err = self.store.load_essential_initial_site_data(context,args) - # if err: - # return None, err - # - # return res, None - def load_menu_items(self,context, args): res, err = self.store.load_menu_items(context,args) if err: diff --git a/src/api/store/misc.py b/src/api/store/misc.py index 77f10dedb..75fc1fa8d 100644 --- a/src/api/store/misc.py +++ b/src/api/store/misc.py @@ -477,66 +477,6 @@ def list_commonly_used_icons(self): sorted_keys = sorted(common_icons, key=common_icons.get, reverse=True) for key in sorted_keys: print(str(key) + ": " + str(common_icons[key])) - - - # def load_essential_initial_site_data(self, context, args): - # try: - # page = args.get("page", None) - # subdonain = args.get("subdomain", None) - # community_id = args.get("community_id", None) - # id = args.get("id", None) - # - # if not subdonain and not community_id: - # return None, CustomMassenergizeError("No community or subdomain provided") - # - # if not page: - # return None, CustomMassenergizeError("No page provided") - # - # community, _ = get_community(community_id=community_id, subdomain=subdonain) - # if not community: - # return None, CustomMassenergizeError("Community not found") - # - # page_settings = { - # "home_page_settings": HomePageSettings.objects.filter(community=community).first(), - # "actions_page_settings": ActionsPageSettings.objects.filter(community=community).first(), - # "events_page_settings": EventsPageSettings.objects.filter(community=community).first(), - # "vendors_page_settings": VendorsPageSettings.objects.filter(community=community).first(), - # "about_us_page_settings": AboutUsPageSettings.objects.filter(community=community).first(), - # "testimonials_page_settings": TestimonialsPageSettings.objects.filter(community=community).first(), - # "teams_page_settings": TeamsPageSettings.objects.filter(community=community).first(), - # "contact_us_page_settings": ContactUsPageSettings.objects.filter(community=community).first(), - # "impact_page_settings": ImpactPageSettings.objects.filter(community=community).first(), - # } - # - # data = {} - # - # page_func_map = { - # "homepage": load_homepage_data, - # "actions": load_actions_data, - # "events": load_events_data, - # "vendors": load_vendors_data, - # "about": load_about_data, - # "testimonials": load_testimonials_data, - # "one_vendor": load_one_vendor_data, - # "impact": load_impact_data, - # "aboutus": load_aboutus_data, - # "contactus": load_contactus_data, - # "teams": load_teams_data, - # "one_team": load_one_team_data, - # "one_action": load_one_action_data, - # "one_event": load_one_event_data, - # "one_testimonial": load_one_testimonial_data, - # "profile": load_profile_data, - # "settings": load_settings_data, - # "policies": load_policies_data, - # } - # func = page_func_map.get(page) - # if func: - # data = func(context, args, community, id, page_settings) - # - # return data, None - # except Exception as e: - # return None, CustomMassenergizeError(e) def load_menu_items(self, context, args): try: diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index baeab06f4..30012a27f 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -189,202 +189,4 @@ def get_viable_menu_items(community): portal_footer_contact_info.simple_json() ] - -# .......................... site setup utils .......................... -def get_enabled_feature_flags_for_community(community): - feature_flags = FeatureFlag.objects.filter( - Q(audience=FeatureFlagConstants().for_everyone()) | - Q(audience=FeatureFlagConstants().for_specific_audience(), communities=community) | - (Q(audience=FeatureFlagConstants().for_all_except()) & ~Q(communities=community)) - ).exclude(expires_on__lt=datetime.now()).prefetch_related('communities') - - return feature_flags -def get_actions_for_community(community, context): - actions = Action.objects.select_related('image', 'community').prefetch_related('tags', 'vendors').filter(community=community) - if not context.is_sandbox: - if context.user_is_logged_in and not context.user_is_admin(): - actions = actions.filter(Q(user__id=context.user_id) | Q(is_published=True)) - else: - actions = actions.filter(is_published=True) - return actions.distinct() - - -def get_events_for_community(community, context): - today = datetime.now() - shared_months = 2 - hosted_months = 6 - earliest_shared = today - timedelta(weeks=4 * shared_months) - earliest_hosted = today - timedelta(weeks=4 * hosted_months) - - events = Event.objects.select_related('image', 'community').prefetch_related('tags', 'invited_communities').filter( - community__id=community.id, start_date_and_time__gte=earliest_hosted) - - shared = community.events_from_others.filter(is_published=True, start_date_and_time__gte=earliest_shared) - if not context.is_sandbox and events: - if context.user_is_logged_in and not context.user_is_admin(): - events = events.filter(Q(user__id=context.user_id) | Q(is_published=True)) - else: - events = events.filter(is_published=True) - all_events = [*events, *shared] - - return all_events - - -def get_teams_for_community(community): - pass - -def get_testimonials_for_community(community, context): - - testimonials = Testimonial.objects.filter( - community=community, is_deleted=False).prefetch_related('tags__tag_collection', 'action__tags', 'vendor', - 'community') - - if not context.is_sandbox: - if context.user_is_logged_in and not context.user_is_admin(): - testimonials = testimonials.filter(Q(user__id=context.user_id) | Q(is_published=True)) - else: - testimonials = testimonials.filter(is_published=True) - - return testimonials.distinct() - -def get_vendors_for_community(community, context): - vendors = community.community_vendors.filter(is_deleted=False) - - if not context.is_sandbox: - if context.user_is_logged_in and not context.user_is_admin(): - vendors = vendors.filter(Q(user__id=context.user_id) | Q(is_published=True)) - else: - vendors = vendors.filter(is_published=True) - return vendors.distinct() - -def get_tags_collections(): - return TagCollection.objects.filter(is_deleted=False) - -# .......................... site setup utils .......................... -def load_homepage_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["home_page_settings"].simple_json() if page_settings["home_page_settings"] else None - data["feature_flags"] = serialize_all(get_enabled_feature_flags_for_community(community)) - data["impact_page_settings"] = page_settings["impact_page_settings"].simple_json() if page_settings["impact_page_settings"] else None - return data - -def load_actions_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["actions_page_settings"].simple_json() if page_settings["actions_page_settings"] else None - data["actions"] = serialize_all(get_actions_for_community(community, context)) - data["tag_collections"] = serialize_all(get_tags_collections()) - return data - -def load_events_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None - data["events"] = serialize_all(get_events_for_community(community, context)) - data["tag_collections"] = serialize_all(get_tags_collections()) - return data - -def load_vendors_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["vendors_page_settings"].simple_json() if page_settings["vendors_page_settings"] else None - data["vendors"] = serialize_all(get_vendors_for_community(community, context)) - data["tag_collections"] = serialize_all(get_tags_collections()) - return data - -def load_about_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None - data["donate_page_settings"] = {} - return data - -def load_testimonials_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["testimonials_page_settings"].simple_json() if page_settings["testimonials_page_settings"] else None - data["testimonials"] = serialize_all(get_testimonials_for_community(community, context)) - data["tag_collections"] = serialize_all(get_tags_collections()) - return data - -def load_one_vendor_data(context, args, community, id, page_settings): - data = {} - vendor = Vendor.objects.filter(id=id).first() - data["vendor"] = vendor.simple_json() - data["testimonials"] = serialize_all(Testimonial.objects.filter(vendor=vendor)) - return data - -def load_impact_data(context, args, community, id, page_settings): - from api.store.graph import GraphStore - data = {} - completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) - communities_impact_graph, _ = GraphStore().graph_communities_impact(context, args) - data["tag_collections"] = serialize_all(get_tags_collections()) - data["graph_actions_completed"] = completed_action_graph_data - data["graph_communities_impact"] = communities_impact_graph - return data - -def load_aboutus_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["about_us_page_settings"].simple_json() if page_settings["about_us_page_settings"] else None - return data - -def load_contactus_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["contact_us_page_settings"].simple_json() if page_settings["contact_us_page_settings"] else None - return data - -def load_teams_data(context, args, community, id, page_settings): - from api.store.team import TeamStore - from api.store.graph import GraphStore - data = {} - completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) - teams_stats, _ = TeamStore().team_stats(context, args) - data["page_data"] = page_settings["teams_page_settings"].simple_json() if page_settings["teams_page_settings"] else None - data["graph_actions_completed"] = completed_action_graph_data - data["teams_stats"] = teams_stats - return data - -def load_one_team_data(context, args, community, id, page_settings): - from api.store.team import TeamStore - data = {} - teams_stats, _ = TeamStore().team_stats(context, args) - data["team"] = Team.objects.filter(id=id).first().simple_json() - data["teams_stats"] = teams_stats - return data - -def load_one_action_data(context, args, community, id, page_settings): - from api.store.graph import GraphStore - data = {} - completed_action_graph_data, err = GraphStore().graph_actions_completed(context, args) - data["graph_actions_completed"] = completed_action_graph_data - data["action"] = Action.objects.filter(id=id).first().simple_json() - return data - -def load_one_event_data(context, args, community, id, page_settings): - data = {} - data["page_data"] = page_settings["events_page_settings"].simple_json() if page_settings["events_page_settings"] else None - data["event"] = Event.objects.filter(id=id).first().simple_json() - return data - -def load_one_testimonial_data(context, args, community, id, page_settings): - data = {} - data["testimonial"] = Testimonial.objects.filter(id=id).first().simple_json() - return data - -def load_profile_data(context, args, community, id, page_settings): - from api.store.team import TeamStore - data = {} - teams_stats, _ = TeamStore().team_stats(context, args) - data["teams_stats"] = teams_stats - return data - -def load_settings_data(context, args, community, id, page_settings): - data = {} - if context.user_is_admin(): - data["preferences"] = UserPortalSettings.Preferences if args.get("subdomain") else AdminPortalSettings.Preferences - else: - data["preferences"] = UserPortalSettings.Preferences - return data - -def load_policies_data(context, args, community, id, page_settings): - data = {} - policies = Policy.objects.filter(is_deleted=False) - data["policies"] = policies - return data - +# -------------------------- Menu Utils -------------------------- \ No newline at end of file From 739bdf6c4ba5eef93e4758c93a2f7e91873e1d5d Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 11:37:42 +0000 Subject: [PATCH 12/16] Refactor validator to expect 'endpoints' instead of 'data' This change modifies the validator in misc.py to expect 'endpoints', rather than 'data'. This better reflects the actual intent of the function, improving code readability and maintainability. --- src/api/handlers/misc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index 86256142d..0ffcd3690 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -169,14 +169,14 @@ def load_essential_initial_site_data(self, request): self.validator.expect("community_id", is_required=False) self.validator.expect("subdomain", is_required=False) - self.validator.expect("data", 'str_list', is_required=True) + self.validator.expect("endpoints", 'str_list', is_required=True) self.validator.expect("id", is_required=False) args, err = self.validator.verify(args, strict=True) if err: return err - endpoints = args.pop("data", []) + endpoints = args.pop("endpoints", []) def fetch_data(endpoint): endpoint = request.build_absolute_uri(endpoint) From a091feebf23ac2774b8c9cfeb165842fc30cbe83 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 11:39:04 +0000 Subject: [PATCH 13/16] Remove unused PAGE_SETUP_ESSENTIALS constant The PAGE_SETUP_ESSENTIALS constant, which was found in the 'constants.py' file by the navigation path 'src/api', was unused and hence, has been removed. This cleanup contributes to overall code tidiness and efficiency. --- src/api/constants.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/api/constants.py b/src/api/constants.py index b7c565d9a..acd12799a 100644 --- a/src/api/constants.py +++ b/src/api/constants.py @@ -37,9 +37,3 @@ COMMUNITY_NOTIFICATION_TYPES = [USER_EVENTS_NUDGES_FF] -PAGE_SETUP_ESSENTIALS = { - "homepage":{ - - } -} - From 176bf15f216a41b1bb640ed0ef371d946d234e8a Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 12:24:05 +0000 Subject: [PATCH 14/16] Add cookies to post request in fetch_data This commit includes cookies in the data payload for the post requests in the fetch_data function. It retrieves cookies from the current request and sends them along with post call to ensure consistent user session management. --- src/api/handlers/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index 0ffcd3690..ed74615ab 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -180,7 +180,7 @@ def load_essential_initial_site_data(self, request): def fetch_data(endpoint): endpoint = request.build_absolute_uri(endpoint) - response = requests.post(endpoint,data=args) + response = requests.post(endpoint,data=args, cookies=request.COOKIES) return response.json() # Use a ThreadPoolExecutor to make requests to all endpoints concurrently From adfd82f6eaaa16f46b7b5c1484e3472312e328c5 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Thu, 25 Apr 2024 14:04:56 +0000 Subject: [PATCH 15/16] Add donation page settings to community API The community API now fetches and provides the visibility status of the donation page. This extra property for donation_page_settings is included in the same manner as the settings for other pages like testimonial page, vendors page, and others. --- src/api/utils/api_utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/api/utils/api_utils.py b/src/api/utils/api_utils.py index 30012a27f..3fc63f7a6 100644 --- a/src/api/utils/api_utils.py +++ b/src/api/utils/api_utils.py @@ -157,6 +157,8 @@ def get_viable_menu_items(community): teams_page_settings = TeamsPageSettings.objects.filter(community=community).first() testimonial_page_settings = TestimonialsPageSettings.objects.filter(community=community).first() vendors_page_settings = VendorsPageSettings.objects.filter(community=community).first() + donation_page_settings = DonationPageSettings.objects.filter(community=community).first() + menu_items = {} all_menu = Menu.objects.all() @@ -171,7 +173,8 @@ def get_viable_menu_items(community): "services": vendors_page_settings.is_published, "testimonials": testimonial_page_settings.is_published, "teams": teams_page_settings.is_published, - "events": events_page_settings.is_published + "events": events_page_settings.is_published, + "donate": donation_page_settings.is_published }, community.subdomain) footer_menu_content = all_menu.get(name='PortalFooterQuickLinks') From cfa40c3a4f56ab78668cefb5596f67bae5430a75 Mon Sep 17 00:00:00 2001 From: Tahiru Abdullai Date: Wed, 1 May 2024 19:26:53 +0000 Subject: [PATCH 16/16] Add TODO comment to create new endpoint in misc.py Increased code readability by including a TODO comment in misc.py, which highlights the need to revert a change and create a new endpoint for user.portal.menu.load. --- src/api/handlers/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/handlers/misc.py b/src/api/handlers/misc.py index ed74615ab..38c4901f8 100644 --- a/src/api/handlers/misc.py +++ b/src/api/handlers/misc.py @@ -20,7 +20,7 @@ def __init__(self): def registerRoutes(self) -> None: self.add("/menus.remake", self.remake_navigation_menu) - self.add("/menus.list", self.load_menu_items) + self.add("/menus.list", self.load_menu_items) #TODO: revert and create new enpoint eg user.portal.menu.load self.add("/data.backfill", self.backfill) self.add("/data.carbonEquivalency.create", self.create_carbon_equivalency) self.add("/data.carbonEquivalency.update", self.update_carbon_equivalency)