Coverage for portality / settings.py: 99%

297 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-04 09:41 +0100

1""" 

2~~AppSettings:Config~~ 

3""" 

4import os 

5from portality import constants 

6from portality.lib import paths 

7from datetime import datetime 

8 

9########################################### 

10# Application Version information 

11# ~~->API:Feature~~ 

12 

13DOAJ_VERSION = "8.6.2" 

14API_VERSION = "4.0.1" 

15 

16###################################### 

17# Deployment configuration 

18 

19HOST = '0.0.0.0' 

20DEBUG = False 

21DEBUG_DEV_LOG = False # show all log of each module 

22PORT = 5004 

23SSL = True 

24VALID_ENVIRONMENTS = ['dev', 'test', 'staging', 'production', 'harvester'] 

25CMS_BUILD_ASSETS_ON_STARTUP = False 

26# Cookies security 

27SESSION_COOKIE_SAMESITE = 'Strict' 

28SESSION_COOKIE_SECURE = True 

29REMEMBER_COOKIE_SECURE = True 

30 

31#################################### 

32# Testdrive for setting up the test environment. 

33# CAUTION - this can modify the index so should NEVER be used in production! 

34TESTDRIVE_ENABLED = False 

35 

36#################################### 

37# Debug Mode 

38 

39# PyCharm debug settings 

40DEBUG_PYCHARM = False # do not try to connect to the PyCharm debugger by default 

41DEBUG_PYCHARM_SERVER = 'localhost' 

42DEBUG_PYCHARM_PORT = 6000 

43 

44# ~~->DebugToolbar:Framework~~ 

45DEBUG_TB_TEMPLATE_EDITOR_ENABLED = True 

46DEBUG_TB_INTERCEPT_REDIRECTS = False 

47 

48# set to True to enable the env list panel in the debug toolbar 

49DEBUG_TB_ENV_LIST_ENABLED = False 

50 

51####################################### 

52# Elasticsearch configuration 

53# ~~->Elasticsearch:Technology 

54 

55# elasticsearch settings # TODO: changing from single host / esprit to multi host on ES & correct the default 

56ELASTICSEARCH_HOSTS = [{'host': 'localhost', 'port': 9200}, {'host': 'localhost', 'port': 9201}] 

57ELASTIC_SEARCH_VERIFY_CERTS = True # Verify the SSL certificate of the ES host. Set to False in dev.cfg to avoid having to configure your local certificates 

58 

59# 2 sets of elasticsearch DB settings - index-per-project and index-per-type. Keep both for now so we can migrate. 

60# e.g. host:port/index/type/id 

61ELASTIC_SEARCH_DB = "doaj" 

62ELASTIC_SEARCH_TEST_DB = "doajtest" 

63 

64# e.g. host:port/type/doc/id 

65ELASTIC_SEARCH_INDEX_PER_TYPE = True 

66INDEX_PER_TYPE_SUBSTITUTE = '_doc' # Migrated from esprit 

67ELASTIC_SEARCH_DB_PREFIX = "doaj-" # note: include the separator 

68ELASTIC_SEARCH_TEST_DB_PREFIX = "doajtest-" 

69 

70INITIALISE_INDEX = True # whether or not to try creating the index and required index types on startup 

71ELASTIC_SEARCH_SNAPSHOT_REPOSITORY = None 

72ELASTIC_SEARCH_SNAPSHOT_TTL = 366 

73 

74ES_TERMS_LIMIT = 1024 

75ELASTICSEARCH_REQ_TIMEOUT = 20 # Seconds - used in core.py for whole ES connection request timeout 

76ES_READ_TIMEOUT = '2m' # Minutes - used in DAO for searches 

77 

78##################################################### 

79# Elastic APM config (MUST be configured in env file) 

80# ~~->APM:Feature~~ 

81 

82ENABLE_APM = False 

83 

84ELASTIC_APM = { 

85 # Set required service name. Allowed characters: 

86 # a-z, A-Z, 0-9, -, _, and space 

87 'SERVICE_NAME': '', 

88 

89 # Use if APM Server requires a token 

90 'SECRET_TOKEN': '', 

91 

92 # Set custom APM Server URL (default: http://localhost:8200) 

93 'SERVER_URL': '', 

94} 

95 

96########################################### 

97# Event handler 

98 

99# Process events immediately/synchronously 

100EVENT_SEND_FUNCTION = "portality.events.background.send_event" 

101 

102########################################### 

103# Read Only Mode 

104# 

105# Use these options to place the application into READ ONLY mode 

106 

107# This puts the UI into READ_ONLY mode 

108# ~~->ReadOnlyMode:Feature~~ 

109READ_ONLY_MODE = False 

110 

111# This puts the cron jobs into READ_ONLY mode 

112SCRIPTS_READ_ONLY_MODE = False 

113 

114########################################### 

115# Feature Toggles 

116 

117# ~~->OfflineMode:Feature~~ 

118OFFLINE_MODE = False 

119 

120# List the features we want to be active (API v1 and v2 remain with redirects to v3) 

121# ~~->API:Feature~~ 

122FEATURES = ['api1', 'api2', 'api3', 'api4'] 

123VALID_FEATURES = ['api1', 'api2', 'api3', 'api4'] 

124 

125######################################## 

126# File Path and URL Path settings 

127 

128# root of the git repo 

129ROOT_DIR = paths.rel2abs(__file__, "..") 

130 

131# base path, to the directory where this settings file lives 

132BASE_FILE_PATH = paths.abs_dir_path(__file__) 

133 

134BASE_URL = "https://doaj.org" 

135if BASE_URL.startswith('https://'): 

136 BASE_DOMAIN = BASE_URL[8:] 

137elif BASE_URL.startswith('http://'): 

138 BASE_DOMAIN = BASE_URL[7:] 

139else: 

140 BASE_DOMAIN = BASE_URL 

141 

142# ~~->API:Feature~~ 

143BASE_API_URL = "https://doaj.org/api/" 

144API_CURRENT_BLUEPRINT_NAME = "api_v4" # change if upgrading API to new version and creating new view 

145CURRENT_API_MAJOR_VERSION = "4" 

146 

147# URL used for the journal ToC URL in the journal CSV export 

148# NOTE: must be the correct route as configured in view/doaj.py 

149# ~~->ToC:WebRoute~~ 

150JOURNAL_TOC_URL_FRAG = BASE_URL + '/toc/' 

151 

152# Used when generating external links, e.g. in the API docs 

153PREFERRED_URL_SCHEME = 'https' 

154 

155# Whether the app is running behind a proxy, for generating URLs based on X-Forwarded-Proto 

156# ~~->ProxyFix:Framework~~ 

157PROXIED = False 

158 

159# directory to upload files to. MUST be full absolute path 

160# The default takes the directory above this, and then down in to "upload" 

161UPLOAD_DIR = os.path.join(ROOT_DIR, "upload") 

162UPLOAD_ASYNC_DIR = os.path.join(ROOT_DIR, "upload_async") 

163FAILED_ARTICLE_DIR = os.path.join(ROOT_DIR, "failed_articles") 

164 

165# directory where reports are output 

166REPORTS_BASE_DIR = "/home/cloo/reports/" 

167 

168################################## 

169# File store 

170# ~~->FileStore:Feature~~ 

171 

172# put this in your production.cfg, to store everything on S3: 

173# STORE_IMPL = "portality.store.StoreS3" 

174 

175STORE_IMPL = "portality.store.StoreLocal" 

176STORE_SCOPE_IMPL = { 

177 # Enable this by scope in order to have different scopes store via different storage implementations 

178 # constants.STORE__SCOPE__PUBLIC_DATA_DUMP: "portality.store.StoreS3", 

179 # constants.STORE__SCOPE__JOURNAL_CSV: "portality.store.StoreS3" 

180} 

181 

182STORE_TMP_IMPL = "portality.store.TempStore" 

183 

184STORE_LOCAL_DIR = paths.rel2abs(__file__, "..", "local_store", "main") 

185STORE_TMP_DIR = paths.rel2abs(__file__, "..", "local_store", "tmp") 

186STORE_LOCAL_EXPOSE = False # if you want to allow files in the local store to be exposed under /store/<path> urls. For dev only. 

187STORE_LOCAL_WRITE_BUFFER_SIZE = 16777216 

188STORE_TMP_WRITE_BUFFER_SIZE = 16777216 

189 

190# containers (buckets in AWS) where various content will be stored 

191# These values are placeholders, and must be overridden in live deployment 

192# this prevents test environments from accidentally writing to the production buckets 

193STORE_ANON_DATA_CONTAINER = "doaj-anon-data-placeholder" 

194STORE_CACHE_CONTAINER = "doaj-data-cache-placeholder" 

195STORE_PUBLIC_DATA_DUMP_CONTAINER = "doaj-data-dump-placeholder" 

196STORE_HARVESTER_CONTAINER = "doaj-harvester" 

197STORE_EXPORT_CONTAINER = "doaj-export-placeholder" 

198STORE_JOURNAL_CSV_CONTAINER = "doaj-journal-csv-placeholder" 

199 

200# S3 credentials for relevant scopes 

201# ~~->S3:Technology~~ 

202STORE_S3_SCOPES = { 

203 "anon_data": { 

204 "aws_access_key_id": "put this in your dev/test/production.cfg", 

205 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

206 }, 

207 "cache": { 

208 "aws_access_key_id": "put this in your dev/test/production.cfg", 

209 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

210 }, 

211 # Used by the api_export script to dump data from the api 

212 constants.STORE__SCOPE__PUBLIC_DATA_DUMP: { 

213 "aws_access_key_id": "put this in your dev/test/production.cfg", 

214 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

215 }, 

216 # Used to store harvester run logs to S3 

217 "harvester": { 

218 "aws_access_key_id": "put this in your dev/test/production.cfg", 

219 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

220 }, 

221 # Used to store the admin-generated CSV reports 

222 "export": { 

223 "aws_access_key_id": "put this in your dev/test/production.cfg", 

224 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

225 }, 

226 constants.STORE__SCOPE__JOURNAL_CSV: { 

227 "aws_access_key_id": "put this in your dev/test/production.cfg", 

228 "aws_secret_access_key": "put this in your dev/test/production.cfg" 

229 } 

230} 

231 

232STORE_S3_MULTIPART_THRESHOLD = 5 * 1024 ** 3 # 5GB 

233 

234#################################### 

235# CMS configuration 

236 

237# paths where static content should be served from. 

238# * in the order you want them searched 

239# * relative to the portality directory 

240# ~~->CMS:DataStore~~ 

241STATIC_PATHS = [ 

242 "static", 

243 "../cms/assets" 

244] 

245 

246# GitHub base url where static content can be edited by the DOAJ team (you can leave out the trailing slash) 

247# ~~->GitHub:ExternalService~~ 

248CMS_EDIT_BASE_URL = "https://github.com/DOAJ/doaj/edit/static_pages/cms" 

249 

250# Where static files are served from - in case we need to serve a file 

251# from there ourselves using Flask instead of nginx (e.g. to support a 

252# legacy route to that file) 

253# Changing this will not change the actual folder that Flask serves 

254# static files from. 

255# http://flask.pocoo.org/snippets/102/ 

256STATIC_DIR = os.path.join(ROOT_DIR, "portality", "static") 

257 

258###################################### 

259# Service Descriptive Text 

260 

261SERVICE_NAME = "Directory of Open Access Journals" 

262 

263################################### 

264# Cookie settings 

265 

266# make this something secret in your overriding app.cfg 

267# ~~->Cookies:Feature~~ 

268SECRET_KEY = "default-key" 

269 

270# Consent Cookie and other Top-Level dismissable notes 

271# ~~->ConsentCookie:Feature~~ 

272CONSENT_COOKIE_KEY = "doaj-cookie-consent" 

273 

274# site notes, which can be configured to run any time with any content 

275# ~~-> SiteNote:Feature~~ 

276SITE_NOTE_ACTIVE = False 

277SITE_NOTE_KEY = "doaj-site-note" 

278SITE_NOTE_SLEEP = 259200 # every 3 days 

279SITE_NOTE_COOKIE_VALUE = "You have seen our most recent site wide announcement" 

280 

281#################################### 

282# Authorisation settings 

283# ~~->AuthNZ:Feature~~ 

284 

285# Can people register publicly? If false, only the superuser can create new accounts 

286PUBLIC_REGISTER = True 

287 

288SUPER_USER_ROLE = "admin" 

289 

290LOGIN_VIA_ACCOUNT_ID = True 

291 

292# amount of time a reset token is valid for (86400 is 24 hours) 

293PASSWORD_RESET_TIMEOUT = 86400 

294# amount of time a reset token for a new account is valid for 

295PASSWORD_CREATE_TIMEOUT = PASSWORD_RESET_TIMEOUT * 14 

296 

297# "api" top-level role is added to all accounts on creation; it can be revoked per account by removal of the role. 

298TOP_LEVEL_ROLES = [ 

299 "admin", 

300 "publisher", 

301 "editor", 

302 "associate_editor", 

303 "api", 

304 "ultra_bulk_delete", 

305 "preservation", 

306 constants.ROLE_PUBLIC_DATA_DUMP, 

307 constants.ROLE_PUBLISHER_JOURNAL_CSV, 

308 constants.ROLE_ADMIN_REPORT_WITH_NOTES, 

309 constants.ROLE_PREMIUM, 

310 constants.ROLE_PREMIUM_OAI, 

311 constants.ROLE_PREMIUM_PDD, 

312 constants.ROLE_PREMIUM_CSV 

313] 

314 

315ROLE_MAP = { 

316 "editor": [ 

317 "associate_editor", # note, these don't cascade, so we still need to list all the low-level roles 

318 "edit_journal", 

319 "edit_suggestion", 

320 "edit_application", # todo: switchover from suggestion to application 

321 "editor_area", 

322 "assign_to_associate", 

323 "list_group_journals", 

324 "list_group_suggestions", 

325 "read_notifications" 

326 ], 

327 "associate_editor": [ 

328 "edit_journal", 

329 "edit_suggestion", 

330 "editor_area", 

331 "read_notifications" 

332 ], 

333 constants.ROLE_PREMIUM: [ 

334 constants.ROLE_PREMIUM_OAI, 

335 constants.ROLE_PREMIUM_PDD, 

336 constants.ROLE_PREMIUM_CSV 

337 ] 

338} 

339 

340# If you change the reserved usernames, your data will likely need a migration to remove their 

341# existing use in the system. 

342SYSTEM_USERNAME = "system" 

343RESERVED_USERNAMES = [SYSTEM_USERNAME] # do not allow the creation of user accounts with this id 

344 

345# Role map to destination route on login (when no other destination page is present) 

346# checked in order, if the user has the role in the first tuple position, they will 

347# be redirected to the endpoint in the second tuple position 

348ROLE_LOGIN_DESTINATIONS = [ 

349 ("admin", "dashboard.top_todo"), 

350 ("editor", "editor.index"), 

351 ("associate_editor", "editor.index"), 

352 ("publisher", "publisher.index") 

353] 

354 

355# if the user doesn't have one of the above roles, where should they be sent after login 

356DEFAULT_LOGIN_DESTINATION = "doaj.home" 

357 

358#################################### 

359# Email Settings 

360# ~~->Email:ExternalService 

361 

362# Settings for Flask-Mail. Set in app.cfg 

363MAIL_SERVER = None # default localhost 

364MAIL_PORT = 25 # default 25 

365#MAIL_USE_TLS # default False 

366#MAIL_USE_SSL # default False 

367#MAIL_DEBUG # default app.debug 

368#MAIL_USERNAME # default None 

369#MAIL_PASSWORD # default None 

370#MAIL_DEFAULT_SENDER # default None 

371#MAIL_MAX_EMAILS # default None 

372#MAIL_SUPPRESS_SEND # default app.testing 

373 

374ENABLE_EMAIL = True 

375ENABLE_PUBLISHER_EMAIL = True 

376 

377ADMIN_NAME = "DOAJ" 

378ADMIN_EMAIL = "sysadmin@cottagelabs.com" 

379ADMINS = ["steve@cottagelabs.com", "mark@cottagelabs.com"] 

380 

381MANAGING_EDITOR_EMAIL = "managing-editors@doaj.org" 

382CONTACT_FORM_ADDRESS = "feedback+contactform@doaj.org" 

383SCRIPT_TAG_DETECTED_EMAIL_RECIPIENTS = ["helpdesk@doaj.org"] 

384 

385SYSTEM_EMAIL_FROM = 'helpdesk@doaj.org' 

386CC_ALL_EMAILS_TO = SYSTEM_EMAIL_FROM # DOAJ may get a dedicated inbox in the future 

387 

388# Error logging via email 

389SUPPRESS_ERROR_EMAILS = False 

390ERROR_LOGGING_EMAIL = 'doaj.internal@gmail.com' 

391ERROR_MAIL_HOSTNAME = 'smtp.mailgun.org' 

392ERROR_MAIL_USERNAME = None 

393ERROR_MAIL_PASSWORD = None 

394 

395# Reports email recipient 

396REPORTS_EMAIL_TO = ["helpdesk@doaj.org"] 

397 

398######################################## 

399# workflow email notification settings 

400# ~~->WorkflowNotifications:Feature~~ 

401 

402MAN_ED_IDLE_WEEKS = 4 # weeks before an application is considered reminder-worthy 

403ED_IDLE_WEEKS = 3 # weeks before the editor is warned about idle applications in their group 

404ASSOC_ED_IDLE_DAYS = 10 

405ASSOC_ED_IDLE_WEEKS = 3 

406 

407# Which statuses the notification queries should be filtered to show 

408# ~~-> ApplicationStatuses:Config~~ 

409MAN_ED_NOTIFICATION_STATUSES = [ 

410 constants.APPLICATION_STATUS_PENDING, 

411 constants.APPLICATION_STATUS_IN_PROGRESS, constants.APPLICATION_STATUS_COMPLETED, 

412 constants.APPLICATION_STATUS_ON_HOLD 

413] 

414ED_NOTIFICATION_STATUSES = [ 

415 constants.APPLICATION_STATUS_PENDING, 

416 constants.APPLICATION_STATUS_IN_PROGRESS, 

417 constants.APPLICATION_STATUS_COMPLETED 

418] 

419ASSOC_ED_NOTIFICATION_STATUSES = [ 

420 constants.APPLICATION_STATUS_PENDING, 

421 constants.APPLICATION_STATUS_IN_PROGRESS 

422] 

423 

424################################### 

425# status endpoint configuration 

426# ~~->StatusEndpoint:Feature~~ 

427 

428# /status endpoint connection to all app machines 

429APP_MACHINES_INTERNAL_IPS = [HOST + ':' + str(PORT)] # This should be set in production.cfg (or dev.cfg etc) 

430 

431########################################### 

432# Background Jobs settings 

433# ~~->Huey:Technology~~ 

434# ~~->Redis:Technology~~ 

435# ~~->BackgroundTasks:Feature~~ 

436 

437# huey/redis settings 

438REDIS_HOST = os.getenv('REDIS_HOST', '127.0.0.1') 

439REDIS_PORT = os.getenv('REDIS_PORT', 6379) 

440HUEY_IMMEDIATE = False 

441HUEY_ASYNC_DELAY = 10 

442 

443# Crontab for never running a job - February 31st (use to disable tasks) 

444CRON_NEVER = {"month": "2", "day": "31", "day_of_week": "*", "hour": "*", "minute": "*"} 

445 

446# Crontab schedules must be for unique times to avoid delays due to perceived race conditions 

447HUEY_SCHEDULE = { 

448 "sitemap": {"month": "*", "day": "*", "day_of_week": "*", "hour": "8", "minute": "0"}, 

449 "reporting": {"month": "*", "day": "1", "day_of_week": "*", "hour": "0", "minute": "0"}, 

450 "journal_csv": {"month": "*", "day": "*", "day_of_week": "*", "hour": "*", "minute": "20"}, 

451 "read_news": {"month": "*", "day": "*", "day_of_week": "*", "hour": "*", "minute": "30"}, 

452 "article_cleanup_sync": {"month": "*", "day": "2", "day_of_week": "*", "hour": "0", "minute": "0"}, 

453 "async_workflow_notifications": {"month": "*", "day": "*", "day_of_week": "1", "hour": "5", "minute": "0"}, 

454 "request_es_backup": {"month": "*", "day": "*", "day_of_week": "*", "hour": "6", "minute": "0"}, 

455 "check_latest_es_backup": {"month": "*", "day": "*", "day_of_week": "*", "hour": "9", "minute": "0"}, 

456 "prune_es_backups": {"month": "*", "day": "*", "day_of_week": "*", "hour": "9", "minute": "15"}, 

457 "public_data_dump": {"month": "*", "day": "*", "day_of_week": "*", "hour": "10", "minute": "0"}, 

458 "harvest": {"month": "*", "day": "*", "day_of_week": "*", "hour": "5", "minute": "30"}, 

459 "anon_export": {"month": "*", "day": "10", "day_of_week": "*", "hour": "1", "minute": "10"}, 

460 "old_data_cleanup": {"month": "*", "day": "12", "day_of_week": "*", "hour": "6", "minute": "30"}, 

461 "monitor_bgjobs": {"month": "*", "day": "*/6", "day_of_week": "*", "hour": "10", "minute": "0"}, 

462 "find_discontinued_soon": {"month": "*", "day": "*", "day_of_week": "*", "hour": "0", "minute": "3"}, 

463 "datalog_journal_added_update": {"month": "*", "day": "*", "day_of_week": "*", "hour": "4", "minute": "30"}, 

464 "auto_assign_editor_group_data": {"month": "*", "day": "*/7", "day_of_week": "*", "hour": "3", "minute": "30"}, 

465 "ris_export": {"month": "*", "day": "15", "day_of_week": "*", "hour": "3", "minute": "30"}, 

466 "site_statistics": {"month": "*", "day": "*", "day_of_week": "*", "hour": "*", "minute": "40"}, 

467} 

468 

469 

470HUEY_TASKS = { 

471 "ingest_articles": {"retries": 10, "retry_delay": 15}, 

472 "preserve": {"retries": 0, "retry_delay": 15}, 

473 "application_autochecks": {"retries": 0, "retry_delay": 15}, 

474 "journal_autochecks": {"retries": 0, "retry_delay": 15} 

475} 

476 

477#################################### 

478# publisher area settings 

479 

480# the earliest date accepted on the publisher's 'enter article metadata' form. 

481# code will default to 15 years before current year if commented out. 

482# ~~->ArticleMetadata:Page~~ 

483METADATA_START_YEAR = 1960 

484 

485# tick (on toc) and doaj seal settings 

486# ~~->Tick:Feature~~ 

487TICK_THRESHOLD = '2014-03-19T00:00:00Z' 

488 

489# ~~->UpdateRequests:Feature~~ 

490UPDATE_REQUESTS_SHOW_OLDEST = "2018-01-01T00:00:00Z" 

491 

492############################################## 

493# Elasticsearch Mappings 

494# ~~->Elasticsearch:Technology~~ 

495 

496FACET_FIELD = ".exact" 

497 

498# an array of DAO classes from which to retrieve the type-specific ES mappings 

499# to be loaded into the index during initialisation. 

500ELASTIC_SEARCH_MAPPINGS = [ 

501 "portality.models.Article", # ~~->Article:Model~~ 

502 "portality.models.Journal", # ~~->Journal:Model~~ 

503 "portality.models.Application", # ~~->Application:Model~~ 

504 "portality.models.DraftApplication", # ~~-> DraftApplication:Model~~ 

505 "portality.models.harvester.HarvestState", # ~~->HarvestState:Model~~ 

506 "portality.models.background.BackgroundJob", # ~~-> BackgroundJob:Model~~ 

507 "portality.models.autocheck.Autocheck", # ~~-> Autocheck:Model~~ 

508 "portality.models.export.Export", # ~~-> Export:Model~~ 

509 "portality.models.DataDump", # ~~-> DataDump:Model~~ 

510 "portality.models.JournalCSV", # ~~-> JournalCSV:Model~~ 

511 "portality.models.ur_review_route.URReviewRoute", # ~~-> URReviewRoute:Model~~ 

512 "portality.models.admin_alert.AdminAlert", # ~~-> AdminAlert:Model~~ 

513 "portality.models.ris_export.RISExport", 

514] 

515 

516# Map from dataobj coercion declarations to ES mappings 

517# ~~->DataObj:Library~~ 

518# ~~->Seamless:Library~~ 

519DATAOBJ_TO_MAPPING_DEFAULTS = { 

520 "unicode": { 

521 "type": "text", 

522 "fields": { 

523 "exact": { 

524 "type": "keyword", 

525 # "index": False, 

526 "store": True 

527 } 

528 } 

529 }, 

530 "str": { 

531 "type": "text", 

532 "fields": { 

533 "exact": { 

534 "type": "keyword", 

535 # "index": False, 

536 "store": True 

537 } 

538 } 

539 }, 

540 "unicode_upper": { 

541 "type": "text", 

542 "fields": { 

543 "exact": { 

544 "type": "keyword", 

545 # "index": False, 

546 "store": True 

547 } 

548 } 

549 }, 

550 "unicode_lower": { 

551 "type": "text", 

552 "fields": { 

553 "exact": { 

554 "type": "keyword", 

555 # "index": False, 

556 "store": True 

557 } 

558 } 

559 }, 

560 "isolang": { 

561 "type": "text", 

562 "fields": { 

563 "exact": { 

564 "type": "keyword", 

565 # "index": False, 

566 "store": True 

567 } 

568 } 

569 }, 

570 "isolang_2letter_strict": { 

571 "type": "text", 

572 "fields": { 

573 "exact": { 

574 "type": "keyword", 

575 # "index": False, 

576 "store": True 

577 } 

578 } 

579 }, 

580 "isolang_2letter_lax": { 

581 "type": "text", 

582 "fields": { 

583 "exact": { 

584 "type": "keyword", 

585 # "index": False, 

586 "store": True 

587 } 

588 } 

589 }, 

590 "country_code": { 

591 "type": "text", 

592 "fields": { 

593 "exact": { 

594 "type": "keyword", 

595 # "index": False, 

596 "store": True 

597 } 

598 } 

599 }, 

600 "currency_code_strict": { 

601 "type": "text", 

602 "fields": { 

603 "exact": { 

604 "type": "keyword", 

605 # "index": False, 

606 "store": True 

607 } 

608 } 

609 }, 

610 "currency_code_lax": { 

611 "type": "text", 

612 "fields": { 

613 "exact": { 

614 "type": "keyword", 

615 # "index": False, 

616 "store": True 

617 } 

618 } 

619 }, 

620 "issn": { 

621 "type": "text", 

622 "fields": { 

623 "exact": { 

624 "type": "keyword", 

625 # "index": False, 

626 "store": True 

627 } 

628 } 

629 }, 

630 "url": { 

631 "type": "text", 

632 "fields": { 

633 "exact": { 

634 "type": "keyword", 

635 # "index": False, 

636 "store": True 

637 } 

638 } 

639 }, 

640 "utcdatetimemicros": { 

641 "type": "date", 

642 "format": "date_optional_time" 

643 }, 

644 "utcdatetime": { 

645 "type": "date", 

646 "format": "date_optional_time" 

647 }, 

648 "bool": { 

649 "type": "boolean" 

650 }, 

651 "integer": { 

652 "type": "long" 

653 }, 

654 "bigenddate": { 

655 "type": "date", 

656 "format": "date_optional_time" 

657 }, 

658 "year": { 

659 "type": "date", 

660 "format": "year" 

661 } 

662} 

663 

664# Extension Map from dataobj coercion declarations to ES mappings. 

665# This is useful when some extensions required for some objects additional to defaults. 

666# ~~->DataObj:Library~~ 

667# ~~->Seamless:Library~~ 

668DATAOBJ_TO_MAPPING_COPY_TO_EXTENSIONS = { 

669 "unicode": {"copy_to": ["all_meta"]}, 

670 "str": {"copy_to": ["all_meta"]}, 

671 "unicode_upper": {"copy_to": ["all_meta"]}, 

672 "unicode_lower": {"copy_to": ["all_meta"]}, 

673 "issn": {"copy_to": ["all_meta"]}, 

674 "url": {"copy_to": ["all_meta"]} 

675} 

676 

677# TODO: we may want a big-type and little-type setting 

678DEFAULT_INDEX_SETTINGS = \ 

679 { 

680 'number_of_shards': 4, 

681 'number_of_replicas': 1, 

682 'analysis': { 

683 'analyzer': { 

684 'ascii_folded': { 

685 'tokenizer': 'standard', 

686 'filter': ['lowercase', 'asciifolding'] 

687 } 

688 } 

689 } 

690 } 

691 

692DEFAULT_DYNAMIC_MAPPING = { 

693 'dynamic_templates': [ 

694 { 

695 "strings": { 

696 "match_mapping_type": "string", 

697 "mapping": { 

698 "type": "text", 

699 "fields": { 

700 "exact": { 

701 "type": "keyword", 

702 # "normalizer": "lowercase" 

703 } 

704 } 

705 } 

706 } 

707 } 

708 ] 

709} 

710 

711# LEGACY MAPPINGS 

712# a dict of the ES mappings. identify by name, and include name as first object name 

713# and identifier for how non-analyzed fields for faceting are differentiated in the mappings 

714MAPPINGS = { 

715 'account': { # ~~->Account:Model~~ 

716 # 'aliases': { 

717 # 'account': {} 

718 # }, 

719 'mappings': DEFAULT_DYNAMIC_MAPPING, 

720 'settings': DEFAULT_INDEX_SETTINGS 

721 } 

722} 

723 

724MAPPINGS['upload'] = MAPPINGS["account"] # ~~->Upload:Model~~ 

725MAPPINGS['bulk_articles'] = MAPPINGS["account"] # ~~->BulkArticles:Model~~ 

726MAPPINGS['cache'] = MAPPINGS["account"] # ~~->Cache:Model~~ 

727MAPPINGS['lcc'] = MAPPINGS["account"] # ~~->LCC:Model~~ 

728MAPPINGS['editor_group'] = MAPPINGS["account"] # ~~->EditorGroup:Model~~ 

729MAPPINGS['news'] = MAPPINGS["account"] # ~~->News:Model~~ 

730MAPPINGS['lock'] = MAPPINGS["account"] # ~~->Lock:Model~~ 

731MAPPINGS['provenance'] = MAPPINGS["account"] # ~~->Provenance:Model~~ 

732MAPPINGS['preserve'] = MAPPINGS["account"] # ~~->Preservation:Model~~ 

733MAPPINGS['notification'] = MAPPINGS["account"] # ~~->Notification:Model~~ 

734MAPPINGS['article_tombstone'] = MAPPINGS["account"] # ~~->ArticleTombstone:Model~~ 

735MAPPINGS['shortened_url'] = MAPPINGS["account"] #~~->URLShortener:Model~~ 

736 

737######################################### 

738# Query Routes 

739# ~~->Query:WebRoute~~ 

740 

741QUERY_ROUTE = { 

742 "query": { 

743 # ~~->PublicJournalQuery:Endpoint~~ 

744 "journal": { 

745 "auth": False, 

746 "role": None, 

747 "query_validators": ["non_public_fields_validator", "public_query_validator"], 

748 "query_filters": ["only_in_doaj", "last_update_fallback", "search_all_meta"], 

749 "result_filters": ["public_result_filter"], 

750 "dao": "portality.models.Journal", # ~~->Journal:Model~~ 

751 "required_parameters": {"ref": ["ssw", "public_journal", "subject_page"]} 

752 }, 

753 # ~~->PublicArticleQuery:Endpoint~~ 

754 "article": { 

755 "auth": False, 

756 "role": None, 

757 "query_validators": ["non_public_fields_validator", "public_query_validator"], 

758 "query_filters": ["only_in_doaj"], 

759 "result_filters": ["public_result_filter"], 

760 "dao": "portality.models.Article", # ~~->Article:Model~~ 

761 "required_parameters": {"ref": ["public_article", "toc", "subject_page"]} 

762 }, 

763 # back-compat for fixed query widget 

764 # ~~->PublicJournalArticleQuery:Endpoint~~ 

765 "journal,article": { 

766 "auth": False, 

767 "role": None, 

768 "query_validators": ["non_public_fields_validator", "public_query_validator"], 

769 "query_filters": ["only_in_doaj", "strip_facets", "es_type_fix", "journal_article_filter"], 

770 "result_filters": ["public_result_filter", "add_fqw_facets", "fqw_back_compat"], 

771 "dao": "portality.models.JournalArticle", # ~~->JournalArticle:Model~~ 

772 "required_parameters": {"ref": ["fqw"]} 

773 } 

774 }, 

775 "publisher_query": { 

776 # ~~->PublisherJournalQuery:Endpoint~~ 

777 "journal": { 

778 "auth": True, 

779 "role": "publisher", 

780 "query_validators": ["non_public_fields_validator"], 

781 "query_filters": ["owner", "only_in_doaj", "search_all_meta"], 

782 "result_filters": ["publisher_result_filter"], 

783 "dao": "portality.models.Journal" # ~~->Journal:Model~~ 

784 }, 

785 # ~~->PublisherApplicationQuery:Endpoint~~ 

786 "applications": { 

787 "auth": True, 

788 "role": "publisher", 

789 "query_validators": ["non_public_fields_validator"], 

790 "query_filters": ["owner", "not_update_request", "search_all_meta"], 

791 "result_filters": ["publisher_result_filter"], 

792 "dao": "portality.models.AllPublisherApplications" # ~~->AllPublisherApplications:Model~~ 

793 }, 

794 # ~~->PublisherUpdateRequestsQuery:Endpoint~~ 

795 "update_requests": { 

796 "auth": True, 

797 "role": "publisher", 

798 "query_validators": ["non_public_fields_validator"], 

799 "query_filters": ["owner", "update_request", "search_all_meta"], 

800 "result_filters": ["publisher_result_filter"], 

801 "dao": "portality.models.Application" # ~~->Application:Model~~ 

802 } 

803 }, 

804 "admin_query": { 

805 # ~~->AdminJournalQuery:Endpoint~~ 

806 "journal": { 

807 "auth": True, 

808 "role": "admin", 

809 "dao": "portality.models.Journal" # ~~->Journal:Model~~ 

810 }, 

811 # ~~->AdminApplicationQuery:Endpoint~~ 

812 "suggestion": { 

813 "auth": True, 

814 "role": "admin", 

815 "query_filters": ["not_update_request"], 

816 "dao": "portality.models.Application" # ~~->Application:Model~~ 

817 }, 

818 # ~~->AdminUpdateRequestQuery:Endpoint~~ 

819 "update_requests": { 

820 "auth": True, 

821 "role": "admin", 

822 "query_filters": ["update_request"], 

823 "dao": "portality.models.Application" # ~~->Application:Model~~ 

824 }, 

825 # ~~->AdminEditorGroupQuery:Endpoint~~ 

826 "editor,group": { 

827 "auth": True, 

828 "role": "admin", 

829 "dao": "portality.models.EditorGroup" # ~~->EditorGroup:Model~~ 

830 }, 

831 # ~~->AdminAccountQuery:Endpoint~~ 

832 "account": { 

833 "auth": True, 

834 "role": "admin", 

835 "dao": "portality.models.Account" # ~~->Account:Model~~ 

836 }, 

837 # ~~->AdminJournalArticleQuery:Endpoint~~ 

838 "journal,article": { 

839 "auth": True, 

840 "role": "admin", 

841 "dao": "portality.models.search.JournalArticle" # ~~->JournalArticle:Model~~ 

842 }, 

843 # ~~->AdminBackgroundJobQuery:Endpoint~~ 

844 "background,job": { 

845 "auth": True, 

846 "role": "admin", 

847 "dao": "portality.models.BackgroundJob" # ~~->BackgroundJob:Model~~ 

848 }, 

849 # ~~->APINotificationQuery:Endpoint~~ 

850 "notifications": { 

851 "auth": True, 

852 "role": "admin", 

853 "dao": "portality.models.Notification", # ~~->Notification:Model~~ 

854 "required_parameters": None 

855 }, 

856 "reports": { 

857 "auth": True, 

858 "role": "admin", 

859 "dao": "portality.models.Export" 

860 }, 

861 "journal_csv": { 

862 "auth": True, 

863 "role": "admin", 

864 "dao": "portality.models.JournalCSV" # ~~->JournalCSV:Model~~ 

865 }, 

866 "pdd": { 

867 "auth": True, 

868 "role": "admin", 

869 "dao": "portality.models.DataDump" # ~~->JournalCSV:Model~~ 

870 }, 

871 "alerts": { 

872 "auth": True, 

873 "role": "admin", 

874 "dao": "portality.models.AdminAlert", # ~~->AdminAlert:Model~~ 

875 "required_parameters": None 

876 }, 

877 "autoassign": { 

878 "auth": True, 

879 "role": "admin", 

880 "dao": "portality.models.URReviewRoute", # ~~->AdminAlert:Model~~ 

881 "required_parameters": None 

882 }, 

883 "ris": { 

884 "auth": True, 

885 "role": "admin", 

886 "dao": "portality.models.RISExport", # ~~->AdminAlert:Model~~ 

887 "required_parameters": None 

888 } 

889 }, 

890 "associate_query": { 

891 # ~~->AssEdJournalQuery:Endpoint~~ 

892 "journal": { 

893 "auth": True, 

894 "role": "associate_editor", 

895 "query_validators": ["non_public_fields_validator"], 

896 "query_filters": ["associate", "search_all_meta", "flagged"], 

897 "dao": "portality.models.Journal" # ~~->Journal:Model~~ 

898 }, 

899 # ~~->AssEdApplicationQuery:Endpoint~~ 

900 "suggestion": { 

901 "auth": True, 

902 "role": "associate_editor", 

903 "query_validators": ["non_public_fields_validator"], 

904 "query_filters": ["associate", "search_all_meta"], 

905 "dao": "portality.models.Application" # ~~->Application:Model~~ 

906 } 

907 }, 

908 "editor_query": { 

909 # ~~->EditorJournalQuery:Endpoint~~ 

910 "journal": { 

911 "auth": True, 

912 "role": "editor", 

913 "query_validators": ["non_public_fields_validator"], 

914 "query_filters": ["editor", "search_all_meta"], 

915 "dao": "portality.models.Journal" # ~~->Journal:Model~~ 

916 }, 

917 # ~~->EditorApplicationQuery:Endpoint~~ 

918 "suggestion": { 

919 "auth": True, 

920 "role": "editor", 

921 "query_validators": ["non_public_fields_validator"], 

922 "query_filters": ["editor", "search_all_meta"], 

923 "dao": "portality.models.Application" # ~~->Application:Model~~ 

924 } 

925 }, 

926 "api_query": { 

927 # ~~->APIArticleQuery:Endpoint~~ 

928 "article": { 

929 "auth": False, 

930 "role": None, 

931 "query_filters": ["only_in_doaj", "public_source"], 

932 "dao": "portality.models.Article", # ~~->Article:Model~~ 

933 "required_parameters": None, 

934 "keepalive": "10m" 

935 }, 

936 # ~~->APIJournalQuery:Endpoint~~ 

937 "journal": { 

938 "auth": False, 

939 "role": None, 

940 "query_validators": ["non_public_fields_validator"], 

941 "query_filters": ["only_in_doaj", "public_source", "search_all_meta"], 

942 "dao": "portality.models.Journal", # ~~->Journal:Model~~ 

943 "required_parameters": None 

944 }, 

945 # ~~->APIApplicationQuery:Endpoint~~ 

946 "application": { 

947 "auth": True, 

948 "role": None, 

949 "query_validators": ["non_public_fields_validator"], 

950 "query_filters": ["owner", "private_source", "search_all_meta"], 

951 "dao": "portality.models.Suggestion", # ~~->Application:Model~~ 

952 "required_parameters": None 

953 } 

954 }, 

955 "dashboard_query": { 

956 # ~~->APINotificationQuery:Endpoint~~ 

957 "notifications": { 

958 "auth": True, 

959 "role": "read_notifications", 

960 "query_filters": ["who_current_user"], # ~~-> WhoCurrentUser:Query 

961 "dao": "portality.models.Notification", # ~~->Notification:Model~~ 

962 "required_parameters": None 

963 } 

964 } 

965} 

966 

967QUERY_FILTERS = { 

968 # sanitisers 

969 "public_query_validator": "portality.lib.query_filters.public_query_validator", 

970 "non_public_fields_validator": "portality.lib.query_filters.non_public_fields_validator", 

971 

972 # query filters 

973 "only_in_doaj": "portality.lib.query_filters.only_in_doaj", 

974 "owner": "portality.lib.query_filters.owner", 

975 "update_request": "portality.lib.query_filters.update_request", 

976 "associate": "portality.lib.query_filters.associate", 

977 "editor": "portality.lib.query_filters.editor", 

978 "strip_facets": "portality.lib.query_filters.strip_facets", 

979 "es_type_fix": "portality.lib.query_filters.es_type_fix", 

980 "last_update_fallback": "portality.lib.query_filters.last_update_fallback", 

981 "not_update_request": "portality.lib.query_filters.not_update_request", 

982 "who_current_user": "portality.lib.query_filters.who_current_user", # ~~-> WhoCurrentUser:Query ~~ 

983 "search_all_meta": "portality.lib.query_filters.search_all_meta", # ~~-> SearchAllMeta:Query ~~ 

984 "journal_article_filter": "portality.lib.query_filters.journal_article_filter", 

985 # ~~-> JournalArticleFilter:Query ~~ 

986 

987 # result filters 

988 "public_result_filter": "portality.lib.query_filters.public_result_filter", 

989 "publisher_result_filter": "portality.lib.query_filters.publisher_result_filter", 

990 "add_fqw_facets": "portality.lib.query_filters.add_fqw_facets", 

991 "fqw_back_compat": "portality.lib.query_filters.fqw_back_compat", 

992 

993 # source filters 

994 "private_source": "portality.lib.query_filters.private_source", 

995 "public_source": "portality.lib.query_filters.public_source", 

996} 

997# Exclude the fields that doesn't want to be searched by public queries 

998# This is part of non_public_fields_validator. 

999PUBLIC_QUERY_VALIDATOR__EXCLUDED_FIELDS = [ 

1000 "admin.notes.note", 

1001 "admin.notes.id", 

1002 "admin.notes.author_id" 

1003] 

1004 

1005ADMIN_NOTES_INDEX_ONLY_FIELDS = { 

1006 "all_meta": { 

1007 "type": "text", 

1008 "fields": { 

1009 "exact": { 

1010 "type": "keyword", 

1011 "store": True 

1012 } 

1013 } 

1014 } 

1015} 

1016 

1017ADMIN_NOTES_SEARCH_MAPPING = { 

1018 "admin.notes.id": { 

1019 "type": "text", 

1020 "fields": { 

1021 "exact": { 

1022 "type": "keyword", 

1023 "store": True 

1024 } 

1025 } 

1026 }, 

1027 "admin.notes.note": { 

1028 "type": "text", 

1029 "fields": { 

1030 "exact": { 

1031 "type": "keyword", 

1032 "store": True 

1033 } 

1034 } 

1035 }, 

1036 "admin.notes.author_id": { 

1037 "type": "text", 

1038 "fields": { 

1039 "exact": { 

1040 "type": "keyword", 

1041 "store": True 

1042 } 

1043 } 

1044 } 

1045} 

1046 

1047ASCII_FOLDED = {"analyzer": "ascii_folded", "search_analyzer": "ascii_folded"} 

1048 

1049JOURNAL_EXCEPTION_MAPPING = { 

1050 "bibjson.title" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1051 "bibjson.alternative_title" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1052 "bibjson.publisher.name" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1053 "index.country" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1054 "index.title": {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED} 

1055} 

1056 

1057ARTICLE_EXCEPTION_MAPPING = { 

1058 "bibjson.abstract" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1059 "bibjson.author.name" : {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1060 "bibjson.journal.publisher": {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1061 "index.country": {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED}, 

1062 "bibjson.title": {**DATAOBJ_TO_MAPPING_DEFAULTS["unicode"], **ASCII_FOLDED} 

1063} 

1064 

1065#################################################### 

1066# Autocomplete 

1067 

1068# ~~->BibJSON:Model~~ 

1069AUTOCOMPLETE_ADVANCED_FIELD_MAPS = { 

1070 "bibjson.publisher.name": "index.publisher_ac", 

1071 "bibjson.institution.name": "index.institution_ac" 

1072} 

1073 

1074#################################################### 

1075# Application Form 

1076# ~~->ApplicationForm:Feature~~ 

1077 

1078# save the public application form as a draft every 60 seconds 

1079PUBLIC_FORM_AUTOSAVE = 60000 

1080 

1081############################################ 

1082# Atom Feed 

1083# ~~->AtomFeed:Feature~~ 

1084 

1085FEED_TITLE = "DOAJ Recent Journals Added" 

1086 

1087# Maximum number of feed entries to be given in a single response. If this is omitted, it will 

1088# default to 20 

1089MAX_FEED_ENTRIES = 100 

1090 

1091# Maximum age of feed entries (in seconds) (default value here is 30 days). 

1092MAX_FEED_ENTRY_AGE = 2592000 

1093 

1094# Licensing terms for feed content 

1095# ~~->SiteLicence:Content~~ 

1096FEED_LICENCE = "(c) DOAJ 2024. CC BY-SA." 

1097 

1098# name of the feed generator (goes in the atom:generator element) 

1099FEED_GENERATOR = "CottageLabs feed generator" 

1100 

1101# Larger image to use as the logo for all of the feeds 

1102# ~~->Favicon:Content~~ 

1103FEED_LOGO = "https://doaj.org/static/doaj/images/favicon.ico" 

1104 

1105########################################### 

1106# OAI-PMH SETTINGS 

1107# ~~->OAIPMH:Feature~~ 

1108 

1109OAI_ADMIN_EMAIL = 'helpdesk+oai@doaj.org' 

1110 

1111# ~~->OAIAriticleXML:Crosswalk~~ 

1112# ~~->OAIJournalXML:Crosswalk~~ 

1113OAI_DC_METADATA_FORMAT = { 

1114 "metadataPrefix": "oai_dc", 

1115 "schema": "http://www.openarchives.org/OAI/2.0/oai_dc.xsd", 

1116 "metadataNamespace": "http://www.openarchives.org/OAI/2.0/oai_dc/" 

1117} 

1118 

1119OAI_DOAJ_METADATA_FORMAT = { 

1120 "metadataPrefix": "oai_doaj", 

1121 "schema": "https://doaj.org/static/doaj/doajArticles.xsd", 

1122 "metadataNamespace": "https://doaj.org/features/oai_doaj/1.0/" 

1123} 

1124 

1125OAIPMH_METADATA_FORMATS = { 

1126 # "specific endpoint": [list, of, formats, supported] 

1127 

1128 None: [OAI_DC_METADATA_FORMAT], # no specific endpoint, the request is to the root /oai path 

1129 "article": [OAI_DC_METADATA_FORMAT, OAI_DOAJ_METADATA_FORMAT] 

1130} 

1131 

1132OAIPMH_IDENTIFIER_NAMESPACE = "doaj.org" 

1133 

1134OAIPMH_LIST_RECORDS_PAGE_SIZE = 100 

1135 

1136OAIPMH_LIST_IDENTIFIERS_PAGE_SIZE = 300 

1137 

1138OAIPMH_RESUMPTION_TOKEN_EXPIRY = 86400 

1139 

1140########################################## 

1141# Article XML configuration 

1142 

1143# paths to schema files to validate incoming documents against for the various 

1144# crosswalks available 

1145# ~~->CrossrefXML:Schema~~ 

1146# ~~->DOAJArticleXML:Schema~~ 

1147SCHEMAS = { 

1148 "doaj": os.path.join(BASE_FILE_PATH, "static", "doaj", "doajArticles.xsd"), 

1149 "crossref442": os.path.join(BASE_FILE_PATH, "static", "crossref", "crossref4.4.2.xsd"), 

1150 "crossref531": os.path.join(BASE_FILE_PATH, "static", "crossref", "crossref5.3.1.xsd") 

1151} 

1152 

1153# placeholders for the loaded schemas 

1154DOAJ_SCHEMA = None 

1155CROSSREF442_SCHEMA = None 

1156CROSSREF531_SCHEMA = None 

1157LOAD_CROSSREF_THREAD = None 

1158 

1159# mapping of format names to modules which implement the crosswalks 

1160# ~~->DOAJArticleXML:Crosswalk~~ 

1161# ~~->CrossrefXML:Crosswalk~~ 

1162ARTICLE_CROSSWALKS = { 

1163 "doaj": "portality.crosswalks.article_doaj_xml.DOAJXWalk", 

1164 "crossref442": "portality.crosswalks.article_crossref_xml.CrossrefXWalk442", 

1165 "crossref531": "portality.crosswalks.article_crossref_xml.CrossrefXWalk531" 

1166} 

1167 

1168# maximum size of files that can be provided by-reference (the default value is 250Mb) 

1169MAX_REMOTE_SIZE = 262144000 

1170 

1171################################################# 

1172# Cache settings 

1173# ~~->Cache:Feature~~ 

1174 

1175# number of seconds site statistics should be considered fresh 

1176# 1800s = 30mins 

1177SITE_STATISTICS_TIMEOUT = 1800 

1178 

1179# directory into which to put files which are cached (e.g. the csv) 

1180CACHE_DIR = os.path.join(ROOT_DIR, "cache") 

1181 

1182# Article and Journal History directories - they should be different 

1183# ~~->ArticleHistory:Feature~~ 

1184# ~~->JournalHistory:Feature~~ 

1185ARTICLE_HISTORY_DIR = os.path.join(ROOT_DIR, "history", "article") 

1186JOURNAL_HISTORY_DIR = os.path.join(ROOT_DIR, "history", "journal") 

1187 

1188################################################# 

1189# Sitemap settings 

1190# ~~->Sitemap:Feature~~ 

1191 

1192# approximate rate of change of the Table of Contents for journals 

1193TOC_CHANGEFREQ = "monthly" 

1194 

1195# Maximum number of sitemap entries per index file before splitting into chunks (50K entries, 50MB size is actual limit) 

1196SITEMAP_INDEX_MAX_ENTRIES = 90 

1197 

1198################################################## 

1199# News feed settings 

1200# ~~->News:Feature~~ 

1201 

1202BLOG_URL = "https://blog.doaj.org/" 

1203 

1204BLOG_FEED_URL = "https://blog.doaj.org/feed/" 

1205 

1206FRONT_PAGE_NEWS_ITEMS = 4 

1207 

1208NEWS_PAGE_NEWS_ITEMS = 20 

1209 

1210################################################## 

1211# Edit Lock settings 

1212# ~~->Lock:Feature~~ 

1213 

1214# amount of time loading an editable page locks it for, in seconds. 

1215EDIT_LOCK_TIMEOUT = 1200 

1216 

1217# amount of time a background task can lock a resource for, in seconds 

1218BACKGROUND_TASK_LOCK_TIMEOUT = 3600 

1219 

1220############################################### 

1221# Bit.ly configuration 

1222# ~~->Bitly:ExternalService~~ 

1223 

1224# bit,ly api shortening service 

1225# BITLY_SHORTENING_API_URL = "https://api-ssl.bitly.com/v4/shorten" 

1226 

1227# bitly oauth token 

1228# ENTER YOUR OWN TOKEN IN APPROPRIATE .cfg FILE 

1229# BITLY_OAUTH_TOKEN = "" 

1230 

1231 

1232################################################# 

1233# API configuration 

1234# ~~->API:Feature~~ 

1235# ~~->APISearch:Feature~~ 

1236 

1237# maximum number of records to return per page 

1238DISCOVERY_MAX_PAGE_SIZE = 100 

1239 

1240# maximum number of records to return in total (a request for a page starting beyond this number will fail) 

1241DISCOVERY_MAX_RECORDS_SIZE = 1000 

1242 

1243# ~~->ArticleBibJSON:Model~~ 

1244DISCOVERY_ARTICLE_SEARCH_SUBS = { 

1245 "title": "bibjson.title", 

1246 "doi": "bibjson.identifier.id.exact", 

1247 "issn": "index.issn.exact", 

1248 "publisher": "bibjson.journal.publisher", 

1249 "journal": "bibjson.journal.title", 

1250 "abstract": "bibjson.abstract" 

1251} 

1252 

1253DISCOVERY_ARTICLE_SORT_SUBS = { 

1254 "title": "index.unpunctitle.exact" 

1255} 

1256 

1257# ~~->JournalBibJSON:Model~~ 

1258DISCOVERY_JOURNAL_SEARCH_SUBS = { 

1259 "title": "index.title", 

1260 "issn": "index.issn.exact", 

1261 "publisher": "bibjson.publisher", 

1262 "license": "index.license.exact", 

1263 "username": "admin.owner.exact" 

1264} 

1265 

1266DISCOVERY_JOURNAL_SORT_SUBS = { 

1267 "title": "index.unpunctitle.exact", 

1268 "issn": "index.issn.exact" 

1269} 

1270 

1271DISCOVERY_APPLICATION_SEARCH_SUBS = { 

1272 "title": "index.title", 

1273 "issn": "index.issn.exact", 

1274 "publisher": "bibjson.publisher", 

1275 "license": "index.license.exact" 

1276} 

1277 

1278DISCOVERY_APPLICATION_SORT_SUBS = { 

1279 "title": "index.unpunctitle.exact", 

1280 "issn": "index.issn.exact" 

1281} 

1282 

1283# API data dump settings 

1284DISCOVERY_BULK_PAGE_SIZE = 1000 

1285DISCOVERY_RECORDS_PER_FILE = 100000 

1286 

1287###################################################### 

1288# Hotjar configuration 

1289# ~~->Hotjar:ExternalService~~ 

1290 

1291# hotjar id - only activate this in production 

1292HOTJAR_ID = "" 

1293 

1294###################################################### 

1295# Analytics configuration 

1296# specify in environment .cfg file - avoids sending live analytics events from test and dev environments 

1297 

1298# ~~->PlausibleAnalytics:ExternalService~~ 

1299# Plausible analytics 

1300# root url of plausible 

1301PLAUSIBLE_URL = "https://plausible.io" 

1302PLAUSIBLE_JS_URL = PLAUSIBLE_URL + "/js/script.outbound-links.file-downloads.js" 

1303PLAUSIBLE_API_URL = PLAUSIBLE_URL + "/api/event" 

1304# site name / domain name that used to register in plausible 

1305PLAUSIBLE_SITE_NAME = BASE_DOMAIN 

1306PLAUSIBLE_LOG_DIR = None 

1307 

1308# Analytics custom dimensions. These are configured in the interface. #fixme: are these still configured since the move from GA? 

1309ANALYTICS_DIMENSIONS = { 

1310 'oai_res_id': 'dimension1', # In analytics as OAI:Record 

1311} 

1312 

1313# Plausible for OAI-PMH 

1314# ~~-> OAIPMH:Feature~~ 

1315ANALYTICS_CATEGORY_OAI = 'OAI-PMH' 

1316 

1317# Plausible for Atom 

1318# ~~-> Atom:Feature~~ 

1319ANALYTICS_CATEGORY_ATOM = 'Atom' 

1320ANALYTICS_ACTION_ACTION = 'Feed request' 

1321 

1322# Plausible for JournalCSV 

1323# ~~-> JournalCSV:Feature~~ 

1324ANALYTICS_CATEGORY_JOURNALCSV = 'JournalCSV' 

1325ANALYTICS_ACTION_JOURNALCSV = 'Download' 

1326 

1327# Plausible for OpenURL 

1328# ~~->OpenURL:Feature~~ 

1329ANALYTICS_CATEGORY_OPENURL = 'OpenURL' 

1330 

1331# Plausible for PublicDataDump 

1332# ~~->PublicDataDump:Feature~~ 

1333ANALYTICS_CATEGORY_PUBLICDATADUMP = 'PublicDataDump' 

1334ANALYTICS_ACTION_PUBLICDATADUMP = 'Download' 

1335 

1336# Plausible for RIS 

1337# ~~->PublicDataDump:Feature~~ 

1338ANALYTICS_CATEGORY_RIS = 'RIS' 

1339ANALYTICS_ACTION_RISEXPORT = 'Export' 

1340 

1341# Plausible for Urlshort 

1342# ~~->URLShortener:Feature~~ 

1343ANALYTICS_CATEGORY_URLSHORT = 'ShortURL' 

1344ANALYTICS_ACTION_URLSHORT_ADD = 'Find or create short url' 

1345ANALYTICS_ACTION_URLSHORT_REDIRECT = 'Redirect' 

1346 

1347# Plausible for API 

1348# ~~-> API:Feature~~ 

1349ANALYTICS_CATEGORY_API = 'API Hit' 

1350ANALYTICS_ACTIONS_API = { 

1351 'search_applications': 'Search applications', 

1352 'search_journals': 'Search journals', 

1353 'search_articles': 'Search articles', 

1354 'create_application': 'Create application', 

1355 'retrieve_application': 'Retrieve application', 

1356 'update_application': 'Update application', 

1357 'delete_application': 'Delete application', 

1358 'create_article': 'Create article', 

1359 'retrieve_article': 'Retrieve article', 

1360 'update_article': 'Update article', 

1361 'delete_article': 'Delete article', 

1362 'retrieve_journal': 'Retrieve journal', 

1363 'bulk_application_create': 'Bulk application create', 

1364 'bulk_application_delete': 'Bulk application delete', 

1365 'bulk_article_create': 'Bulk article create', 

1366 'bulk_article_create_status': 'Bulk article create status', 

1367 'bulk_article_delete': 'Bulk article delete' 

1368} 

1369 

1370# Plausible for fixed query widget 

1371# ~~->FixedQueryWidget:Feature~~ 

1372ANALYTICS_CATEGORY_FQW = 'FQW' 

1373ANALYTICS_ACTION_FQW = 'Hit' 

1374 

1375##################################################### 

1376# Anonymised data export (for dev) configuration 

1377# ~~->AnonExport:Feature~~ 

1378 

1379ANON_SALT = 'changeme' 

1380 

1381# ======================================== 

1382# Quick Reject Feature Config 

1383# ~~->QuickReject:Feature~~ 

1384 

1385QUICK_REJECT_REASONS = [ 

1386 "The journal has not published enough research content to qualify for DOAJ inclusion.", 

1387 "The ISSN is incorrect, provisional or not registered with issn.org.", 

1388 "The URL(s) provided in the application do not work.", 

1389 "The journal is already in DOAJ.", 

1390 "The journal is not Open Access.", 

1391 "The journal title in the application and/or website does not match the title at issn.org.", 

1392 "The application has incorrect answers or the URLs given do not provide the required information.", 

1393 "This application is a duplicate.", 

1394 "The full-text articles are not available article by article with individual links.", 

1395 "The information in the journal implies that it does not employ a fair & robust peer review process.", 

1396 "The journal or publisher has been previously rejected or removed from DOAJ.", 

1397 "The journal's copyright policy is not available or unclear.", 

1398 "The journal's licensing policy is not available or unclear.", 

1399 "The information about the journal is in different languages.", 

1400 "The information about the journal is not the same in all languages.", 

1401 "The journal makes a false claim to be indexed in DOAJ or other databases or displays non-standard Impact Factors.", 

1402 "The journal does not employ good publishing practices." 

1403] 

1404 

1405MINIMAL_OA_START_DATE = 1900 

1406 

1407############################################# 

1408# Harvester Configuration 

1409# ~~->Harvester:Feature~~ 

1410 

1411# Configuration options for the DOAJ API Client 

1412 

1413# EPMC Client configuration 

1414# ~~-> EPMC:ExternalService~~ 

1415EPMC_REST_API = "https://www.ebi.ac.uk/europepmc/webservices/rest/" 

1416EPMC_TARGET_VERSION = "6.9" # doc here: https://europepmc.org/docs/Europe_PMC_RESTful_Release_Notes.pdf 

1417EPMC_HARVESTER_THROTTLE = 0.2 

1418 

1419# General harvester configuration 

1420HARVESTERS = [ 

1421 "portality.tasks.harvester_helpers.epmc.epmc_harvester.EPMCHarvester" 

1422] 

1423 

1424INITIAL_HARVEST_DATE = "2015-12-01T00:00:00Z" 

1425 

1426# List of account ids to harvest from. MUST NOT be checked into the repo, put these 

1427# in the local.cfg instead 

1428HARVEST_ACCOUNTS = [] 

1429 

1430# Amount of time a harvester record is allowed to be in "queued" or "processing" state before we 

1431# assume it's a zombie, and ignore it 

1432HARVESTER_ZOMBIE_AGE = 604800 

1433 

1434####################################################### 

1435# Preservation configuration 

1436# ~~->Preservation:Feature 

1437PRESERVATION_URL = "http://PresevatinURL" 

1438PRESERVATION_USERNAME = "user_name" 

1439PRESERVATION_PASSWD = "password" 

1440PRESERVATION_COLLECTION = {} 

1441 

1442######################################################### 

1443# Background tasks --- anon export 

1444TASKS_ANON_EXPORT_CLEAN = False 

1445TASKS_ANON_EXPORT_LIMIT = None 

1446TASKS_ANON_EXPORT_BATCH_SIZE = 100000 

1447TASKS_ANON_EXPORT_SCROLL_TIMEOUT = '5m' 

1448 

1449######################################################### 

1450# Background tasks --- old_data_cleanup 

1451TASK_DATA_RETENTION_DAYS = { 

1452 "notification": 180, # ~~-> Notifications:Feature ~~ 

1453 "background_job": 180, # ~~-> BackgroundJobs:Feature ~~ 

1454 "admin_alert": 180, # ~~-> AdminAlerts:Feature ~~ 

1455} 

1456 

1457######################################## 

1458# Editorial Dashboard - set to-do list size 

1459TODO_LIST_SIZE = 48 

1460 

1461######################################################### 

1462# Background tasks --- monitor_bgjobs 

1463TASKS_MONITOR_BGJOBS_TO = ["helpdesk@doaj.org", ] 

1464TASKS_MONITOR_BGJOBS_FROM = "helpdesk@doaj.org" 

1465 

1466################################## 

1467# Background monitor 

1468# ~~->BackgroundMonitor:Feature~~ 

1469 

1470# some time period for convenience 

1471_MIN = 60 

1472_HOUR = 3600 

1473_DAY = 24 * _HOUR 

1474_WEEK = 7 * _DAY 

1475 

1476# Configures the age of the last completed job on the queue before the queue is marked as unstable 

1477# (in seconds) 

1478BG_MONITOR_LAST_COMPLETED = { 

1479 'events': 2 * _HOUR, # 2 hours 

1480 'scheduled_short': 2 * _HOUR, # 2 hours 

1481 'scheduled_long': _DAY + 2 * _HOUR, # 26 hours 

1482} 

1483 

1484# Default monitoring config for background job types which are not enumerated in BG_MONITOR_ERRORS_CONFIG below 

1485BG_MONITOR_DEFAULT_CONFIG = { 

1486 ## default values for queued config 

1487 

1488 # the total number of items that are allowed to be in `queued` state at the same time. 

1489 # Any more than this and the result is flagged 

1490 'total': 2, 

1491 

1492 # The age of the oldest record allowed to be in the `queued` state. 

1493 # If the oldest queued item was created before this, the result is flagged 

1494 'oldest': 20 * _MIN, 

1495 

1496 ## default values for error config 

1497 

1498 # The time period over which to check for errors, from now to now - check_sec 

1499 'check_sec': _HOUR, 

1500 

1501 # The number of errors allowed in the check period before the result is flagged 

1502 'allowed_num_err': 0, 

1503 

1504 # The last time this job ran within the specified time period, was it successful. 

1505 # If the most recent job in the timeframe is an error, this will trigger an "unstable" state (0 turns this off) 

1506 'last_run_successful_in': 0 

1507} 

1508 

1509# Configures the monitoring period and the allowed number of errors in that period before a queue is marked 

1510# as unstable 

1511BG_MONITOR_ERRORS_CONFIG = { 

1512 'anon_export': { 

1513 'check_sec': _WEEK, # a week 

1514 'allowed_num_err': 0 

1515 }, 

1516 'article_bulk_create': { 

1517 'check_sec': _DAY, # 1 day 

1518 'allowed_num_err': 0, 

1519 }, 

1520 'article_cleanup_sync': { 

1521 'check_sec': 2 * _DAY, # 2 days 

1522 'allowed_num_err': 0 

1523 }, 

1524 'harvest': { 

1525 'check_sec': _DAY, 

1526 'allowed_num_err': 0, 

1527 }, 

1528 'ingest_articles': { 

1529 'check_sec': _DAY, 

1530 'allowed_num_err': 0 

1531 }, 

1532 'journal_csv': { 

1533 'check_sec': 3 * _HOUR, 

1534 'allowed_num_err': 1, 

1535 }, 

1536 'public_data_dump': { 

1537 'check_sec': 2 * _HOUR, 

1538 'allowed_num_err': 0 

1539 }, 

1540 'process_event': { 

1541 'check_sec': 2 * _HOUR, 

1542 'allowed_num_err': 0 

1543 } 

1544} 

1545 

1546# Configures the total number of queued items and the age of the oldest of those queued items allowed 

1547# before the queue is marked as unstable. This is provided by type, so we can monitor all types separately 

1548BG_MONITOR_QUEUED_CONFIG = { 

1549 'anon_export': { 

1550 'total': 1, 

1551 'oldest': 20 * _MIN 

1552 }, 

1553 'article_bulk_create': { 

1554 'total': 3, 

1555 'oldest': 10 * _MIN 

1556 }, 

1557 'harvest': { 

1558 'total': 1, 

1559 'oldest': _DAY 

1560 }, 

1561 'ingest_articles': { 

1562 'total': 10, 

1563 'oldest': 10 * _MIN 

1564 }, 

1565 'journal_bulk_edit': { 

1566 'total': 2, 

1567 'oldest': 10 * _MIN 

1568 }, 

1569 'journal_csv': { 

1570 'total': 1, 

1571 'oldest': 20 * _MIN 

1572 }, 

1573 'public_data_dump': { 

1574 'total': 1, 

1575 'oldest': _DAY 

1576 }, 

1577 'set_in_doaj': { 

1578 'total': 2, 

1579 'oldest': 10 * _MIN 

1580 }, 

1581 'suggestion_bulk_edit': { 

1582 'total': 2, 

1583 'oldest': 10 * _MIN 

1584 }, 

1585 'process_event': { 

1586 'total': 500, 

1587 'oldest': 6 * _HOUR 

1588 } 

1589} 

1590 

1591BG_MONITOR_LAST_SUCCESSFULLY_RUN_CONFIG = { 

1592 'anon_export': { 

1593 'last_run_successful_in': 32 * _DAY 

1594 }, 

1595 'article_cleanup_sync': { 

1596 'last_run_successful_in': 33 * _DAY 

1597 }, 

1598 'async_workflow_notifications': { 

1599 'last_run_successful_in': _WEEK + _DAY 

1600 }, 

1601 'check_latest_es_backup': { 

1602 'last_run_successful_in': _DAY + _HOUR 

1603 }, 

1604 'datalog_journal_added_update': { 

1605 'last_run_successful_in': _HOUR 

1606 }, 

1607 'find_discontinued_soon': { 

1608 'last_run_successful_in': _DAY + _HOUR 

1609 }, 

1610 'harvest': { 

1611 'last_run_successful_in': _DAY + _HOUR 

1612 }, 

1613 'journal_csv': { 

1614 'last_run_successful_in': 2 * _HOUR 

1615 }, 

1616 'monitor_bgjobs': { 

1617 'last_run_successful_in': _WEEK + _DAY 

1618 }, 

1619 'old_data_cleanup': { 

1620 'last_run_successful_in': 32 * _DAY 

1621 }, 

1622 'prune_es_backups': { 

1623 'last_run_successful_in': _DAY + _HOUR 

1624 }, 

1625 'public_data_dump': { 

1626 'last_run_successful_in': 32 * _DAY 

1627 }, 

1628 'read_news': { 

1629 'last_run_successful_in': 2 * _HOUR 

1630 }, 

1631 'reporting': { 

1632 'last_run_successful_in': 32 * _DAY 

1633 }, 

1634 'request_es_backup': { 

1635 'last_run_successful_in': _DAY + _HOUR 

1636 }, 

1637 'sitemap': { 

1638 'last_run_successful_in': _DAY + _HOUR 

1639 } 

1640} 

1641 

1642################################################## 

1643# Public data dump settings 

1644 

1645# how long should the temporary URL for public data dumps last 

1646PUBLIC_DATA_DUMP_URL_TIMEOUT = 3600 

1647 

1648################################################## 

1649# Journal CSV Setings 

1650 

1651# how long should the temporary URL for journal csvs last 

1652JOURNAL_CSV_URL_TIMEOUT = 3600 

1653 

1654################################################## 

1655# Pages under maintenance 

1656 

1657PRESERVATION_PAGE_UNDER_MAINTENANCE = False 

1658 

1659# report journals that discontinue in ... days (eg. 1 = tomorrow) 

1660DISCONTINUED_DATE_DELTA = 0 

1661 

1662################################################## 

1663# Feature tours currently active 

1664 

1665TOUR_COOKIE_PREFIX = "doaj_tour_" 

1666TOUR_COOKIE_MAX_AGE = 31536000 

1667 

1668TOURS = { 

1669 "/admin/application/*": [ 

1670 { 

1671 "roles": ["admin"], 

1672 "selectors": [".flags__container"], 

1673 "content_id": "admin_flags", 

1674 "name": "Flags", 

1675 "description": "Make teamwork smoother by adding a flag to journals and applications — a note assigned to a teammate, with an optional deadline." 

1676 } 

1677 ], 

1678 "/admin/journal/*": [ 

1679 { 

1680 "roles": ["admin"], 

1681 "selectors": [".autochecks-manager-toggle"], 

1682 "content_id": "admin_journal_autochecks", 

1683 "name": "Autochecks", 

1684 "description": "Autochecks are available on some journals, and can help you to identify potential problems with the journal's metadata." 

1685 } 

1686 ], 

1687 "/editor/": [ 

1688 { 

1689 "roles": ["editor"], 

1690 "content_id": "application_by_status", 

1691 "name": "New Links in the Colour Legend", 

1692 "description": "Discover how the colour legend labels now serve as links to quickly filter and view applications by group and status." 

1693 } 

1694 ], 

1695 "/dashboard/": [ 

1696 { 

1697 "roles": ["admin"], 

1698 "content_id": "application_by_status", 

1699 "name": "New Links in the Colour Legend", 

1700 "description": "Discover how the colour legend labels now serve as links to quickly filter and view applications by group and status." 

1701 } 

1702 ] 

1703} 

1704 

1705####################################################### 

1706# Selenium test environment 

1707 

1708# url of selenium server, selenium remote will be used if it's not empty 

1709# usually it's a docker container and the url should be 'http://localhost:4444/wd/hub' 

1710SELENIUM_REMOTE_URL = 'http://localhost:4444/wd/hub' 

1711 

1712# host and port that used to run doaj server in background for selenium testcases 

1713# if you use docker selenium browser container, ip should be ip of docker network interface such as 172.17.0.1 

1714# SELENIUM_DOAJ_HOST = 'localhost' 

1715SELENIUM_DOAJ_HOST = '172.17.0.1' 

1716SELENIUM_DOAJ_PORT = 5014 

1717 

1718################################################# 

1719# Concurrency timeout(s) 

1720 

1721UR_CONCURRENCY_TIMEOUT = 10 

1722 

1723############################################# 

1724# Google Sheet 

1725# ~~->GoogleSheet:ExternalService~~ 

1726 

1727# Google Sheet API 

1728# value should be key file path of json, empty string means disabled 

1729GOOGLE_KEY_PATH = '' 

1730 

1731# The /export path to test users CSV file on google sheets (file is public) 

1732TEST_USERS_CSV_DL_PATH = "" 

1733 

1734 

1735############################################# 

1736# Datalog 

1737# ~~->Datalog:Feature~~ 

1738 

1739# Datalog for Journal Added 

1740 

1741# google sheet filename for datalog ja 

1742DATALOG_JA_FILENAME = 'DOAJ: journals added and withdrawn' 

1743 

1744# worksheet name or tab name that datalog will write to 

1745DATALOG_JA_WORKSHEET_NAME = 'Added' 

1746 

1747################################################## 

1748# Autocheck Resource configurations 

1749 

1750# Should we autocheck incoming applications and update requests 

1751AUTOCHECK_INCOMING = False 

1752 

1753AUTOCHECK_RESOURCE_ISSN_ORG_TIMEOUT = 10 

1754AUTOCHECK_RESOURCE_ISSN_ORG_THROTTLE = 1 # seconds between requests 

1755 

1756################################################### 

1757# Automatic Update Request editor group assignment 

1758 

1759AUTO_ASSIGN_UR_EDITOR_GROUP = True 

1760AUTO_ASSIGN_EDITOR_BY_PUBLISHER_SHEET = "https://docs.google.com/spreadsheets/d/e/2PACX-1vQg09oCuqQcP0XTFyRiLzpPFoqUeEE6hSDEIglUvSLU-TGVP9C3j4XLgslmBLJmQcdlGujz1b9TN6CN/pub?gid=0&single=true&output=csv" 

1761AUTO_ASSIGN_EDITOR_BY_COUNTRY_SHEET = "https://docs.google.com/spreadsheets/d/e/2PACX-1vQg09oCuqQcP0XTFyRiLzpPFoqUeEE6hSDEIglUvSLU-TGVP9C3j4XLgslmBLJmQcdlGujz1b9TN6CN/pub?gid=1948254841&single=true&output=csv" 

1762AUTO_ASSIGN_EDITOR_GOOGLE_SHEET = "https://docs.google.com/spreadsheets/d/1EDvesL3si4zRj97RCUjTcqglin_XJ5OLGAR8xifoTr0/edit" 

1763 

1764################################################## 

1765# Background jobs Management settings 

1766 

1767# list of actions name that will be cleaned up if they are redundant 

1768BGJOB_MANAGE_REDUNDANT_ACTIONS = [ 

1769 'read_news', 'journal_csv' 

1770] 

1771 

1772################################################## 

1773# Honeypot bot-trap settings for forms (now: only registration form) 

1774HONEYPOT_TIMER_THRESHOLD = 5000 

1775 

1776################################ 

1777# Url Shortener 

1778# ~~->URLShortener:Feature~~ 

1779 

1780# URLSHORT_LIMIT* used to limit the number of short URLs (URLSHORT_LIMIT) created 

1781# by a user within a certain time period (URLSHORT_LIMIT_WITHIN_DAYS) 

1782URLSHORT_LIMIT_WITHIN_DAYS = 7 

1783URLSHORT_LIMIT = 50_000 

1784 

1785URLSHORT_ALLOWED_SUPERDOMAINS = ['doaj.org'] 

1786URLSHORT_ALIAS_LENGTH = 6 

1787HONEYPOT_TIMER_THRESHOLD = 5000 

1788 

1789################################################## 

1790# Premium membership configurations 

1791 

1792# Should the system enforce premium membership mode 

1793PREMIUM_MODE = True 

1794 

1795# should the system respect phase-in mode, accommodating the phase-in start as the 

1796# oldest date for non-premium content 

1797PREMIUM_PHASE_IN = False 

1798PREMIUM_PHASE_IN_START = datetime(2026, 3, 19) 

1799 

1800# What is the delay non-premium users have to data access 

1801NON_PREMIUM_DELAY_SECONDS = 30 * _DAY 

1802 

1803################################################## 

1804# Object validation settings 

1805 

1806SEAMLESS_JOURNAL_LIKE_SILENT_PRUNE = False