diff --git a/disco/apps.py b/disco/apps.py index 2a32879..e20ca95 100644 --- a/disco/apps.py +++ b/disco/apps.py @@ -9,4 +9,4 @@ class DiscoConfig(AppConfig): is_arches_application = True def ready(self): - generate_frontend_configuration() \ No newline at end of file + generate_frontend_configuration() diff --git a/disco/celery.py b/disco/celery.py index d3c8f71..4d3e9ff 100644 --- a/disco/celery.py +++ b/disco/celery.py @@ -7,7 +7,7 @@ if platform.system().lower() == "windows": os.environ.setdefault("FORKED_BY_MULTIPROCESSING", "1") -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'disco.settings') -app = Celery('disco') -app.config_from_object('django.conf:settings', namespace='CELERY') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "disco.settings") +app = Celery("disco") +app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() diff --git a/disco/migrations/0001_initial.py b/disco/migrations/0001_initial.py index 9048d53..dedef83 100644 --- a/disco/migrations/0001_initial.py +++ b/disco/migrations/0001_initial.py @@ -6,6 +6,7 @@ from django.conf import settings import os + class Migration(migrations.Migration): initial = True @@ -22,10 +23,10 @@ def add_document_templates(apps, schema_editor): name="Example Template", description="This is an example template", defaults={ - "template":"document_templates/example-template.docx", - "preview":"document_templates/72cc4dcf-9500-418f-a42e-7d980937a9db_preview.pdf", - "thumbnail":"document_templates/72cc4dcf-9500-418f-a42e-7d980937a9db_thumbnail.png", - } + "template": "document_templates/example-template.docx", + "preview": "document_templates/72cc4dcf-9500-418f-a42e-7d980937a9db_preview.pdf", + "thumbnail": "document_templates/72cc4dcf-9500-418f-a42e-7d980937a9db_thumbnail.png", + }, ) file_names = ( @@ -35,13 +36,19 @@ def add_document_templates(apps, schema_editor): ) for name in file_names: - with open(os.path.join(settings.APP_NAME, 'document_templates', name), 'rb') as template_file: - default_storage.save(os.path.join('document_templates', name), File(template_file)) + with open( + os.path.join(settings.APP_NAME, "document_templates", name), "rb" + ) as template_file: + default_storage.save( + os.path.join("document_templates", name), File(template_file) + ) def remove_document_templates(apps, schema_editor): ArchesTemplate = apps.get_model("arches_templating", "ArchesTemplate") - example_template = ArchesTemplate.objects.get(templateid="72cc4dcf-9500-418f-a42e-7d980937a9db") + example_template = ArchesTemplate.objects.get( + templateid="72cc4dcf-9500-418f-a42e-7d980937a9db" + ) example_template.delete() operations = [ diff --git a/disco/search_indexes/sample_index.py b/disco/search_indexes/sample_index.py index 3c23155..cd9c1e8 100644 --- a/disco/search_indexes/sample_index.py +++ b/disco/search_indexes/sample_index.py @@ -3,8 +3,18 @@ class SampleIndex(BaseIndex): def prepare_index(self): - self.index_metadata = {"mappings": {"properties": {"tile_count": {"type": "keyword"}, "graph_id": {"type": "keyword"}}}} + self.index_metadata = { + "mappings": { + "properties": { + "tile_count": {"type": "keyword"}, + "graph_id": {"type": "keyword"}, + } + } + } super(SampleIndex, self).prepare_index() def get_documents_to_index(self, resourceinstance, tiles): - return ({"tile_count": len(tiles), "graph_id": resourceinstance.graph_id}, str(resourceinstance.resourceinstanceid)) + return ( + {"tile_count": len(tiles), "graph_id": resourceinstance.graph_id}, + str(resourceinstance.resourceinstanceid), + ) diff --git a/disco/settings.py b/disco/settings.py index ab6964f..87a783d 100644 --- a/disco/settings.py +++ b/disco/settings.py @@ -10,7 +10,7 @@ from arches.settings import * -APP_NAME = 'disco' +APP_NAME = "disco" APP_VERSION = semantic_version.Version(major=0, minor=0, patch=0) APP_ROOT = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) MIN_ARCHES_VERSION = arches.__version__ @@ -18,30 +18,49 @@ WEBPACK_LOADER = { "DEFAULT": { - "STATS_FILE": os.path.join(APP_ROOT, '..', 'webpack/webpack-stats.json'), + "STATS_FILE": os.path.join(APP_ROOT, "..", "webpack/webpack-stats.json"), }, } -DATATYPE_LOCATIONS.append('disco.datatypes') -FUNCTION_LOCATIONS.append('arches_for_science.functions') -FUNCTION_LOCATIONS.append('disco.functions') -ETL_MODULE_LOCATIONS.append('disco.etl_modules') -SEARCH_COMPONENT_LOCATIONS.append('disco.search_components') +DATATYPE_LOCATIONS.append("disco.datatypes") +FUNCTION_LOCATIONS.append("arches_for_science.functions") +FUNCTION_LOCATIONS.append("disco.functions") +ETL_MODULE_LOCATIONS.append("disco.etl_modules") +SEARCH_COMPONENT_LOCATIONS.append("disco.search_components") -LOCALE_PATHS.insert(0, os.path.join(APP_ROOT, 'locale')) +LOCALE_PATHS.insert(0, os.path.join(APP_ROOT, "locale")) FILE_TYPE_CHECKING = False -FILE_TYPES = ["bmp", "gif", "jpg", "jpeg", "pdf", "png", "psd", "rtf", "tif", "tiff", "xlsx", "csv", "zip", "json"] +FILE_TYPES = [ + "bmp", + "gif", + "jpg", + "jpeg", + "pdf", + "png", + "psd", + "rtf", + "tif", + "tiff", + "xlsx", + "csv", + "zip", + "json", +] # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 't*#3-h-$cj^1^c(g6f6%pn2&1-g@!5tk%k2!n0!ns(s@2ck!*1' +SECRET_KEY = "t*#3-h-$cj^1^c(g6f6%pn2&1-g@!5tk%k2!n0!ns(s@2ck!*1" # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ROOT_URLCONF = 'disco.urls' +ROOT_URLCONF = "disco.urls" # Modify this line as needed for your project to connect to elasticsearch with a password that you generate -ELASTICSEARCH_CONNECTION_OPTIONS = {"request_timeout": 30, "verify_certs": False, "basic_auth": ("elastic", "E1asticSearchforArche5")} +ELASTICSEARCH_CONNECTION_OPTIONS = { + "request_timeout": 30, + "verify_certs": False, + "basic_auth": ("elastic", "E1asticSearchforArche5"), +} # If you need to connect to Elasticsearch via an API key instead of username/password, use the syntax below: # ELASTICSEARCH_CONNECTION_OPTIONS = {"timeout": 30, "verify_certs": False, "api_key": ""} @@ -56,7 +75,7 @@ # Or Kibana: https://www.elastic.co/guide/en/kibana/current/api-keys.html # a prefix to append to all elasticsearch indexes, note: must be lower case -ELASTICSEARCH_PREFIX = 'disco' +ELASTICSEARCH_PREFIX = "disco" ELASTICSEARCH_CUSTOM_INDEXES = [] # [{ @@ -94,14 +113,9 @@ "PASSWORD": "postgis", "PORT": "5432", "POSTGIS_TEMPLATE": "template_postgis", - "TEST": { - "CHARSET": None, - "COLLATION": None, - "MIRROR": None, - "NAME": None - }, + "TEST": {"CHARSET": None, "COLLATION": None, "MIRROR": None, "NAME": None}, "TIME_ZONE": None, - "USER": "postgres" + "USER": "postgres", } } @@ -166,25 +180,27 @@ "arches.app.utils.context_processors.livereload", "arches.app.utils.context_processors.map_info", "arches.app.utils.context_processors.app_settings", - "arches_for_science.utils.context_processors.project_settings" - ] + "arches_for_science.utils.context_processors.project_settings", + ], ) ALLOWED_HOSTS = [] -SYSTEM_SETTINGS_LOCAL_PATH = os.path.join(APP_ROOT, 'system_settings', 'System_Settings.json') -WSGI_APPLICATION = 'disco.wsgi.application' +SYSTEM_SETTINGS_LOCAL_PATH = os.path.join( + APP_ROOT, "system_settings", "System_Settings.json" +) +WSGI_APPLICATION = "disco.wsgi.application" # URL that handles the media served from MEDIA_ROOT, used for managing stored files. # It must end in a slash if set to a non-empty value. -MEDIA_URL = '/files/' +MEDIA_URL = "/files/" # Absolute filesystem path to the directory that will hold user-uploaded files. -MEDIA_ROOT = os.path.join(APP_ROOT) +MEDIA_ROOT = os.path.join(APP_ROOT) # URL prefix for static files. # Example: "http://media.lawrence.com/static/" -STATIC_URL = '/static/' +STATIC_URL = "/static/" # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files @@ -195,37 +211,37 @@ # when hosting Arches under a sub path set this value to the sub path eg : "/{sub_path}/" FORCE_SCRIPT_NAME = None -RESOURCE_IMPORT_LOG = os.path.join(APP_ROOT, 'logs', 'resource_import.log') -DEFAULT_RESOURCE_IMPORT_USER = {'username': 'admin', 'userid': 1} +RESOURCE_IMPORT_LOG = os.path.join(APP_ROOT, "logs", "resource_import.log") +DEFAULT_RESOURCE_IMPORT_USER = {"username": "admin", "userid": 1} LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'console': { - 'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "console": { + "format": "%(asctime)s %(name)-12s %(levelname)-8s %(message)s", }, }, - 'handlers': { - 'file': { - 'level': 'WARNING', # DEBUG, INFO, WARNING, ERROR - 'class': 'logging.FileHandler', - 'filename': os.path.join(APP_ROOT, 'arches.log'), - 'formatter': 'console' + "handlers": { + "file": { + "level": "WARNING", # DEBUG, INFO, WARNING, ERROR + "class": "logging.FileHandler", + "filename": os.path.join(APP_ROOT, "arches.log"), + "formatter": "console", + }, + "console": { + "level": "WARNING", + "class": "logging.StreamHandler", + "formatter": "console", }, - 'console': { - 'level': 'WARNING', - 'class': 'logging.StreamHandler', - 'formatter': 'console' - } }, - 'loggers': { - 'arches': { - 'handlers': ['file', 'console'], - 'level': 'WARNING', - 'propagate': True + "loggers": { + "arches": { + "handlers": ["file", "console"], + "level": "WARNING", + "propagate": True, } - } + }, } @@ -233,16 +249,16 @@ DATA_UPLOAD_MAX_MEMORY_SIZE = 15728640 # Unique session cookie ensures that logins are treated separately for each app -SESSION_COOKIE_NAME = 'disco' +SESSION_COOKIE_NAME = "disco" # For more info on configuring your cache: https://docs.djangoproject.com/en/2.2/topics/cache/ CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', + "default": { + "BACKEND": "django.core.cache.backends.dummy.DummyCache", }, - 'user_permission': { - 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', - 'LOCATION': 'user_permission_cache', + "user_permission": { + "BACKEND": "django.core.cache.backends.db.DatabaseCache", + "LOCATION": "user_permission_cache", }, } @@ -252,23 +268,25 @@ BYPASS_UNIQUE_CONSTRAINT_TILE_VALIDATION = False BYPASS_REQUIRED_VALUE_TILE_VALIDATION = False -DATE_IMPORT_EXPORT_FORMAT = "%Y-%m-%d" # Custom date format for dates imported from and exported to csv +DATE_IMPORT_EXPORT_FORMAT = ( + "%Y-%m-%d" # Custom date format for dates imported from and exported to csv +) # This is used to indicate whether the data in the CSV and SHP exports should be # ordered as seen in the resource cards or not. EXPORT_DATA_FIELDS_IN_CARD_ORDER = False -#Identify the usernames and duration (seconds) for which you want to cache the time wheel -CACHE_BY_USER = {'anonymous': 3600 * 24} -TILE_CACHE_TIMEOUT = 600 #seconds -CLUSTER_DISTANCE_MAX = 5000 #meters +# Identify the usernames and duration (seconds) for which you want to cache the time wheel +CACHE_BY_USER = {"anonymous": 3600 * 24} +TILE_CACHE_TIMEOUT = 600 # seconds +CLUSTER_DISTANCE_MAX = 5000 # meters GRAPH_MODEL_CACHE_TIMEOUT = None -OAUTH_CLIENT_ID = '' +OAUTH_CLIENT_ID = "" -APP_TITLE = 'Arches | Heritage Data Management' -COPYRIGHT_TEXT = 'All Rights Reserved.' -COPYRIGHT_YEAR = '2019' +APP_TITLE = "Arches | Heritage Data Management" +COPYRIGHT_TEXT = "All Rights Reserved." +COPYRIGHT_YEAR = "2019" ENABLE_CAPTCHA = False # RECAPTCHA_PUBLIC_KEY = '' @@ -289,17 +307,26 @@ DEFAULT_FROM_EMAIL = EMAIL_HOST_USER -CELERY_BROKER_URL = "" # RabbitMQ --> "amqp://guest:guest@localhost", Redis --> "redis://localhost:6379/0" -CELERY_ACCEPT_CONTENT = ['json'] -CELERY_RESULT_BACKEND = 'django-db' # Use 'django-cache' if you want to use your cache as your backend -CELERY_TASK_SERIALIZER = 'json' +CELERY_BROKER_URL = "" # RabbitMQ --> "amqp://guest:guest@localhost", Redis --> "redis://localhost:6379/0" +CELERY_ACCEPT_CONTENT = ["json"] +CELERY_RESULT_BACKEND = ( + "django-db" # Use 'django-cache' if you want to use your cache as your backend +) +CELERY_TASK_SERIALIZER = "json" CELERY_SEARCH_EXPORT_EXPIRES = 24 * 3600 # seconds CELERY_SEARCH_EXPORT_CHECK = 3600 # seconds CELERY_BEAT_SCHEDULE = { - "delete-expired-search-export": {"task": "arches.app.tasks.delete_file", "schedule": CELERY_SEARCH_EXPORT_CHECK,}, - "notification": {"task": "arches.app.tasks.message", "schedule": CELERY_SEARCH_EXPORT_CHECK, "args": ("Celery Beat is Running",),}, + "delete-expired-search-export": { + "task": "arches.app.tasks.delete_file", + "schedule": CELERY_SEARCH_EXPORT_CHECK, + }, + "notification": { + "task": "arches.app.tasks.message", + "schedule": CELERY_SEARCH_EXPORT_CHECK, + "args": ("Celery Beat is Running",), + }, } # Set to True if you want to send celery tasks to the broker without being able to detect celery. @@ -349,10 +376,10 @@ # {langcode}-{regioncode} eg: en, en-gb .... # a list of language codes can be found here http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGES = [ -# ('de', _('German')), - ('en', _('English')), -# ('en-gb', _('British English')), -# ('es', _('Spanish')), + # ('de', _('German')), + ("en", _("English")), + # ('en-gb', _('British English')), + # ('es', _('Spanish')), ] # override this to permenantly display/hide the language switcher @@ -369,7 +396,7 @@ AWS_STORAGE_BUCKET_NAME = "disco-dev-test-bucket" -DOCKER=False +DOCKER = False RENDERERS += [ { @@ -402,7 +429,7 @@ "iconclass": "fa fa-bolt", "component": "views/components/cards/file-renderers/raman-reader", "ext": "txt", - "type": "text/plain", + "type": "text/plain", "exclude": "", }, { @@ -435,7 +462,7 @@ "iconclass": "fa fa-bolt", "component": "views/components/cards/file-renderers/xy-reader", "ext": "txt", - "type": "text/plain", + "type": "text/plain", "exclude": "", }, ] @@ -443,15 +470,51 @@ X_FRAME_OPTIONS = "SAMEORIGIN" FORMATS = [ - {"name": "Bruker M6 (point)", "id": "bm6", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Bruker 5g", "id": "b5g", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Bruker Tracer IV-V", "id": "bt45", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Bruker Tracer III", "id": "bt3", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Bruker 5i", "id": "b5i", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Bruker Artax", "id": "bart", "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042"}, - {"name": "Renishaw InVia - 785", "id": "r785", "renderer": "94fa1720-6773-4f99-b49b-4ea0926b3933"}, - {"name": "Ranishsaw inVia - 633/514", "id": "r633", "renderer": "94fa1720-6773-4f99-b49b-4ea0926b3933"}, - {"name": "ASD FieldSpec IV hi res", "id": "asd", "renderer": "88dccb59-14e3-4445-8f1b-07f0470b38bb"}, + { + "name": "Bruker M6 (point)", + "id": "bm6", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Bruker 5g", + "id": "b5g", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Bruker Tracer IV-V", + "id": "bt45", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Bruker Tracer III", + "id": "bt3", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Bruker 5i", + "id": "b5i", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Bruker Artax", + "id": "bart", + "renderer": "31be40ae-dbe6-4f41-9c13-1964d7d17042", + }, + { + "name": "Renishaw InVia - 785", + "id": "r785", + "renderer": "94fa1720-6773-4f99-b49b-4ea0926b3933", + }, + { + "name": "Ranishsaw inVia - 633/514", + "id": "r633", + "renderer": "94fa1720-6773-4f99-b49b-4ea0926b3933", + }, + { + "name": "ASD FieldSpec IV hi res", + "id": "asd", + "renderer": "88dccb59-14e3-4445-8f1b-07f0470b38bb", + }, ] XY_TEXT_FILE_FORMATS = ["dx", "txt"] @@ -459,7 +522,7 @@ try: from .package_settings import * except ImportError: - try: + try: from package_settings import * except ImportError as e: pass @@ -467,7 +530,7 @@ try: from .settings_local import * except ImportError as e: - try: + try: from settings_local import * except ImportError as e: pass @@ -475,10 +538,10 @@ if DOCKER: try: from .settings_docker import * - except ImportError: + except ImportError: try: from settings_docker import * except ImportError as e: pass -# returns an output that can be read by NODEJS \ No newline at end of file +# returns an output that can be read by NODEJS diff --git a/disco/settings_docker.py b/disco/settings_docker.py index 7f553b3..41b8455 100644 --- a/disco/settings_docker.py +++ b/disco/settings_docker.py @@ -41,9 +41,17 @@ def get_optional_env_variable(var_name): get_env_variable("RABBITMQ_USER"), get_env_variable("RABBITMQ_PASS") ) # RabbitMQ --> "amqp://guest:guest@localhost", Redis --> "redis://localhost:6379/0" -CANTALOUPE_HTTP_ENDPOINT = "http://{}:{}".format(get_env_variable("CANTALOUPE_HOST"), get_env_variable("CANTALOUPE_PORT")) +CANTALOUPE_HTTP_ENDPOINT = "http://{}:{}".format( + get_env_variable("CANTALOUPE_HOST"), get_env_variable("CANTALOUPE_PORT") +) ELASTICSEARCH_HTTP_PORT = get_env_variable("ESPORT") -ELASTICSEARCH_HOSTS = [{"scheme": "http", "host": get_env_variable("ESHOST"), "port": int(ELASTICSEARCH_HTTP_PORT)}] +ELASTICSEARCH_HOSTS = [ + { + "scheme": "http", + "host": get_env_variable("ESHOST"), + "port": int(ELASTICSEARCH_HTTP_PORT), + } +] USER_ELASTICSEARCH_PREFIX = get_optional_env_variable("ELASTICSEARCH_PREFIX") if USER_ELASTICSEARCH_PREFIX: diff --git a/disco/urls.py b/disco/urls.py index bba30fc..f4005d5 100644 --- a/disco/urls.py +++ b/disco/urls.py @@ -16,4 +16,4 @@ if settings.SHOW_LANGUAGE_SWITCH is True: urlpatterns = i18n_patterns(*urlpatterns) - urlpatterns.append(path("i18n/", include("django.conf.urls.i18n"))) \ No newline at end of file + urlpatterns.append(path("i18n/", include("django.conf.urls.i18n"))) diff --git a/disco/wsgi.py b/disco/wsgi.py index e4bcfe1..db26eac 100644 --- a/disco/wsgi.py +++ b/disco/wsgi.py @@ -1,4 +1,4 @@ -''' +""" ARCHES - a program developed to inventory and manage immovable cultural heritage. Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund @@ -14,11 +14,12 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -''' +""" import os import sys import inspect + path = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) if path not in sys.path: @@ -27,10 +28,12 @@ # reverting back to the old style of setting the DJANGO_SETTINGS_MODULE env variable # refer to the following blog post under the heading "Leaking of process environment variables." # http://blog.dscpl.com.au/2012/10/requests-running-in-wrong-django.html -os.environ['DJANGO_SETTINGS_MODULE'] = "disco.settings" +os.environ["DJANGO_SETTINGS_MODULE"] = "disco.settings" from django.core.wsgi import get_wsgi_application + application = get_wsgi_application() from arches.app.models.system_settings import settings + settings.update_from_db() diff --git a/docker/settings_docker.py b/docker/settings_docker.py index 7f553b3..41b8455 100644 --- a/docker/settings_docker.py +++ b/docker/settings_docker.py @@ -41,9 +41,17 @@ def get_optional_env_variable(var_name): get_env_variable("RABBITMQ_USER"), get_env_variable("RABBITMQ_PASS") ) # RabbitMQ --> "amqp://guest:guest@localhost", Redis --> "redis://localhost:6379/0" -CANTALOUPE_HTTP_ENDPOINT = "http://{}:{}".format(get_env_variable("CANTALOUPE_HOST"), get_env_variable("CANTALOUPE_PORT")) +CANTALOUPE_HTTP_ENDPOINT = "http://{}:{}".format( + get_env_variable("CANTALOUPE_HOST"), get_env_variable("CANTALOUPE_PORT") +) ELASTICSEARCH_HTTP_PORT = get_env_variable("ESPORT") -ELASTICSEARCH_HOSTS = [{"scheme": "http", "host": get_env_variable("ESHOST"), "port": int(ELASTICSEARCH_HTTP_PORT)}] +ELASTICSEARCH_HOSTS = [ + { + "scheme": "http", + "host": get_env_variable("ESHOST"), + "port": int(ELASTICSEARCH_HTTP_PORT), + } +] USER_ELASTICSEARCH_PREFIX = get_optional_env_variable("ELASTICSEARCH_PREFIX") if USER_ELASTICSEARCH_PREFIX: diff --git a/manage.py b/manage.py index cefcfcc..ef07cf3 100644 --- a/manage.py +++ b/manage.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -''' +""" ARCHES - a program developed to inventory and manage immovable cultural heritage. Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund @@ -16,7 +16,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . -''' +""" import os