Coverage for functions \ flipdare \ constants.py: 100%

0 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# confidential and copyrighted material. Unauthorised copying, 

6# modification, distribution, or use of this file is strictly 

7# prohibited without prior written permission from Flipdare Pty Ltd. 

8# 

9# This software includes third-party components licensed under MIT, 

10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

11# 

12 

13# pragma: no cover 

14 

15 

16import sys 

17from pathlib import Path 

18from typing import Any, Final 

19 

20from flipdare.app_log import LOG 

21from flipdare.generated.shared.model.core.stopwatch_duration import StopwatchDuration 

22from flipdare.generated.shared.stripe.stripe_country_code import StripeCountryCode 

23from flipdare.generated.shared.stripe.stripe_currency_code import StripeCurrencyCode 

24 

25 

26from flipdare.payments.payment_types import StripeRegionFee 

27 

28__all__ = [ 

29 "IS_DEBUG", 

30 "IS_TRACE", 

31 "NO_COLLECTION", 

32 "ADMIN_DOC_ID", 

33 "DEF_OK_MESSAGE", 

34 "API_TOKEN_PREFIX", 

35 "BEARER_TOKEN_PREFIX", 

36 "EMAIL_LOG_SUBJECT_MAX_LENGTH", 

37 "EMAIL_MAX_LINE_LENGTH", 

38 "EMAIL_STACK_LINE_MAX_LENGTH", 

39 "TIMESTAMP_KEY_SUFFIX", 

40 "MAX_SUSPICION_SCORE", 

41 "REMOTE_CONFIG_KEYS", 

42 "COMPANY_SHORT_NAME", 

43 "COMPANY_SHORT_LINK", 

44 "COMPANY_LINK", 

45 "COMPANY_TERMS", 

46 "COMPANY_REFUND", 

47 "COMPANY_LOG_MED", 

48 "COMPANY_LONG", 

49 "COMPANY_TAGLINE", 

50 "COMPANY_ADDRESS", 

51 "APP_VERSION", 

52 "FFMPEG_COMMAND", 

53 "NO_DOC_ID", 

54 "DEF_TZ_STRING", 

55 "SLUG_CODE_LENGTH", 

56 "MAX_ERROR_STRING_LENGTH", 

57 "NOT_APPLICABLE", 

58 "DELETED_USER_MIN_ID_LENGTH", 

59 "DELETED_USER_NAME_PREFIX", 

60 "DELETED_USER_EMAIL_DOMAIN", 

61 "DELETED_USER_PREFIX", 

62 "DEFAULT_USER_AVATAR_PATH", 

63 "DEFAULT_GROUP_AVATAR_PATH", 

64 "EMAIL_TEMPLATE_DIR", 

65 "ADMIN_TEMPLATE_SUB_DIR", 

66 "USER_TEMPLATE_SUB_DIR", 

67 "MAX_SUMMARY_EMAIL_ENTRIES", 

68 "MAX_SUMMARY_EMAIL_ROW_ENTRIES", 

69 "INTERNAL_ADMIN_EMAIL", 

70 "DEF_NO_REPLY_EMAIL", 

71 "DEF_NO_REPLY_LABEL", 

72 "DEF_MAIL_FROM", 

73 "DEF_MAIL_FROM_LABEL", 

74 "DEF_MAIL_SUPPORT", 

75 "DEF_MAIL_SUPPORT_LABEL", 

76 "MAIL_UNSUBSCRIBE", 

77 "MAIL_UNSUBSCRIBE_NAME", 

78 "FIREBASE_TMP_RECEIPT_PATH", 

79 "FIREBASE_TMP_EMAIL_PATH", 

80 "FIREBASE_PUBLIC_EMAIL_TMPL", 

81 "FIREBASE_RECEIPT_RETENTION_DAYS", 

82 "FIREBASE_EMAIL_RETENTION_DAYS", 

83 "CURRENCY_API_TIMEOUT", 

84 "BASE_APP_BACKEND_LINK", 

85 "IOS_STORE_URL", 

86 "ANDROID_STORE_URL", 

87 "DEEP_LINK_BASE", 

88 "DEFERRED_DEEP_LINK_URL", 

89 "FALLBACK_WEB_URL", 

90 "BACKEND_BASE_LINK", 

91 "REPORT_JOB_STATS_DAYS", 

92 "RPORT_LOG_STATS_DAYS", 

93 "REPORT_CHARGE_STATS_DAYS", 

94 "REPORT_ERROR_STATS_DAYS", 

95 "JOB_WAIT_SANITY_SECONDS", 

96 "EMAIL_ERROR_COUNT", 

97 "MIN_ANALYSIS_COUNT", 

98 "DEF_ERROR_SCORE", 

99 "SMTP_STATUS_OK", 

100 "FIRESTORE_DOC_ID_LENGTH", 

101 "STATS_Z_SCORE_THRESHOLD", 

102 "STATS_IQR_MULTIPLIER", 

103 "EXCHANGE_RATE_URL", 

104 "EXCHANGE_RATE_HEADERS", 

105 "EXCHANGE_RATE_BASE_CURRENCY", 

106 "DEFAULT_CHANGE_SCORE", 

107 "TRIGGER_CHANGE_SCORE_THRESHOLD", 

108 "HIGH_TRANSACTION_AMOUNT", 

109 "HIGH_RISK_SCORE", 

110 "MEDIUM_RISK_SCORE", 

111 "MEDIUM_LOW_RISK_SCORE", 

112 "RISK_ANALYSIS_CHARGE_BACK_GRACE_CT", 

113 "DEF_RETRIEVAL_WINDOW_HOURS", 

114 "RESTRICTION_MAX_WAIT_ADMIN_ACK_FLAG", 

115 "RESTRICTION_REMINDER_USER_RESPONSE_FLAG", 

116 "RESTRICTION_MAX_WAIT_USER_RESPONSE_FLAG", 

117 "CHAT_REVIEW_REPUTATION_THRESHOLD", 

118 "CHAT_REJECT_REPUTATION_THRESHOLD", 

119 "DEF_REPUTATION_CACHE_SIZE", 

120 "DEF_REPUTATION", 

121 "REP_DECAY_EQUILLIBRIUM", 

122 "REP_HIGH_RISK_THRESHOLD", 

123 "REP_LOW_RISK_THRESHOLD", 

124 "REP_REVIEW_PENALTY", 

125 "REP_REVIEW_THRESHOLD", 

126 "REP_VIOLATION_PENALTY", 

127 "REP_CLEAN_REWARD", 

128 "REP_DECAY_RATE", 

129 "REP_PENALTY_GROWTH_RATE", 

130 "MOD_CONFIDENCE_THRESHOLD", 

131 "MOD_SHARPNESS", 

132 "MOD_MIN_AGREEMENT_CATEGORIES", 

133 "MOD_WEIGHTED_FLAGGED_SCORE", 

134 "MOD_WEIGHTED_REVIEW_SCORE", 

135 "APPROX_INFINITE", 

136 "MAX_SEARCH_TAG_COUNT", 

137 "MAX_SEARCH_RESULTS", 

138 "MAX_SEARCH_RESULTS_PER_PAGE", 

139 "TYPESENSE_HARD_PER_PAGE_LIMIT", 

140 "MAX_SEARCH_FRIENDS_JOIN", 

141 "MAX_SEARCH_GROUP_MEMBERS", 

142 "SEARCH_CACHE_TTL_SECONDS", 

143 "SEARCH_CACHE_MAX_SIZE", 

144 "MIN_SEARCH_TYPOS", 

145 "MIN_SEARCH_AUTOCOMPLETE_TYPOS", 

146 "MAX_SEARCH_AUTOCOMPLETE_RESULTS_PER_PAGE", 

147 "MAX_SEARCH_AUTOCOMPLETE_RESULTS", 

148 "MAX_DB_CURSOR_LIMIT", 

149 "RUNTIME_DB_REFRESH_INTERVAL", 

150 "DEF_SIGNED_URL_EXPIRATION", 

151 "VOTING_DURATION", 

152 "PAYMENT_PERIOD_HRS", 

153 "GRAPH_LOG_SCALE_THRESHOLD", 

154 "THOMPSON_SAMPLING_ITERATIONS", 

155 "THOMPSON_THRESHOLD", 

156 "THOMPSON_CONFIDENCE", 

157 "KEMENY_MIN_VOTES", 

158 "KEMENY_MIN_PERCENT_PENDING", 

159 "KEMENY_APPROVAL_THRESHOLD", 

160 "KEMENY_REJECTION_THRESHOLD", 

161 "KEMENY_PENDING_WEIGHT", 

162 "WILSON_CONFIDENCE_INTERVAL", 

163 "SPRT_FALSE_POSITIVE_RATE", 

164 "SPRT_FALSE_NEGATIVE_RATE", 

165 "SPRT_MIN_VOTES", 

166 "SPRT_SHOULD_REJECT_PROBABILITY", 

167 "SPRT_SHOULD_APPROVE_PROBABILITY", 

168 "BAYESIAN_PRIOR_ALPHA", 

169 "BAYESIAN_PRIOR_BETA", 

170 "BAYESIAN_MAJORITY_THRESHOLD", 

171 "BAYESIAN_MINORITY_THRESHOLD", 

172 "BAYESIAN_CDF_THRESHOLD", 

173 "STRIPE_SUPPORT_EMAIL", 

174 "STRIPE_COMPANY_LINK", 

175 "STRIPE_DISPLAY_NAME_TMPL", 

176 "STRIPE_DOING_BUSINESS_AS_TMPL", 

177 "STRIPE_PRODUCT_DESC_TMPL", 

178 "STRIPE_DEFAULT_MCC", 

179 "STRIPE_DISPUTE_URL", 

180 "STRIPE_DASHBOARD_URL", 

181 "STRIPE_CLIENT_NETWORK_RETRIES", 

182 "STRIPE_VERSION", 

183 "STRIPE_STATEMENT_DESCRIPTOR", 

184 "STRIPE_STATEMENT_PREFIX", 

185 "STRIPE_FALLBACK_PREFIX_STR", 

186 "STRIPE_FALLBACK_LAST_STR", 

187 "DEFAULT_REAUTHORIZATION_DAYS_CUSTOMER", 

188 "STRIPE_MAX_STATEMENT_LENGTH", 

189 "STRIPE_STATEMENT_EXCLUSION_CHARS", 

190 "STRIPE_INVOICE_PREFIX_LENGTH", 

191 "STRIPE_BASE_FEES", 

192 "STRIPE_FEE_MAPPING", 

193 "DEF_CURRENCY_CODE", 

194 "DEF_COUNTRY_CODE", 

195 "PAYMENT_MAX_RETRIES", 

196 "PAYMENT_MAX_INFO_RETRIES", 

197] 

198 

199 

200# misc, need here to avoid circular imports 

201def _get_system_root_for(sub_directory: str | Path) -> Path: 

202 if sys.platform == "win32": 

203 # Returns 'C:\' or 'D:\' based on where the script is running 

204 root = Path().cwd().resolve().anchor 

205 return Path(root) / sub_directory 

206 else: 

207 # On Unix, Path("/") is the explicit absolute root 

208 return Path("/").resolve() / sub_directory 

209 

210 

211IS_DEBUG = LOG().is_debug 

212IS_TRACE = LOG().is_trace or LOG().is_debug 

213 

214NO_COLLECTION = "NoCollection" 

215ADMIN_DOC_ID = "__ADMIN_ADMIN__ADMIN_ADMIN__" # needs to be 28 chars 

216 

217DEF_OK_MESSAGE = "Ok" 

218 

219API_TOKEN_PREFIX = "Basic" # noqa: S105 

220BEARER_TOKEN_PREFIX = "Bearer" # noqa: S105 

221 

222EMAIL_LOG_SUBJECT_MAX_LENGTH = 40 

223EMAIL_MAX_LINE_LENGTH = 500 

224 

225EMAIL_STACK_LINE_MAX_LENGTH = 500 

226 

227TIMESTAMP_KEY_SUFFIX = ["_at", "At"] 

228 

229# ----------------------------------------------------------------------------- 

230# forms 

231# ----------------------------------------------------------------------------- 

232 

233# this is the field name we expect for the honeypot in our requests, can be changed as needed 

234HONEYPOT_NAME: Final = "address" 

235MAX_SUSPICION_SCORE = 2 # the max score a request can get before we consider it suspicious 

236 

237# ----------------------------------------------------------------------------- 

238# app 

239# ----------------------------------------------------------------------------- 

240 

241REMOTE_CONFIG_KEYS = ["job_config"] 

242 

243COMPANY_SHORT_NAME = "flipdare" 

244COMPANY_SUPPORT_URL = "https://flipdare.com/support" 

245 

246COMPANY_SHORT_LINK = "flipdare.com" 

247COMPANY_LINK = f"https://{COMPANY_SHORT_LINK}" 

248COMPANY_TERMS = f"https://{COMPANY_SHORT_LINK}/terms" 

249COMPANY_REFUND = f"https://{COMPANY_SHORT_LINK}/refunds" 

250 

251# RELEASE: update hosting for the logo. 

252COMPANY_LOG_MED = Path(__file__).parent / "config" / "logo" / "logo-512x512.png" 

253 

254# RELEASE: update address to US LLC address for tax purposes. 

255COMPANY_LONG = "Flipdare Inc." 

256COMPANY_TAGLINE = "flipdare.com - You game?" 

257 

258 

259COMPANY_ADDRESS = f"""{COMPANY_LONG} 

2601234, Business Rd 

261Sydney, NSW 

2622000, Australia""" 

263 

264 

265APP_VERSION = "1.0.0" 

266FFMPEG_COMMAND = "ffmpeg" 

267NO_DOC_ID = "NoDocID" 

268DEF_TZ_STRING = "UTC" 

269 

270SLUG_CODE_LENGTH = 14 

271 

272MAX_ERROR_STRING_LENGTH = 150 

273NOT_APPLICABLE = "N/A" 

274 

275DELETED_USER_MIN_ID_LENGTH = 8 

276DELETED_USER_NAME_PREFIX = "Former" 

277DELETED_USER_EMAIL_DOMAIN = "deleted.flipdare.com" 

278DELETED_USER_PREFIX = "N/A" 

279 

280DEFAULT_USER_AVATAR_PATH = "content/user/" 

281DEFAULT_GROUP_AVATAR_PATH = "content/group/" 

282 

283# paths 

284JOB_CONFIG_PATH: Path = Path(__file__).parent / "config" / "job_config.yaml" 

285DEFAULT_AVATAR_CONFIG_PATH: Path = Path(__file__).parent / "config" / "default_avatar.yaml" 

286 

287DOWNLOAD_FILE_DIR: Path = _get_system_root_for("tmp") 

288 

289EMAIL_TEMPLATE_DIR = Path(__file__).parent / "config" / "email_template" 

290ADMIN_TEMPLATE_SUB_DIR = "admin" 

291USER_TEMPLATE_SUB_DIR = "user" 

292 

293# email 

294MAX_SUMMARY_EMAIL_ENTRIES = 12 

295MAX_SUMMARY_EMAIL_ROW_ENTRIES = 6 

296 

297INTERNAL_ADMIN_EMAIL = "team@flipdare.com" 

298 

299DEF_NO_REPLY_EMAIL = "no-reply@flipdare.com" 

300DEF_NO_REPLY_LABEL = "team flipdare" 

301 

302DEF_MAIL_FROM = "admin@flipdare.com" 

303DEF_MAIL_FROM_LABEL = "flipdare admin" 

304 

305DEF_MAIL_SUPPORT = "support@flipdare.com" 

306DEF_MAIL_SUPPORT_LABEL = "Cartman from flipdare" 

307 

308MAIL_UNSUBSCRIBE = DEF_MAIL_SUPPORT 

309MAIL_UNSUBSCRIBE_NAME = DEF_MAIL_SUPPORT_LABEL 

310 

311# these are temp public paths for files that 

312# are cleaned up after a certain amount of time 

313FIREBASE_TMP_RECEIPT_PATH = "public-receipts" 

314FIREBASE_TMP_EMAIL_PATH = "public_email" 

315 

316FIREBASE_PUBLIC_EMAIL_TMPL = ( 

317 "https://firebasestorage.googleapis.com/v0/b/$BUCKET_NAME/o/$SAFE_PATH?alt=media&token=$TOKEN" 

318) 

319 

320FIREBASE_RECEIPT_RETENTION_DAYS = 90 

321FIREBASE_EMAIL_RETENTION_DAYS = 180 

322 

323# timeouts 

324CURRENCY_API_TIMEOUT = 60 # seconds 

325 

326# ----------------------------------------------------------------------------- 

327# links/deep links 

328# ----------------------------------------------------------------------------- 

329BASE_APP_BACKEND_LINK = "https://localhost:8000/" 

330 

331IOS_STORE_URL = "https://apps.apple.com/app/idYOUR_APP_ID" 

332ANDROID_STORE_URL = "https://play.google.com/store/apps/details?id=com.flipdare.app" 

333 

334DEEP_LINK_BASE = "https://flipdare.com/l" 

335DEFERRED_DEEP_LINK_URL = "https://chottu.link" 

336 

337FALLBACK_WEB_URL = "https://flipdare.com" 

338 

339 

340# this is for AppBackendLink, which creates 

341# backend links for admin/dashboard use... 

342# NOTE: this needs to be set to LOCALHOST for the time being. 

343BACKEND_BASE_LINK = "https://localhost:8000/" 

344 

345# ----------------------------------------------------------------------------- 

346# reporting/jobs 

347# ----------------------------------------------------------------------------- 

348 

349REPORT_JOB_STATS_DAYS = 1 

350RPORT_LOG_STATS_DAYS = 1 

351REPORT_CHARGE_STATS_DAYS = 3 

352REPORT_ERROR_STATS_DAYS = 7 

353 

354# for jobs 

355JOB_WAIT_SANITY_SECONDS = 15 * 60 # 15 minutes 

356EMAIL_ERROR_COUNT = 10 

357 

358# for reporting 

359MIN_ANALYSIS_COUNT = 2 

360DEF_ERROR_SCORE = 1.0 

361 

362# misc 

363SMTP_STATUS_OK = 250 

364 

365FIRESTORE_DOC_ID_LENGTH = 128 

366 

367STATS_Z_SCORE_THRESHOLD = 2 

368STATS_IQR_MULTIPLIER = 1.5 

369 

370# ----------------------------------------------------------------------------- 

371# exchange rates 

372# ----------------------------------------------------------------------------- 

373EXCHANGE_RATE_URL = "https://openexchangerates.org/api/latest.json?app_id=__API_KEY__&base=USD&prettyprint=false&show_alternative=false" 

374 

375EXCHANGE_RATE_HEADERS = {"accept": "application/json"} 

376 

377EXCHANGE_RATE_BASE_CURRENCY = StripeCurrencyCode.USD 

378 

379# ----------------------------------------------------------------------------- 

380# stats 

381# ----------------------------------------------------------------------------- 

382 

383# NOTE: the lower the sensitivity, the more likely a change will trigger a 

384# NOTE: return a change score close to 1.0, 

385# NOTE: which would trigger a job to update the model in the database. 

386# Configuration: (sensitivity, is_currency) 

387# Lower sensitivity = High Priority (triggers faster) 

388DEFAULT_CHANGE_SCORE = 1.0 

389TRIGGER_CHANGE_SCORE_THRESHOLD = 0.5 

390 

391LOW_PRI_SENSITIVITY: float = 2.0 

392MED_PRI_SENSITIVITY: float = 1.0 

393HIGH_PRI_SENSITIVITY: float = 0.1 

394 

395# Cents changes need to be much larger to trigger than count changes 

396CENTS_PRIORITY_BUFFER: float = 40.0 

397 

398STATS_SENSITIVITY: dict[str, dict[str, tuple[float, bool]]] = { 

399 "view_stats": { 

400 "bookmarks": (HIGH_PRI_SENSITIVITY, False), # High priority count 

401 "flags": (LOW_PRI_SENSITIVITY, False), # Low priority count 

402 "likes": (HIGH_PRI_SENSITIVITY, False), # High priority count 

403 "dislikes": (MED_PRI_SENSITIVITY, False), # Medium priority count 

404 "views": (LOW_PRI_SENSITIVITY, False), # Low priority count 

405 }, 

406 "dare_stats": { 

407 "backers": (HIGH_PRI_SENSITIVITY, False), # High priority count 

408 "flags": (LOW_PRI_SENSITIVITY, False), # Low priority count 

409 "requested": (MED_PRI_SENSITIVITY, False), # Medium priority count 

410 "accepted": (HIGH_PRI_SENSITIVITY, False), # High priority count 

411 "completed": (HIGH_PRI_SENSITIVITY, False), # High priority count 

412 "earnings": (MED_PRI_SENSITIVITY, True), # Currency (less sensitive) 

413 }, 

414 "pledge_stats": { 

415 # NOTE:, since cents can be large numbers, we want to use a lower sensitivity for 

416 # NOTE: changes in cents, and a higher sensitivity for changes in count 

417 "earned.count": (HIGH_PRI_SENSITIVITY, False), # High priority count 

418 "earned.cents": (MED_PRI_SENSITIVITY, True), # Currency (less sensitive) 

419 "accepted.count": (HIGH_PRI_SENSITIVITY, False), # High priority count 

420 "accepted.cents": (MED_PRI_SENSITIVITY, True), # Currency (less sensitive) 

421 "pending.count": (MED_PRI_SENSITIVITY, False), # Medium priority count 

422 "pending.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

423 "disputed.count": (LOW_PRI_SENSITIVITY, False), # Low priority count 

424 "disputed.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

425 "refunded.count": (LOW_PRI_SENSITIVITY, False), # Low priority count 

426 "refunded.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

427 "rejected.count": (LOW_PRI_SENSITIVITY, False), # Low priority count 

428 "rejected.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

429 "cancelled.count": (LOW_PRI_SENSITIVITY, False), # Low priority count 

430 "cancelled.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

431 "error.count": (LOW_PRI_SENSITIVITY, False), # Low priority count 

432 "error.cents": (LOW_PRI_SENSITIVITY, True), # Currency (less sensitive) 

433 }, 

434} 

435 

436# ----------------------------------------------------------------------------- 

437# RISK 

438# ----------------------------------------------------------------------------- 

439 

440# $250.00 in cents, transactions above this amount are considered high risk 

441HIGH_TRANSACTION_AMOUNT = 250_00 

442HIGH_RISK_SCORE = 85 

443MEDIUM_RISK_SCORE = 50 

444MEDIUM_LOW_RISK_SCORE = 30 

445 

446# ----------------------------------------------------------------------------- 

447# risk analysis 

448# ----------------------------------------------------------------------------- 

449 

450RISK_ANALYSIS_CHARGE_BACK_GRACE_CT = 2 

451 

452# ----------------------------------------------------------------------------- 

453# safety 

454# ----------------------------------------------------------------------------- 

455 

456# note, use 2 hours to account for any time sync issues 

457# note, since this should be run every hour, 4 hours is safe 

458DEF_RETRIEVAL_WINDOW_HOURS = 4 

459RESTRICTION_MAX_WAIT_ADMIN_ACK_FLAG = 3 # days 

460 

461RESTRICTION_REMINDER_USER_RESPONSE_FLAG = 3 # days 

462RESTRICTION_MAX_WAIT_USER_RESPONSE_FLAG = 7 # days 

463 

464CHAT_REVIEW_REPUTATION_THRESHOLD = -20 

465CHAT_REJECT_REPUTATION_THRESHOLD = 0 

466 

467DEF_REPUTATION_CACHE_SIZE = 10000 

468DEF_REPUTATION = 50 

469 

470REP_DECAY_EQUILLIBRIUM = 50 

471REP_HIGH_RISK_THRESHOLD = 30 

472REP_LOW_RISK_THRESHOLD = 70 

473REP_REVIEW_PENALTY = -5 

474REP_REVIEW_THRESHOLD = 20 

475REP_VIOLATION_PENALTY = -20 

476REP_CLEAN_REWARD = 1 

477REP_DECAY_RATE = 0.9 

478REP_PENALTY_GROWTH_RATE = 0.1 

479 

480REP_DEFAULT_MULTIPLIERS: dict[str, float] = { 

481 "text": 1.0, 

482 "image": 1.5, # Images slightly higher risk 

483 "video": 1.2, 

484 "profile": 2.0, # Profile content analyzed more 

485} 

486 

487 

488MOD_CONFIDENCE_THRESHOLD = 0.7 

489MOD_SHARPNESS = 2.5 

490MOD_MIN_AGREEMENT_CATEGORIES = 2 

491MOD_WEIGHTED_FLAGGED_SCORE = 0.35 

492MOD_WEIGHTED_REVIEW_SCORE = 0.15 

493 

494 

495APPROX_INFINITE = 1e-10 

496 

497# ----------------------------------------------------------------------------- 

498# search 

499# ----------------------------------------------------------------------------- 

500 

501 

502# this is used in tag generation to limit the amount of tags 

503# generated for groups.. 

504MAX_SEARCH_TAG_COUNT = 10000 

505 

506# the max number of results to return for any query. 

507# typesense is optimized for 250, but we 

508# allow +250 in case the user wants more! 

509MAX_SEARCH_RESULTS = 500 

510 

511# per page, dont want to high because its a bit of processing... 

512MAX_SEARCH_RESULTS_PER_PAGE = 50 

513 

514# this is only required for friend unions. 

515TYPESENSE_HARD_PER_PAGE_LIMIT = 250 # Typesense hard limit 

516# this is only required for friend unions. 

517MAX_SEARCH_FRIENDS_JOIN = 1000 

518 

519# this limits the number of friend relationships we create for group members. 

520MAX_SEARCH_GROUP_MEMBERS = 500 

521 

522# Default cache settings: 5 min TTL, max 1000 entries 

523SEARCH_CACHE_TTL_SECONDS = 300 

524SEARCH_CACHE_MAX_SIZE = 1000 

525 

526MIN_SEARCH_TYPOS = 1 

527MIN_SEARCH_AUTOCOMPLETE_TYPOS = 2 

528 

529MAX_SEARCH_AUTOCOMPLETE_RESULTS_PER_PAGE = 15 

530MAX_SEARCH_AUTOCOMPLETE_RESULTS = 50 

531 

532# database 

533MAX_DB_CURSOR_LIMIT = 1000 

534 

535RUNTIME_DB_REFRESH_INTERVAL = 5 * 60 # seconds (5 minutes) 

536DEF_SIGNED_URL_EXPIRATION = 7 * 24 * 60 * 60 # 7 days 

537 

538 

539SEARCH_NAT_LANG_CONFIG: dict[str, Any] = { 

540 "id": "gemini-flash", 

541 "model_name": "google/gemini-2.5-flash", 

542 "api_key": "__API_KEY__", 

543 "max_bytes": 16000, 

544 "temperature": 0.0, 

545 "system_prompt": "Optional custom system prompt to append to the one that Typesense generates based on your dataset", 

546} 

547 

548 

549# ----------------------------------------------------------------------------- 

550# VOTING 

551# ----------------------------------------------------------------------------- 

552 

553 

554VOTING_DURATION = StopwatchDuration.THREE_DAYS 

555PAYMENT_PERIOD_HRS = 168 # hours / 7 days 

556 

557# 

558# plotting 

559# 

560GRAPH_LOG_SCALE_THRESHOLD = 2500.0 

561# 

562# Thompson Sampling parameters 

563# 

564THOMPSON_SAMPLING_ITERATIONS = 10000 

565THOMPSON_THRESHOLD = 0.5 

566THOMPSON_CONFIDENCE = 0.90 

567# 

568# Kemeny-Young parameters 

569# 

570KEMENY_MIN_VOTES = 3 

571KEMENY_MIN_PERCENT_PENDING = 0.25 

572KEMENY_APPROVAL_THRESHOLD = 0.67 

573KEMENY_REJECTION_THRESHOLD = 0.33 

574KEMENY_PENDING_WEIGHT = 0.45 

575# 

576# Wilson Score Interval parameters 

577# 

578WILSON_CONFIDENCE_INTERVAL = 0.95 

579 

580# 

581# Sequential Probability Ratio Test (SPRT) parameters 

582# 

583# 5% chance of wrong approval 

584SPRT_FALSE_POSITIVE_RATE = 0.05 

585# 5% chance of wrong rejection 

586SPRT_FALSE_NEGATIVE_RATE = 0.05 

587# Minumum votes required for SPRT to make a decision 

588SPRT_MIN_VOTES = 5 

589# Hypotheses: p0 = 0.4 (should reject), p1 = 0.6 (should approve) 

590SPRT_SHOULD_REJECT_PROBABILITY = 0.4 

591SPRT_SHOULD_APPROVE_PROBABILITY = 0.6 

592 

593 

594# 

595# Bayesian Average parameters 

596# 

597BAYESIAN_PRIOR_ALPHA = 1.0 

598BAYESIAN_PRIOR_BETA = 1.0 

599# Very confident it will pass 

600BAYESIAN_MAJORITY_THRESHOLD = 0.95 

601# Very confident it will fail 

602BAYESIAN_MINORITY_THRESHOLD = 0.06 

603# Probability that approval rate > 0.45 

604# NOTE: Using 0.45 threshold to account for pending votes 

605BAYESIAN_CDF_THRESHOLD = 0.45 

606 

607# ----------------------------------------------------------------------------- 

608# stripe 

609# ----------------------------------------------------------------------------- 

610 

611STRIPE_SUPPORT_EMAIL = "stripe-support@flipdare.com" 

612STRIPE_COMPANY_LINK = COMPANY_LINK 

613STRIPE_DISPLAY_NAME_TMPL = "FLIP - $NAME" 

614STRIPE_DOING_BUSINESS_AS_TMPL = "$NAME from flipdare" 

615STRIPE_PRODUCT_DESC_TMPL = "content by $NAME on flipdare.com" 

616STRIPE_DEFAULT_MCC = "4899" # "Cable and Other Pay Television Services" 

617 

618STRIPE_DISPUTE_URL = "https://dashboard.stripe.com/disputes/{dispute_id}" 

619STRIPE_DASHBOARD_URL = "https://dashboard.stripe.com/login" 

620 

621STRIPE_CLIENT_NETWORK_RETRIES = 3 

622STRIPE_VERSION = "2026-01-28.clover" 

623 

624STRIPE_STATEMENT_DESCRIPTOR = "FLIPDARE" # Your marketplace brand 

625STRIPE_STATEMENT_PREFIX = "FLIP" # Shorter version for cards 

626 

627STRIPE_FALLBACK_PREFIX_STR = "User" 

628STRIPE_FALLBACK_LAST_STR = "X" 

629 

630# default reauth period for Customer-Initiated Transaction authorization 

631# technically 7 days, but we use 6 to ensure they are reauthorized before the 7 day limit, 

632# to avoid any issues with auth expiring before capture. 

633DEFAULT_REAUTHORIZATION_DAYS_CUSTOMER = 6 

634 

635# 

636# Character limits 

637# Ref: https://stripe.com/docs/payments/payment-intents 

638# statement_descriptor 22 

639# Statement descriptors are limited to 22 characters, can't use the special characters 

640# <, >, ', ", or *, and must not consist solely of numbers. 

641# When using dynamic statement descriptors, 

642# the dynamic text is appended to the statement descriptor prefix set in the Stripe Dashboard. 

643# An asterisk (*) and an empty space are also added to separate the default statement descriptor 

644# from the dynamic portion. 

645# These 2 characters count towards the 22 character limit. 

646STRIPE_MAX_STATEMENT_LENGTH = 22 

647STRIPE_STATEMENT_EXCLUSION_CHARS = {"<", ">", "'", '"', "*"} 

648 

649# The prefix for the customer used to generate unique invoice numbers. 

650# Must be 3-12 uppercase letters or numbers. 

651# NOTE: be careful changing this, as it can cause collisions in invoice numbers 

652# NOTE: which would cause charges to fail. 

653# NOTE: if changed, you will need to check the db, for the invoice Id stored in stripe settings, 

654# NOTE: otherwise you will get conflicts! 

655STRIPE_INVOICE_PREFIX_LENGTH = 12 

656 

657 

658# FEES 

659STRIPE_BASE_FEES = { 

660 # Rates: [Percentage, Fixed, Int'l Surcharge, Currency Surcharge] 

661 "US": StripeRegionFee(0.029, 0.30, 0.015, 0.01), 

662 "EU": StripeRegionFee(0.015, 0.25, 0.0175, 0.02), 

663 "UK": StripeRegionFee(0.015, 0.20, 0.0175, 0.02), 

664 "AU": StripeRegionFee(0.017, 0.30, 0.018, 0.02), 

665 "CA": StripeRegionFee(0.029, 0.30, 0.008, 0.02), 

666 "SG": StripeRegionFee(0.034, 0.50, 0.01, 0.02), 

667 "JP": StripeRegionFee(0.036, 0.00, 0.00, 0.02), 

668} 

669 

670STRIPE_FEE_MAPPING = { 

671 StripeCurrencyCode.USD: "US", 

672 StripeCurrencyCode.AUD: "AU", 

673 StripeCurrencyCode.CAD: "CA", 

674 StripeCurrencyCode.GBP: "UK", 

675 StripeCurrencyCode.JPY: "JP", 

676 StripeCurrencyCode.SGD: "SG", 

677 StripeCurrencyCode.EUR: "EU", 

678 StripeCurrencyCode.BGN: "EU", 

679 StripeCurrencyCode.CZK: "EU", 

680 StripeCurrencyCode.DKK: "EU", 

681 StripeCurrencyCode.HUF: "EU", 

682 StripeCurrencyCode.PLN: "EU", 

683 StripeCurrencyCode.RON: "EU", 

684 StripeCurrencyCode.SEK: "EU", 

685} 

686 

687# ----------------------------------------------------------------------------- 

688# payments 

689# ----------------------------------------------------------------------------- 

690 

691DEF_CURRENCY_CODE = StripeCurrencyCode.USD 

692DEF_COUNTRY_CODE = StripeCountryCode.US 

693 

694# the number of times we retry processing before stopping. 

695PAYMENT_MAX_RETRIES = 10 # for any status. 

696PAYMENT_MAX_INFO_RETRIES = 14 # approx 14 days. (for additional info)