Coverage for src/couchers/resources.py: 94%
69 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-29 01:26 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-12-29 01:26 +0000
1import functools
2import json
3import logging
4from pathlib import Path
6from sqlalchemy.sql import delete, text
8from couchers.config import config
9from couchers.db import session_scope
10from couchers.models import Language, Region, TimezoneArea
11from couchers.sql import couchers_select as select
12from proto import resources_pb2
14logger = logging.getLogger(__name__)
16resources_folder = Path(__file__).parent / ".." / ".." / "resources"
19@functools.cache
20def get_terms_of_service():
21 """
22 Get the latest terms of service
23 """
24 with open(resources_folder / "terms_of_service.md", "r") as f:
25 return f.read()
28@functools.cache
29def get_community_guidelines():
30 """
31 Get the latest Community Guidelines
32 """
33 with open(resources_folder / "community_guidelines.json", "r") as f:
34 community_guidelines = json.load(f)
35 ret = []
36 for cg in community_guidelines:
37 with open(resources_folder / "icons" / cg["icon"], "r") as f:
38 ret.append(
39 resources_pb2.CommunityGuideline(
40 title=cg["title"],
41 guideline=cg["guideline"],
42 icon_svg=f.read(),
43 )
44 )
45 return ret
48@functools.cache
49def get_region_dict():
50 """
51 Get list of allowed regions as a dictionary of {alpha3: name}.
52 """
53 with session_scope() as session:
54 return {region.code: region.name for region in session.execute(select(Region)).scalars().all()}
57def region_is_allowed(code):
58 """
59 Check a region code is valid
60 """
61 return code in get_region_dict()
64@functools.cache
65def get_language_dict():
66 """
67 Get list of allowed languages as a dictionary of {code: name}.
68 """
69 with session_scope() as session:
70 return {language.code: language.name for language in session.execute(select(Language)).scalars().all()}
73@functools.cache
74def get_badge_data():
75 """
76 Get list of profile badges in form {id: Badge}
77 """
78 with open(resources_folder / "badges.json", "r") as f:
79 return json.load(f)
82@functools.cache
83def get_badge_dict():
84 """
85 Get list of profile badges in form {id: Badge}
86 """
87 return {badge["id"]: badge for badge in get_badge_data()["badges"]}
90@functools.cache
91def get_static_badge_dict():
92 """
93 Get list of static badges in form {id: list(user_ids)}
94 """
95 return get_badge_data()["static_badges"]
98def language_is_allowed(code):
99 """
100 Check a language code is valid
101 """
102 return code in get_language_dict()
105def copy_resources_to_database(session):
106 """
107 Syncs the source-of-truth data from files into the database. Call this at the end of a migration.
109 Foreign key constraints that refer to resource tables need to be set to DEFERRABLE.
111 We sync as follows:
113 1. Lock the table to be updated fully
114 2. Defer all constraints
115 3. Truncate the table
116 4. Re-insert everything
118 Truncating and recreating guarantees the data is fully in sync.
119 """
120 with open(resources_folder / "regions.json", "r") as f:
121 regions = [(region["alpha3"], region["name"]) for region in json.load(f)]
123 with open(resources_folder / "languages.json", "r") as f:
124 languages = [(language["code"], language["name"]) for language in json.load(f)]
126 timezone_areas_file = resources_folder / "timezone_areas.sql"
128 if not timezone_areas_file.exists():
129 if not config["DEV"]:
130 raise Exception("Missing timezone_areas.sql and not running in dev")
132 timezone_areas_file = resources_folder / "timezone_areas.sql-fake"
133 logger.info("Using fake timezone areas")
135 with open(timezone_areas_file, "r") as f:
136 tz_sql = f.read()
138 # set all constraints marked as DEFERRABLE to be checked at the end of this transaction, not immediately
139 session.execute(text("SET CONSTRAINTS ALL DEFERRED"))
141 session.execute(delete(Region))
142 for code, name in regions:
143 session.add(Region(code=code, name=name))
145 session.execute(delete(Language))
146 for code, name in languages:
147 session.add(Language(code=code, name=name))
149 session.execute(delete(TimezoneArea))
150 session.execute(text(tz_sql))