Coverage for portality/settings.py: 99%
237 statements
« prev ^ index » next coverage.py v6.4.2, created at 2022-11-09 16:22 +0000
« prev ^ index » next coverage.py v6.4.2, created at 2022-11-09 16:22 +0000
1"""
2~~AppSettings:Config~~
3"""
4import os
5from portality import constants
6from portality.lib import paths
8###########################################
9# Application Version information
10# ~~->API:Feature~~
12DOAJ_VERSION = "6.2.8"
13API_VERSION = "3.0.1"
15######################################
16# Deployment configuration
18HOST = '0.0.0.0'
19DEBUG = False
20PORT = 5004
21SSL = True
22VALID_ENVIRONMENTS = ['dev', 'test', 'staging', 'production', 'harvester']
23CMS_BUILD_ASSETS_ON_STARTUP = False
24# Cookies security
25SESSION_COOKIE_SAMESITE='Strict'
26SESSION_COOKIE_SECURE=True
27REMEMBER_COOKIE_SECURE = True
29####################################
30# Debug Mode
32# PyCharm debug settings
33DEBUG_PYCHARM = False # do not try to connect to the PyCharm debugger by default
34DEBUG_PYCHARM_SERVER = 'localhost'
35DEBUG_PYCHARM_PORT = 6000
37#~~->DebugToolbar:Framework~~
38DEBUG_TB_TEMPLATE_EDITOR_ENABLED = True
39DEBUG_TB_INTERCEPT_REDIRECTS = False
41#######################################
42# Elasticsearch configuration
43#~~->Elasticsearch:Technology
45# elasticsearch settings # TODO: changing from single host / esprit to multi host on ES & correct the default
46ELASTIC_SEARCH_HOST = os.getenv('ELASTIC_SEARCH_HOST', 'http://localhost:9200') # remember the http:// or https://
47ELASTICSEARCH_HOSTS = [{'host': 'localhost', 'port': 9200}, {'host': 'localhost', 'port': 9201}]
48ELASTIC_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
50# 2 sets of elasticsearch DB settings - index-per-project and index-per-type. Keep both for now so we can migrate.
51# e.g. host:port/index/type/id
52ELASTIC_SEARCH_DB = "doaj"
53ELASTIC_SEARCH_TEST_DB = "doajtest"
55# e.g. host:port/type/doc/id
56ELASTIC_SEARCH_INDEX_PER_TYPE = True
57INDEX_PER_TYPE_SUBSTITUTE = '_doc' # Migrated from esprit
58ELASTIC_SEARCH_DB_PREFIX = "doaj-" # note: include the separator
59ELASTIC_SEARCH_TEST_DB_PREFIX = "doajtest-"
61INITIALISE_INDEX = True # whether or not to try creating the index and required index types on startup
62ELASTIC_SEARCH_VERSION = "1.7.5"
63ELASTIC_SEARCH_SNAPSHOT_REPOSITORY = None
64ELASTIC_SEARCH_SNAPSHOT_TTL = 366
66ES_TERMS_LIMIT = 1024
68#####################################################
69# Elastic APM config (MUST be configured in env file)
70# ~~->APM:Feature~~
72ENABLE_APM = False
74ELASTIC_APM = {
75 # Set required service name. Allowed characters:
76 # a-z, A-Z, 0-9, -, _, and space
77 'SERVICE_NAME': '',
79 # Use if APM Server requires a token
80 'SECRET_TOKEN': '',
82 # Set custom APM Server URL (default: http://localhost:8200)
83 'SERVER_URL': '',
84}
86###########################################
87# Event handler
89# use this to queue events asynchronously through kafka
90EVENT_SEND_FUNCTION = "portality.events.kafka_producer.send_event"
91# use this one to bypass kafka and process events immediately/synchronously
92# EVENT_SEND_FUNCTION = "portality.events.shortcircuit.send_event"
94KAFKA_BROKER = "kafka://localhost:9092"
95KAFKA_EVENTS_TOPIC = "events"
96KAFKA_BOOTSTRAP_SERVER = "localhost:9092"
98###########################################
99# Read Only Mode
100#
101# Use these options to place the application into READ ONLY mode
103# This puts the UI into READ_ONLY mode
104# ~~->ReadOnlyMode:Feature~~
105READ_ONLY_MODE = False
107# This puts the cron jobs into READ_ONLY mode
108SCRIPTS_READ_ONLY_MODE = False
111###########################################
112# Feature Toggles
114# ~~->OfflineMode:Feature~~
115OFFLINE_MODE = False
117# List the features we want to be active (API v1 and v2 remain with redirects to v3)
118# ~~->API:Feature~~
119FEATURES = ['api1', 'api2', 'api3']
120VALID_FEATURES = ['api1', 'api2', 'api3']
122########################################
123# File Path and URL Path settings
125# root of the git repo
126ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
128# base path, to the directory where this settings file lives
129BASE_FILE_PATH = os.path.dirname(os.path.realpath(__file__))
131BASE_URL = "https://doaj.org"
132if BASE_URL.startswith('https://'):
133 BASE_DOMAIN = BASE_URL[8:]
134elif BASE_URL.startswith('http://'):
135 BASE_DOMAIN = BASE_URL[7:]
136else:
137 BASE_DOMAIN = BASE_URL
139# ~~->API:Feature~~
140BASE_API_URL = "https://doaj.org/api/"
141API_CURRENT_BLUEPRINT_NAME = "api_v3" # change if upgrading API to new version and creating new view
143# URL used for the journal ToC URL in the journal CSV export
144# NOTE: must be the correct route as configured in view/doaj.py
145# ~~->ToC:WebRoute~~
146JOURNAL_TOC_URL_FRAG = BASE_URL + '/toc/'
148# Used when generating external links, e.g. in the API docs
149PREFERRED_URL_SCHEME = 'https'
151# Whether the app is running behind a proxy, for generating URLs based on X-Forwarded-Proto
152# ~~->ProxyFix:Framework~~
153PROXIED = False
155# directory to upload files to. MUST be full absolute path
156# The default takes the directory above this, and then down in to "upload"
157UPLOAD_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "upload")
158FAILED_ARTICLE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "failed_articles")
160# directory where reports are output
161REPORTS_BASE_DIR = "/home/cloo/reports/"
163##################################
164# File store
165# ~~->FileStore:Feature~~
167# put this in your production.cfg, to store on S3:
168# STORE_IMPL = "portality.store.StoreS3"
170STORE_IMPL = "portality.store.StoreLocal"
171STORE_TMP_IMPL = "portality.store.TempStore"
173STORE_LOCAL_DIR = paths.rel2abs(__file__, "..", "local_store", "main")
174STORE_TMP_DIR = paths.rel2abs(__file__, "..", "local_store", "tmp")
175STORE_LOCAL_EXPOSE = False # if you want to allow files in the local store to be exposed under /store/<path> urls. For dev only.
176STORE_LOCAL_WRITE_BUFFER_SIZE = 16777216
177STORE_TMP_WRITE_BUFFER_SIZE = 16777216
179# containers (buckets in AWS) where various content will be stored
180# These values are placeholders, and must be overridden in live deployment
181# this prevents test environments from accidentally writing to the production buckets
182STORE_ANON_DATA_CONTAINER = "doaj-anon-data-placeholder"
183STORE_CACHE_CONTAINER = "doaj-data-cache-placeholder"
184STORE_PUBLIC_DATA_DUMP_CONTAINER = "doaj-data-dump-placeholder"
185STORE_HARVESTER_CONTAINER = "doaj-harvester"
187# S3 credentials for relevant scopes
188# ~~->S3:Technology~~
189STORE_S3_SCOPES = {
190 "anon_data" : {
191 "aws_access_key_id" : "put this in your dev/test/production.cfg",
192 "aws_secret_access_key" : "put this in your dev/test/production.cfg"
193 },
194 "cache" : {
195 "aws_access_key_id" : "put this in your dev/test/production.cfg",
196 "aws_secret_access_key" : "put this in your dev/test/production.cfg"
197 },
198 # Used by the api_export script to dump data from the api
199 "public_data_dump" : {
200 "aws_access_key_id" : "put this in your dev/test/production.cfg",
201 "aws_secret_access_key" : "put this in your dev/test/production.cfg"
202 },
203 # Used to store harvester run logs to S3
204 "harvester" : {
205 "aws_access_key_id" : "put this in your dev/test/production.cfg",
206 "aws_secret_access_key" : "put this in your dev/test/production.cfg"
207 }
208}
210STORE_S3_MULTIPART_THRESHOLD = 5 * 1024**3 # 5GB
212####################################
213# CMS configuration
215# paths where static content should be served from.
216# * in the order you want them searched
217# * relative to the portality directory
218# ~~->CMS:DataStore~~
219STATIC_PATHS = [
220 "static",
221 "../cms/assets"
222]
224# GitHub base url where static content can be edited by the DOAJ team (you can leave out the trailing slash)
225#~~->GitHub:ExternalService~~
226CMS_EDIT_BASE_URL = "https://github.com/DOAJ/doaj/edit/static_pages/cms"
228# Where static files are served from - in case we need to serve a file
229# from there ourselves using Flask instead of nginx (e.g. to support a
230# legacy route to that file)
231# Changing this will not change the actual folder that Flask serves
232# static files from.
233# http://flask.pocoo.org/snippets/102/
234STATIC_DIR = os.path.join(ROOT_DIR, "portality", "static")
236######################################
237# Service Descriptive Text
239SERVICE_NAME = "Directory of Open Access Journals"
240SERVICE_TAGLINE = "DOAJ is an online directory that indexes and provides access to quality open access, peer-reviewed journals."
242###################################
243# Cookie settings
245# make this something secret in your overriding app.cfg
246# ~~->Cookies:Feature~~
247SECRET_KEY = "default-key"
250# Consent Cookie and other Top-Level dismissable notes
251# ~~->ConsentCookie:Feature~~
252CONSENT_COOKIE_KEY = "doaj-cookie-consent"
254# site notes, which can be configured to run any time with any content
255# ~~-> SiteNote:Feature~~
256SITE_NOTE_ACTIVE = False
257SITE_NOTE_KEY = "doaj-site-note"
258SITE_NOTE_SLEEP = 259200 # every 3 days
259SITE_NOTE_COOKIE_VALUE = "You have seen our most recent site wide announcement"
260SITE_NOTE_TEMPLATE = "doaj/site_note.html"
262####################################
263# Authorisation settings
264# ~~->AuthNZ:Feature~~
266# Can people register publicly? If false, only the superuser can create new accounts
267PUBLIC_REGISTER = True
269SUPER_USER_ROLE = "admin"
271LOGIN_VIA_ACCOUNT_ID = True
273# amount of time a reset token is valid for (86400 is 24 hours)
274PASSWORD_RESET_TIMEOUT = 86400
275# amount of time a reset token for a new account is valid for
276PASSWORD_CREATE_TIMEOUT = PASSWORD_RESET_TIMEOUT * 14
278#"api" top-level role is added to all acounts on creation; it can be revoked per account by removal of the role.
279TOP_LEVEL_ROLES = ["admin", "publisher", "editor", "associate_editor", "api", "ultra_bulk_delete", "preservation"]
281ROLE_MAP = {
282 "editor": [
283 "associate_editor", # note, these don't cascade, so we still need to list all the low-level roles
284 "edit_journal",
285 "edit_suggestion",
286 "edit_application", # todo: switchover from suggestion to application
287 "editor_area",
288 "assign_to_associate",
289 "list_group_journals",
290 "list_group_suggestions",
291 ],
292 "associate_editor": [
293 "edit_journal",
294 "edit_suggestion",
295 "editor_area",
296 ]
297}
299# If you change the reserved usernames, your data will likely need a migration to remove their
300# existing use in the system.
301SYSTEM_USERNAME = "system"
302RESERVED_USERNAMES = [SYSTEM_USERNAME] # do not allow the creation of user accounts with this id
304# Role map to destination route on login (when no other destination page is present)
305# checked in order, if the user has the role in the first tuple position, they will
306# be redirected to the endpoint in the second tuple position
307ROLE_LOGIN_DESTINATIONS = [
308 ("admin", "dashboard.top_todo"),
309 ("editor", "editor.index"),
310 ("associate_editor", "editor.index"),
311 ("publisher", "publisher.index")
312]
314# if the user doesn't have one of the above roles, where should they be sent after login
315DEFAULT_LOGIN_DESTINATION = "doaj.home"
317####################################
318# Email Settings
319# ~~->Email:ExternalService
321# Settings for Flask-Mail. Set in app.cfg
322MAIL_SERVER = None # default localhost
323MAIL_PORT = 25 # default 25
324#MAIL_USE_TLS # default False
325#MAIL_USE_SSL # default False
326#MAIL_DEBUG # default app.debug
327#MAIL_USERNAME # default None
328#MAIL_PASSWORD # default None
329#MAIL_DEFAULT_SENDER # default None
330#MAIL_MAX_EMAILS # default None
331#MAIL_SUPPRESS_SEND # default app.testing
333ENABLE_EMAIL = True
334ENABLE_PUBLISHER_EMAIL = True
336ADMIN_NAME = "DOAJ"
337ADMIN_EMAIL = "sysadmin@cottagelabs.com"
338ADMINS = ["steve@cottagelabs.com", "mark@cottagelabs.com"]
340MANAGING_EDITOR_EMAIL = "managing-editors@doaj.org"
341CONTACT_FORM_ADDRESS = "feedback+contactform@doaj.org"
342SCRIPT_TAG_DETECTED_EMAIL_RECIPIENTS = ["helpdesk@doaj.org"]
344SYSTEM_EMAIL_FROM = 'helpdesk@doaj.org'
345CC_ALL_EMAILS_TO = SYSTEM_EMAIL_FROM # DOAJ may get a dedicated inbox in the future
347# Error logging via email
348SUPPRESS_ERROR_EMAILS = False
349ERROR_LOGGING_EMAIL = 'doaj.internal@gmail.com'
350ERROR_MAIL_HOSTNAME = 'smtp.mailgun.org'
351ERROR_MAIL_USERNAME = None
352ERROR_MAIL_PASSWORD = None
354# Reports email recipient
355REPORTS_EMAIL_TO = ["helpdesk@doaj.org"]
357########################################
358# workflow email notification settings
359# ~~->WorkflowNotifications:Feature~~
361MAN_ED_IDLE_WEEKS = 4 # weeks before an application is considered reminder-worthy
362ED_IDLE_WEEKS = 3 # weeks before the editor is warned about idle applications in their group
363ASSOC_ED_IDLE_DAYS = 10
364ASSOC_ED_IDLE_WEEKS = 3
366# Which statuses the notification queries should be filtered to show
367MAN_ED_NOTIFICATION_STATUSES = [
368 constants.APPLICATION_STATUS_PENDING,
369 constants.APPLICATION_STATUS_IN_PROGRESS, constants.APPLICATION_STATUS_COMPLETED,
370 constants.APPLICATION_STATUS_ON_HOLD
371]
372ED_NOTIFICATION_STATUSES = [
373 constants.APPLICATION_STATUS_PENDING,
374 constants.APPLICATION_STATUS_IN_PROGRESS,
375 constants.APPLICATION_STATUS_COMPLETED
376]
377ASSOC_ED_NOTIFICATION_STATUSES = [
378 constants.APPLICATION_STATUS_PENDING,
379 constants.APPLICATION_STATUS_IN_PROGRESS
380]
382###################################
383# status endpoint configuration
384# ~~->StatusEndpoint:Feature~~
386# /status endpoint connection to all app machines
387APP_MACHINES_INTERNAL_IPS = [HOST + ':' + str(PORT)] # This should be set in production.cfg (or dev.cfg etc)
389###########################################
390# Background Jobs settings
391# ~~->Huey:Technology~~
392# ~~->Redis:Technology~~
393# ~~->BackgroundTasks:Feature~~
395# huey/redis settings
396HUEY_REDIS_HOST = os.getenv('HUEY_REDIS_HOST', '127.0.0.1')
397HUEY_REDIS_PORT = os.getenv('HUEY_REDIS_PORT', 6379)
398HUEY_EAGER = False
400# Crontab for never running a job - February 31st (use to disable tasks)
401CRON_NEVER = {"month": "2", "day": "31", "day_of_week": "*", "hour": "*", "minute": "*"}
403# Crontab schedules must be for unique times to avoid delays due to perceived race conditions
404HUEY_SCHEDULE = {
405 "sitemap": {"month": "*", "day": "*", "day_of_week": "*", "hour": "8", "minute": "0"},
406 "reporting": {"month": "*", "day": "1", "day_of_week": "*", "hour": "0", "minute": "0"},
407 "journal_csv": {"month": "*", "day": "*", "day_of_week": "*", "hour": "*", "minute": "35"},
408 "read_news": {"month": "*", "day": "*", "day_of_week": "*", "hour": "*", "minute": "30"},
409 "article_cleanup_sync": {"month": "*", "day": "2", "day_of_week": "*", "hour": "0", "minute": "0"},
410 "async_workflow_notifications": {"month": "*", "day": "*", "day_of_week": "1", "hour": "5", "minute": "0"},
411 "request_es_backup": {"month": "*", "day": "*", "day_of_week": "*", "hour": "6", "minute": "0"},
412 "check_latest_es_backup": {"month": "*", "day": "*", "day_of_week": "*", "hour": "9", "minute": "0"},
413 "prune_es_backups": {"month": "*", "day": "*", "day_of_week": "*", "hour": "9", "minute": "15"},
414 "public_data_dump": {"month": "*", "day": "*/6", "day_of_week": "*", "hour": "10", "minute": "0"},
415 "harvest": {"month": "*", "day": "*", "day_of_week": "*", "hour": "5", "minute": "30"},
416 "anon_export": {"month": "*", "day": "10", "day_of_week": "*", "hour": "6", "minute": "30"},
417}
419HUEY_TASKS = {
420 "ingest_articles": {"retries": 10, "retry_delay": 15},
421 "preserve": {"retries": 0, "retry_delay": 15}
422}
424####################################
425# publisher area settings
427# the earliest date accepted on the publisher's 'enter article metadata' form.
428# code will default to 15 years before current year if commented out.
429# ~~->ArticleMetadata:Page~~
430METADATA_START_YEAR = 1960
432# tick (on toc) and doaj seal settings
433# ~~->Tick:Feature~~
434TICK_THRESHOLD = '2014-03-19T00:00:00Z'
436# ~~->UpdateRequests:Feature~~
437UPDATE_REQUESTS_SHOW_OLDEST = "2018-01-01T00:00:00Z"
439##############################################
440# Elasticsearch Mappings
441# ~~->Elasticsearch:Technology~~
443FACET_FIELD = ".exact"
445# an array of DAO classes from which to retrieve the type-specific ES mappings
446# to be loaded into the index during initialisation.
447ELASTIC_SEARCH_MAPPINGS = [
448 "portality.models.Journal", # ~~->Journal:Model~~
449 "portality.models.Application", # ~~->Application:Model~~
450 "portality.models.DraftApplication", # ~~-> DraftApplication:Model~~
451 "portality.models.harvester.HarvestState", # ~~->HarvestState:Model~~
452 "portality.models.background.BackgroundJob" # ~~-> BackgroundJob:Model~~
453]
455# Map from dataobj coercion declarations to ES mappings
456# ~~->DataObj:Library~~
457# ~~->Seamless:Library~~
458DATAOBJ_TO_MAPPING_DEFAULTS = {
459 "unicode": {
460 "type": "text",
461 "fields": {
462 "exact": {
463 "type": "keyword",
464# "index": False,
465 "store": True
466 }
467 }
468 },
469 "str": {
470 "type": "text",
471 "fields": {
472 "exact": {
473 "type": "keyword",
474# "index": False,
475 "store": True
476 }
477 }
478 },
479 "unicode_upper": {
480 "type": "text",
481 "fields": {
482 "exact": {
483 "type": "keyword",
484# "index": False,
485 "store": True
486 }
487 }
488 },
489 "unicode_lower": {
490 "type": "text",
491 "fields": {
492 "exact": {
493 "type": "keyword",
494# "index": False,
495 "store": True
496 }
497 }
498 },
499 "isolang": {
500 "type": "text",
501 "fields": {
502 "exact": {
503 "type": "keyword",
504# "index": False,
505 "store": True
506 }
507 }
508 },
509 "isolang_2letter": {
510 "type": "text",
511 "fields": {
512 "exact": {
513 "type": "keyword",
514# "index": False,
515 "store": True
516 }
517 }
518 },
519 "country_code": {
520 "type": "text",
521 "fields": {
522 "exact": {
523 "type": "keyword",
524# "index": False,
525 "store": True
526 }
527 }
528 },
529 "currency_code": {
530 "type": "text",
531 "fields": {
532 "exact": {
533 "type": "keyword",
534# "index": False,
535 "store": True
536 }
537 }
538 },
539 "issn": {
540 "type": "text",
541 "fields": {
542 "exact": {
543 "type": "keyword",
544# "index": False,
545 "store": True
546 }
547 }
548 },
549 "url": {
550 "type": "text",
551 "fields": {
552 "exact": {
553 "type": "keyword",
554# "index": False,
555 "store": True
556 }
557 }
558 },
559 "utcdatetimemicros": {
560 "type": "date",
561 "format": "date_optional_time"
562 },
563 "utcdatetime": {
564 "type": "date",
565 "format": "date_optional_time"
566 },
567 "bool": {
568 "type": "boolean"
569 },
570 "integer": {
571 "type": "long"
572 },
573 "bigenddate": {
574 "type": "date",
575 "format": "date_optional_time"
576 },
577 "year": {
578 "type": "date",
579 "format": "year"
580 }
581}
583# TODO: we may want a big-type and little-type setting
584DEFAULT_INDEX_SETTINGS = \
585 {
586 'number_of_shards': 4,
587 'number_of_replicas': 1
588 }
591DEFAULT_DYNAMIC_MAPPING = {
592 'dynamic_templates': [
593 {
594 "strings": {
595 "match_mapping_type": "string",
596 "mapping": {
597 "type": "text",
598 "fields": {
599 "exact": {
600 "type": "keyword",
601 #"normalizer": "lowercase"
602 }
603 }
604 }
605 }
606 }
607 ]
608}
610# LEGACY MAPPINGS
611# a dict of the ES mappings. identify by name, and include name as first object name
612# and identifier for how non-analyzed fields for faceting are differentiated in the mappings
613MAPPINGS = {
614 'account': { #~~->Account:Model~~
615 # 'aliases': {
616 # 'account': {}
617 # },
618 'mappings': DEFAULT_DYNAMIC_MAPPING,
619 'settings': DEFAULT_INDEX_SETTINGS
620 }
621}
622# MAPPINGS['article'] = {'article': DEFAULT_DYNAMIC_MAPPING} #~~->Article:Model~~
623# MAPPINGS['upload'] = {'upload': DEFAULT_DYNAMIC_MAPPING} #~~->Upload:Model~~
624# MAPPINGS['cache'] = {'cache': DEFAULT_DYNAMIC_MAPPING} #~~->Cache:Model~~
625# MAPPINGS['lcc'] = {'lcc': DEFAULT_DYNAMIC_MAPPING} #~~->LCC:Model~~
626# MAPPINGS['editor_group'] = {'editor_group': DEFAULT_DYNAMIC_MAPPING} #~~->EditorGroup:Model~~
627# MAPPINGS['news'] = {'news': DEFAULT_DYNAMIC_MAPPING} #~~->News:Model~~
628# MAPPINGS['lock'] = {'lock': DEFAULT_DYNAMIC_MAPPING} #~~->Lock:Model~~
629# MAPPINGS['provenance'] = {'provenance': DEFAULT_DYNAMIC_MAPPING} #~~->Provenance:Model~~
630# MAPPINGS['preserve'] = {'preserve': DEFAULT_DYNAMIC_MAPPING} #~~->Preservation:Model~~
632MAPPINGS['article'] = MAPPINGS["account"] #~~->Article:Model~~
633MAPPINGS['upload'] = MAPPINGS["account"] #~~->Upload:Model~~
634MAPPINGS['cache'] = MAPPINGS["account"] #~~->Cache:Model~~
635MAPPINGS['lcc'] = MAPPINGS["account"] #~~->LCC:Model~~
636MAPPINGS['editor_group'] = MAPPINGS["account"] #~~->EditorGroup:Model~~
637MAPPINGS['news'] = MAPPINGS["account"] #~~->News:Model~~
638MAPPINGS['lock'] = MAPPINGS["account"] #~~->Lock:Model~~
639MAPPINGS['provenance'] = MAPPINGS["account"] #~~->Provenance:Model~~
640MAPPINGS['preserve'] = MAPPINGS["account"] #~~->Preservation:Model~~
641MAPPINGS['notification'] = MAPPINGS["account"] #~~->Notification:Model~~
643#########################################
644# Query Routes
645# ~~->Query:WebRoute~~
647QUERY_ROUTE = {
648 "query" : {
649 # ~~->PublicJournalQuery:Endpoint~~
650 "journal" : {
651 "auth" : False,
652 "role" : None,
653 "query_validator" : "public_query_validator",
654 "query_filters" : ["only_in_doaj", "last_update_fallback"],
655 "result_filters" : ["public_result_filter"],
656 "dao" : "portality.models.Journal", # ~~->Journal:Model~~
657 "required_parameters" : {"ref" : ["ssw", "public_journal", "subject_page"]}
658 },
659 # ~~->PublicArticleQuery:Endpoint~~
660 "article" : {
661 "auth" : False,
662 "role" : None,
663 "query_validator" : "public_query_validator",
664 "query_filters" : ["only_in_doaj"],
665 "result_filters" : ["public_result_filter"],
666 "dao" : "portality.models.Article", # ~~->Article:Model~~
667 "required_parameters" : {"ref" : ["public_article", "toc", "subject_page"]}
668 },
669 # back-compat for fixed query widget
670 # ~~->PublicJournalArticleQuery:Endpoint~~
671 "journal,article" : {
672 "auth" : False,
673 "role" : None,
674 "query_validator" : "public_query_validator",
675 "query_filters" : ["only_in_doaj", "strip_facets", "es_type_fix"],
676 "result_filters" : ["public_result_filter", "add_fqw_facets", "fqw_back_compat"],
677 "dao" : "portality.models.JournalArticle", # ~~->JournalArticle:Model~~
678 "required_parameters" : {"ref" : ["fqw"]}
679 }
680 },
681 "publisher_query" : {
682 # ~~->PublisherJournalQuery:Endpoint~~
683 "journal" : {
684 "auth" : True,
685 "role" : "publisher",
686 "query_filters" : ["owner", "only_in_doaj"],
687 "result_filters" : ["publisher_result_filter"],
688 "dao" : "portality.models.Journal" # ~~->Journal:Model~~
689 },
690 # ~~->PublisherApplicationQuery:Endpoint~~
691 "applications" : {
692 "auth" : True,
693 "role" : "publisher",
694 "query_filters" : ["owner", "not_update_request"],
695 "result_filters" : ["publisher_result_filter"],
696 "dao" : "portality.models.AllPublisherApplications" # ~~->AllPublisherApplications:Model~~
697 },
698 # ~~->PublisherUpdateRequestsQuery:Endpoint~~
699 "update_requests" : {
700 "auth" : True,
701 "role" : "publisher",
702 "query_filters" : ["owner", "update_request"],
703 "result_filters" : ["publisher_result_filter"],
704 "dao" : "portality.models.Application" # ~~->Application:Model~~
705 }
706 },
707 "admin_query" : {
708 # ~~->AdminJournalQuery:Endpoint~~
709 "journal" : {
710 "auth" : True,
711 "role" : "admin",
712 "dao" : "portality.models.Journal" # ~~->Journal:Model~~
713 },
714 # ~~->AdminApplicationQuery:Endpoint~~
715 "suggestion" : {
716 "auth" : True,
717 "role" : "admin",
718 "query_filters" : ["not_update_request"],
719 "dao" : "portality.models.Application" # ~~->Application:Model~~
720 },
721 # ~~->AdminUpdateRequestQuery:Endpoint~~
722 "update_requests": {
723 "auth": True,
724 "role": "admin",
725 "query_filters" : ["update_request"],
726 "dao": "portality.models.Application" # ~~->Application:Model~~
727 },
728 # ~~->AdminEditorGroupQuery:Endpoint~~
729 "editor,group" : {
730 "auth" : True,
731 "role" : "admin",
732 "dao" : "portality.models.EditorGroup" # ~~->EditorGroup:Model~~
733 },
734 # ~~->AdminAccountQuery:Endpoint~~
735 "account" : {
736 "auth" : True,
737 "role" : "admin",
738 "dao" : "portality.models.Account" # ~~->Account:Model~~
739 },
740 # ~~->AdminJournalArticleQuery:Endpoint~~
741 "journal,article" : {
742 "auth" : True,
743 "role" : "admin",
744 "dao" : "portality.models.search.JournalArticle" # ~~->JournalArticle:Model~~
745 },
746 # ~~->AdminBackgroundJobQuery:Endpoint~~
747 "background,job" : {
748 "auth" : True,
749 "role" : "admin",
750 "dao" : "portality.models.BackgroundJob" # ~~->BackgroundJob:Model~~
751 }
752 },
753 "associate_query" : {
754 # ~~->AssEdJournalQuery:Endpoint~~
755 "journal" : {
756 "auth" : True,
757 "role" : "associate_editor",
758 "query_filters" : ["associate"],
759 "dao" : "portality.models.Journal" # ~~->Journal:Model~~
760 },
761 # ~~->AssEdApplicationQuery:Endpoint~~
762 "suggestion" : {
763 "auth" : True,
764 "role" : "associate_editor",
765 "query_filters" : ["associate"],
766 "dao" : "portality.models.Application" # ~~->Application:Model~~
767 }
768 },
769 "editor_query" : {
770 # ~~->EditorJournalQuery:Endpoint~~
771 "journal" : {
772 "auth" : True,
773 "role" : "editor",
774 "query_filters" : ["editor"],
775 "dao" : "portality.models.Journal" # ~~->Journal:Model~~
776 },
777 # ~~->EditorApplicationQuery:Endpoint~~
778 "suggestion" : {
779 "auth" : True,
780 "role" : "editor",
781 "query_filters" : ["editor"],
782 "dao" : "portality.models.Application" # ~~->Application:Model~~
783 }
784 },
785 "api_query" : {
786 # ~~->APIArticleQuery:Endpoint~~
787 "article" : {
788 "auth" : False,
789 "role" : None,
790 "query_filters" : ["only_in_doaj", "public_source"],
791 "dao" : "portality.models.Article", # ~~->Article:Model~~
792 "required_parameters" : None,
793 "keepalive" : "10m"
794 },
795 # ~~->APIJournalQuery:Endpoint~~
796 "journal" : {
797 "auth" : False,
798 "role" : None,
799 "query_filters" : ["only_in_doaj", "public_source"],
800 "dao" : "portality.models.Journal", # ~~->Journal:Model~~
801 "required_parameters" : None
802 },
803 # ~~->APIApplicationQuery:Endpoint~~
804 "application" : {
805 "auth" : True,
806 "role" : None,
807 "query_filters" : ["owner", "private_source"],
808 "dao" : "portality.models.Suggestion", # ~~->Application:Model~~
809 "required_parameters" : None
810 }
811 }
812}
814QUERY_FILTERS = {
815 # sanitisers
816 "public_query_validator" : "portality.lib.query_filters.public_query_validator",
818 # query filters
819 "only_in_doaj" : "portality.lib.query_filters.only_in_doaj",
820 "owner" : "portality.lib.query_filters.owner",
821 "update_request" : "portality.lib.query_filters.update_request",
822 "associate" : "portality.lib.query_filters.associate",
823 "editor" : "portality.lib.query_filters.editor",
824 "strip_facets" : "portality.lib.query_filters.strip_facets",
825 "es_type_fix" : "portality.lib.query_filters.es_type_fix",
826 "last_update_fallback" : "portality.lib.query_filters.last_update_fallback",
827 "not_update_request" : "portality.lib.query_filters.not_update_request",
829 # result filters
830 "public_result_filter": "portality.lib.query_filters.public_result_filter",
831 "publisher_result_filter": "portality.lib.query_filters.publisher_result_filter",
832 "add_fqw_facets" : "portality.lib.query_filters.add_fqw_facets",
833 "fqw_back_compat" : "portality.lib.query_filters.fqw_back_compat",
835 # source filters
836 "private_source": "portality.lib.query_filters.private_source",
837 "public_source": "portality.lib.query_filters.public_source",
838}
840####################################################
841# Autocomplete
843# ~~->BibJSON:Model~~
844AUTOCOMPLETE_ADVANCED_FIELD_MAPS = {
845 "bibjson.publisher.name" : "index.publisher_ac",
846 "bibjson.institution.name" : "index.institution_ac"
847}
849####################################################
850# Application Form
851# ~~->ApplicationForm:Feature~~
853# save the public application form as a draft every 60 seconds
854PUBLIC_FORM_AUTOSAVE = 60000
857############################################
858# Atom Feed
859# ~~->AtomFeed:Feature~~
861FEED_TITLE = "DOAJ Recent Journals Added"
863# Maximum number of feed entries to be given in a single response. If this is omitted, it will
864# default to 20
865MAX_FEED_ENTRIES = 100
867# Maximum age of feed entries (in seconds) (default value here is 30 days).
868MAX_FEED_ENTRY_AGE = 2592000
870# Licensing terms for feed content
871# ~~->SiteLicence:Content~~
872FEED_LICENCE = "(c) DOAJ 2013. CC BY-SA."
874# name of the feed generator (goes in the atom:generator element)
875FEED_GENERATOR = "CottageLabs feed generator"
877# Larger image to use as the logo for all of the feeds
878# ~~->Favicon:Content~~
879FEED_LOGO = "https://doaj.org/static/doaj/images/favicon.ico"
882###########################################
883# OAI-PMH SETTINGS
884# ~~->OAIPMH:Feature~~
886# ~~->OAIAriticleXML:Crosswalk~~
887# ~~->OAIJournalXML:Crosswalk~~
888OAI_DC_METADATA_FORMAT = {
889 "metadataPrefix": "oai_dc",
890 "schema": "http://www.openarchives.org/OAI/2.0/oai_dc.xsd",
891 "metadataNamespace": "http://www.openarchives.org/OAI/2.0/oai_dc/"
892}
894OAI_DOAJ_METADATA_FORMAT = {
895 "metadataPrefix": "oai_doaj",
896 "schema": "https://doaj.org/static/doaj/doajArticles.xsd",
897 "metadataNamespace": "https://doaj.org/features/oai_doaj/1.0/"
898}
900OAIPMH_METADATA_FORMATS = {
901 # "specific endpoint": [list, of, formats, supported]
903 None: [OAI_DC_METADATA_FORMAT], # no specific endpoint, the request is to the root /oai path
904 "article": [OAI_DC_METADATA_FORMAT, OAI_DOAJ_METADATA_FORMAT]
905}
907OAIPMH_IDENTIFIER_NAMESPACE = "doaj.org"
909OAIPMH_LIST_RECORDS_PAGE_SIZE = 100
911OAIPMH_LIST_IDENTIFIERS_PAGE_SIZE = 300
913OAIPMH_RESUMPTION_TOKEN_EXPIRY = 86400
916##########################################
917# Article XML configuration
919# paths to schema files to validate incoming documents against for the various
920# crosswalks available
921# ~~->CrossrefXML:Schema~~
922# ~~->DOAJArticleXML:Schema~~
923SCHEMAS = {
924 "doaj": os.path.join(BASE_FILE_PATH, "static", "doaj", "doajArticles.xsd"),
925 "crossref442": os.path.join(BASE_FILE_PATH, "static", "crossref", "crossref4.4.2.xsd"),
926 "crossref531": os.path.join(BASE_FILE_PATH, "static", "crossref", "crossref5.3.1.xsd")
927}
929# placeholders for the loaded schemas
930DOAJ_SCHEMA = None
931CROSSREF442_SCHEMA = None
932CROSSREF531_SCHEMA = None
933LOAD_CROSSREF_THREAD = None
935# mapping of format names to modules which implement the crosswalks
936# ~~->DOAJArticleXML:Crosswalk~~
937# ~~->CrossrefXML:Crosswalk~~
938ARTICLE_CROSSWALKS = {
939 "doaj": "portality.crosswalks.article_doaj_xml.DOAJXWalk",
940 "crossref442": "portality.crosswalks.article_crossref_xml.CrossrefXWalk442",
941 "crossref531": "portality.crosswalks.article_crossref_xml.CrossrefXWalk531"
942}
944# maximum size of files that can be provided by-reference (the default value is 250Mb)
945MAX_REMOTE_SIZE = 262144000
947#################################################
948# Cache settings
949# ~~->Cache:Feature~~
951# number of seconds site statistics should be considered fresh
952# 1800s = 30mins
953SITE_STATISTICS_TIMEOUT = 1800
955# directory into which to put files which are cached (e.g. the csv)
956CACHE_DIR = os.path.join(ROOT_DIR, "cache")
958# Article and Journal History directories - they should be different
959# ~~->ArticleHistory:Feature~~
960# ~~->JournalHistory:Feature~~
961ARTICLE_HISTORY_DIR = os.path.join(ROOT_DIR, "history", "article")
962JOURNAL_HISTORY_DIR = os.path.join(ROOT_DIR, "history", "journal")
965#################################################
966# Sitemap settings
967# ~~->Sitemap:Feature~~
969# approximate rate of change of the Table of Contents for journals
970TOC_CHANGEFREQ = "monthly"
974##################################################
975# News feed settings
976# ~~->News:Feature~~
978BLOG_URL = "http://doajournals.wordpress.com/"
980BLOG_FEED_URL = "http://doajournals.wordpress.com/feed/atom/"
982FRONT_PAGE_NEWS_ITEMS = 6
984NEWS_PAGE_NEWS_ITEMS = 20
987##################################################
988# Edit Lock settings
989# ~~->Lock:Feature~~
991# amount of time loading an editable page locks it for, in seconds.
992EDIT_LOCK_TIMEOUT = 1200
994# amount of time a background task can lock a resource for, in seconds
995BACKGROUND_TASK_LOCK_TIMEOUT = 3600
998###############################################
999# Bit.ly configuration
1000# ~~->Bitly:ExternalService~~
1002# bit,ly api shortening service
1003BITLY_SHORTENING_API_URL = "https://api-ssl.bitly.com/v4/shorten"
1005# bitly oauth token
1006# ENTER YOUR OWN TOKEN IN APPROPRIATE .cfg FILE
1007BITLY_OAUTH_TOKEN = ""
1009###############################################
1010# Date handling
1011#
1012# when dates.format is called without a format argument, what format to use?
1013# FIXME: this is actually wrong - should really use the timezone correctly
1014DEFAULT_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
1016# date formats that we know about, and should try, in order, when parsing
1017DATE_FORMATS = [
1018 "%Y-%m-%dT%H:%M:%S.%fZ", # e.g. 2010-01-01T00:00:00.000Z
1019 "%Y-%m-%dT%H:%M:%SZ", # e.g. 2014-09-23T11:30:45Z
1020 "%Y-%m-%d", # e.g. 2014-09-23
1021 "%d/%m/%y", # e.g. 29/02/80
1022 "%d/%m/%Y", # e.g. 29/02/1980
1023 "%d-%m-%Y", # e.g. 01-01-2015
1024 "%Y.%m.%d", # e.g. 2014.09.12
1025 "%d.%m.%Y", # e.g. 12.9.2014
1026 "%d.%m.%y", # e.g. 12.9.14
1027 "%d %B %Y", # e.g. 21 June 2014
1028 "%d-%b-%Y", # e.g. 31-Jul-13
1029 "%d-%b-%y", # e.g. 31-Jul-2013
1030 "%b-%y", # e.g. Aug-13
1031 "%B %Y", # e.g. February 2014
1032 "%Y" # e.g. 1978
1033]
1035# The last_manual_update field was initialised to this value. Used to label as 'never'.
1036DEFAULT_TIMESTAMP = "1970-01-01T00:00:00Z"
1038#################################################
1039# API configuration
1040# ~~->API:Feature~~
1041# ~~->APISearch:Feature~~
1043# maximum number of records to return per page
1044DISCOVERY_MAX_PAGE_SIZE = 100
1046# maximum number of records to return in total (a request for a page starting beyond this number will fail)
1047DISCOVERY_MAX_RECORDS_SIZE = 1000
1049# ~~->ArticleBibJSON:Model~~
1050DISCOVERY_ARTICLE_SEARCH_SUBS = {
1051 "title" : "bibjson.title",
1052 "doi" : "bibjson.identifier.id.exact",
1053 "issn" : "index.issn.exact",
1054 "publisher" : "bibjson.journal.publisher",
1055 "journal" : "bibjson.journal.title",
1056 "abstract" : "bibjson.abstract"
1057}
1059DISCOVERY_ARTICLE_SORT_SUBS = {
1060 "title" : "index.unpunctitle.exact"
1061}
1063# ~~->JournalBibJSON:Model~~
1064DISCOVERY_JOURNAL_SEARCH_SUBS = {
1065 "title" : "index.title",
1066 "issn" : "index.issn.exact",
1067 "publisher" : "bibjson.publisher",
1068 "license" : "index.license.exact",
1069 "username" : "admin.owner.exact"
1070}
1072DISCOVERY_JOURNAL_SORT_SUBS = {
1073 "title" : "index.unpunctitle.exact",
1074 "issn" : "index.issn.exact"
1075}
1077DISCOVERY_APPLICATION_SEARCH_SUBS = {
1078 "title" : "index.title",
1079 "issn" : "index.issn.exact",
1080 "publisher" : "bibjson.publisher",
1081 "license" : "index.license.exact"
1082}
1084DISCOVERY_APPLICATION_SORT_SUBS = {
1085 "title" : "index.unpunctitle.exact",
1086 "issn" : "index.issn.exact"
1087}
1089# API data dump settings
1090DISCOVERY_BULK_PAGE_SIZE = 1000
1091DISCOVERY_RECORDS_PER_FILE = 100000
1094######################################################
1095# Hotjar configuration
1096# ~~->Hotjar:ExternalService~~
1098# hotjar id - only activate this in production
1099HOTJAR_ID = ""
1102######################################################
1103# Google Analytics configuration
1104# specify in environment .cfg file - avoids sending live analytics
1105# events from test and dev environments
1106# ~~->GoogleAnalytics:ExternalService~~
1108GOOGLE_ANALYTICS_ID = ''
1110# Where to put the google analytics logs
1111GOOGLE_ANALTYICS_LOG_DIR = None
1113# Google Analytics custom dimensions. These are configured in the GA interface.
1114GA_DIMENSIONS = {
1115 'oai_res_id': 'dimension1', # In GA as OAI:Record
1116}
1118# GA for OAI-PMH
1119# ~~-> OAIPMH:Feature~~
1120GA_CATEGORY_OAI = 'OAI-PMH'
1122# GA for Atom
1123# ~~-> Atom:Feature~~
1124GA_CATEGORY_ATOM = 'Atom'
1125GA_ACTION_ACTION = 'Feed request'
1127# GA for JournalCSV
1128# ~~-> JournalCSV:Feature~~
1129GA_CATEGORY_JOURNALCSV = 'JournalCSV'
1130GA_ACTION_JOURNALCSV = 'Download'
1132# GA for OpenURL
1133# ~~->OpenURL:Feature~~
1134GA_CATEGORY_OPENURL = 'OpenURL'
1136# GA for API
1137# ~~-> API:Feature~~
1138GA_CATEGORY_API = 'API Hit'
1139GA_ACTIONS_API = {
1140 'search_applications': 'Search applications',
1141 'search_journals': 'Search journals',
1142 'search_articles': 'Search articles',
1143 'create_application': 'Create application',
1144 'retrieve_application': 'Retrieve application',
1145 'update_application': 'Update application',
1146 'delete_application': 'Delete application',
1147 'create_article': 'Create article',
1148 'retrieve_article': 'Retrieve article',
1149 'update_article': 'Update article',
1150 'delete_article': 'Delete article',
1151 'retrieve_journal': 'Retrieve journal',
1152 'bulk_application_create': 'Bulk application create',
1153 'bulk_application_delete': 'Bulk application delete',
1154 'bulk_article_create': 'Bulk article create',
1155 'bulk_article_delete': 'Bulk article delete'
1156}
1159# GA for fixed query widget
1160# ~~->FixedQueryWidget:Feature~~
1161GA_CATEGORY_FQW = 'FQW'
1162GA_ACTION_FQW = 'Hit'
1164#####################################################
1165# Anonymised data export (for dev) configuration
1166# ~~->AnonExport:Feature~~
1168ANON_SALT = 'changeme'
1170# ========================================
1171# Quick Reject Feature Config
1172# ~~->QuickReject:Feature~~
1174QUICK_REJECT_REASONS = [
1175 "The journal has not published enough research content to qualify for DOAJ inclusion.",
1176 "The ISSN is incorrect, provisional or not registered with issn.org.",
1177 "The URL(s) provided in the application do not work.",
1178 "The journal is already in DOAJ.",
1179 "The journal is not Open Access.",
1180 "The journal title in the application and/or website does not match the title at issn.org.",
1181 "The application has incorrect answers or the URLs given do not provide the required information.",
1182 "This application is a duplicate.",
1183 "The full-text articles are not available article by article with individual links.",
1184 "The information in the journal implies that it does not employ a fair & robust peer review process.",
1185 "The journal or publisher has been previously rejected or removed from DOAJ.",
1186 "The journal's copyright policy is not available or unclear.",
1187 "The journal's licensing policy is not available or unclear.",
1188 "The information about the journal is in different languages.",
1189 "The information about the journal is not the same in all languages.",
1190 "The journal makes a false claim to be indexed in DOAJ or other databases or displays non-standard Impact Factors.",
1191 "The journal does not employ good publishing practices."
1192]
1194MINIMAL_OA_START_DATE = 1900
1197#############################################
1198## Harvester Configuration
1199# ~~->Harvester:Feature~~
1201## Configuration options for the DOAJ API Client
1203## EPMC Client configuration
1204# ~~-> EPMC:ExternalService~~
1205EPMC_REST_API = "https://www.ebi.ac.uk/europepmc/webservices/rest/"
1206EPMC_TARGET_VERSION = "6.6" # doc here: https://europepmc.org/docs/Europe_PMC_RESTful_Release_Notes.pdf
1207EPMC_HARVESTER_THROTTLE = 0.2
1209# General harvester configuration
1210HARVESTERS = [
1211 "portality.tasks.harvester_helpers.epmc.epmc_harvester.EPMCHarvester"
1212]
1214INITIAL_HARVEST_DATE = "2015-12-01T00:00:00Z"
1216# List of account ids to harvest from. MUST NOT be checked into the repo, put these
1217# in the local.cfg instead
1218HARVEST_ACCOUNTS = []
1220# Amount of time a harvester record is allowed to be in "queued" or "processing" state before we
1221# assume it's a zombie, and ignore it
1222HARVESTER_ZOMBIE_AGE = 604800
1224#######################################################
1225# ReCAPTCHA configuration
1226# ~~->ReCAPTCHA:ExternalService
1228#Recaptcha test keys, should be overridden in dev.cfg by the keys obtained from Google ReCaptcha v2
1229RECAPTCHA_ENABLE = True
1230RECAPTCHA_SITE_KEY = '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI'
1231RECAPTCHA_SECRET_KEY = "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
1233#######################################################
1234# Preservation configuration
1235# ~~->Preservation:Feature
1236PRESERVATION_URL = "http://PresevatinURL"
1237PRESERVATION_USERNAME = "user_name"
1238PRESERVATION_PASSWD = "password"
1239PRESERVATION_COLLECTION = {}
1242#########################################################
1243# Background tasks --- anon export
1244TASKS_ANON_EXPORT_CLEAN = False
1245TASKS_ANON_EXPORT_LIMIT = None
1246TASKS_ANON_EXPORT_BATCH_SIZE = 100000
1248########################################
1249# Editorial Dashboard - set to-do list size
1250TODO_LIST_SIZE = 48
1252#######################################################
1253# Plausible analytics
1254# root url of plausible
1255PLAUSIBLE_URL = "https://plausible.io"
1256PLAUSIBLE_JS_URL = PLAUSIBLE_URL + "/js/plausible.js"
1257PLAUSIBLE_API_URL = PLAUSIBLE_URL + "/api/event/"
1258# site name / domain name that used to register in plausible
1259PLAUSIBLE_SITE_NAME = BASE_DOMAIN
1260PLAUSIBLE_LOG_DIR = None