Coverage for app/backend/src/couchers/servicers/resources.py: 100%
28 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-23 04:10 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-23 04:10 +0000
1import functools
2import logging
4import babel
5from google.protobuf import empty_pb2
6from sqlalchemy.orm import Session
8from couchers.context import CouchersContext
9from couchers.i18n.localize import try_localize_language_name_from_iso639, try_localize_region_name_from_iso3166
10from couchers.proto import resources_pb2, resources_pb2_grpc
11from couchers.resources import get_badge_dict, get_icon, get_language_dict, get_region_dict, get_terms_of_service
13logger = logging.getLogger(__name__)
15COMMUNITY_GUIDELINES = [
16 {"key": "be_kind", "icon": "hand_with_heart.svg"},
17 {"key": "no_dating", "icon": "ghost.svg"},
18 {"key": "be_safe", "icon": "shield.svg"},
19 {"key": "no_money", "icon": "handshake.svg"},
20]
23# These responses are static per locale, and localizing ~300 names costs tens of ms of CPU (mostly
24# babel.Locale.parse), so cache the built protos. Sharing one message across requests is safe as
25# long as nothing mutates responses after return (the tracing interceptor deepcopies before
26# sanitizing).
27@functools.lru_cache
28def _get_regions_res(locale: babel.Locale) -> resources_pb2.GetRegionsRes:
29 return resources_pb2.GetRegionsRes(
30 regions=[
31 resources_pb2.Region(
32 alpha3=alpha3,
33 name=try_localize_region_name_from_iso3166(alpha3, locale) or english_name,
34 )
35 for alpha3, english_name in sorted(get_region_dict().items())
36 ]
37 )
40@functools.lru_cache
41def _get_languages_res(locale: babel.Locale) -> resources_pb2.GetLanguagesRes:
42 return resources_pb2.GetLanguagesRes(
43 languages=[
44 resources_pb2.Language(
45 code=code,
46 name=try_localize_language_name_from_iso639(code, locale, standalone=True) or english_name,
47 )
48 for code, english_name in sorted(get_language_dict().items())
49 ]
50 )
53class Resources(resources_pb2_grpc.ResourcesServicer):
54 def GetTermsOfService(
55 self, request: empty_pb2.Empty, context: CouchersContext, session: Session
56 ) -> resources_pb2.GetTermsOfServiceRes:
57 return resources_pb2.GetTermsOfServiceRes(terms_of_service=get_terms_of_service())
59 def GetCommunityGuidelines(
60 self, request: empty_pb2.Empty, context: CouchersContext, session: Session
61 ) -> resources_pb2.GetCommunityGuidelinesRes:
62 return resources_pb2.GetCommunityGuidelinesRes(
63 community_guidelines=[
64 resources_pb2.CommunityGuideline(
65 title=context.localization.localize_string(f"community_guidelines.{cg['key']}_title"),
66 guideline=context.localization.localize_string(f"community_guidelines.{cg['key']}_guideline"),
67 icon_svg=get_icon(cg["icon"]),
68 )
69 for cg in COMMUNITY_GUIDELINES
70 ]
71 )
73 def GetRegions(
74 self, request: empty_pb2.Empty, context: CouchersContext, session: Session
75 ) -> resources_pb2.GetRegionsRes:
76 return _get_regions_res(context.localization.babel_locale)
78 def GetLanguages(
79 self, request: empty_pb2.Empty, context: CouchersContext, session: Session
80 ) -> resources_pb2.GetLanguagesRes:
81 return _get_languages_res(context.localization.babel_locale)
83 def GetBadges(
84 self, request: empty_pb2.Empty, context: CouchersContext, session: Session
85 ) -> resources_pb2.GetBadgesRes:
86 return resources_pb2.GetBadgesRes(
87 badges=[
88 resources_pb2.Badge(
89 id=badge.id,
90 name=context.localization.localize_string(f"badges.{badge.id}_name"),
91 description=context.localization.localize_string(f"badges.{badge.id}_description"),
92 color=badge.color,
93 )
94 for badge in get_badge_dict().values()
95 ]
96 )