diff --git a/.dockerignore b/.dockerignore index 170d567..e523cef 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,4 +15,7 @@ mail.log/ /static/ *.sqlite3* *.log* -deployment* \ No newline at end of file +deployment* + +docs/ +.readthedocs.yaml \ No newline at end of file diff --git a/.gitignore b/.gitignore index 31b1435..4daa70a 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ deployment-key* /custom staging.yml production.yml + +# Docs local builds +/docs/build \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..018f5a4 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,25 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/source/conf.py + +# If using Sphinx, optionally build your docs in additional formats such as PDF +formats: + - pdf + +# Optionally declare the Python requirements required to build your docs +python: + install: + - requirements: docs/source/requirements.txt \ No newline at end of file diff --git a/Caddyfile b/Caddyfile old mode 100644 new mode 100755 index 70cd7c9..116752a --- a/Caddyfile +++ b/Caddyfile @@ -4,12 +4,13 @@ @proxy_paths { not path /static/* + not path /media/* } - reverse_proxy @proxy_paths http://web:8000 + reverse_proxy @proxy_paths http://server:8000 log { output stderr - format single_field common_log + format console } } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index 14b3c68..4884e33 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.8-slim +FROM python:3.9-slim RUN groupadd -r mapper && useradd --no-log-init -r -g mapper mapper diff --git a/README.md b/README.md index ad95a60..17ca3b0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # BRECcIA Mapper +[![Documentation Status](https://readthedocs.org/projects/breccia/badge/?version=latest)](https://breccia.readthedocs.io/en/latest/?badge=latest) + BRECcIA Mapper is a web app to collect and explore data about the relationships between researchers and their stakeholders on large-scale, multi-site research projects. This allows researchers to visually represent the relationships between project staff and stakeholders involved in the their project at different points in time. Through this it is possible to explore the extent of networks and change over time, and identify where new relationships can be developed or existing ones strengthened. @@ -14,6 +16,7 @@ Deployment is managed using [Ansible](https://www.ansible.com/) and Docker (http ## Contributors - James Graham (@jag1g13) - developer +- Matthew Grove (@mgrove36) - developer - Genevieve Agaba - Sebastian Reichel - Claire Bedelian diff --git a/activities/migrations/0007_alter_activity_id_alter_activitymedium_id_and_more.py b/activities/migrations/0007_alter_activity_id_alter_activitymedium_id_and_more.py new file mode 100755 index 0000000..d4d7663 --- /dev/null +++ b/activities/migrations/0007_alter_activity_id_alter_activitymedium_id_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.1.4 on 2023-01-05 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('activities', '0006_activity_attendance_optional'), + ] + + operations = [ + migrations.AlterField( + model_name='activity', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='activitymedium', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='activityseries', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='activitytype', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/breccia_mapper/settings.py b/breccia_mapper/settings.py index 6627eac..3a3d8fe 100644 --- a/breccia_mapper/settings.py +++ b/breccia_mapper/settings.py @@ -16,18 +16,6 @@ https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ Many configuration settings are input from `settings.ini`. The most likely required settings are: SECRET_KEY, DEBUG, ALLOWED_HOSTS, DATABASE_URL, PROJECT_*_NAME, EMAIL_* -- PARENT_PROJECT_NAME - default: Parent Project Name - Displayed in templates where the name of the parent project should be used - -- PROJECT_LONG_NAME - default: Project Long Name - Displayed in templates where the full name of the project should be used - -- PROJECT_SHORT_NAME - default: shortname - Displayed in templates where a short identifier for the project should be used - - SECRET_KEY (REQUIRED) Used to generate CSRF tokens - must never be made public @@ -118,16 +106,9 @@ import dj_database_url SETTINGS_EXPORT = [ 'DEBUG', - 'PARENT_PROJECT_NAME', - 'PROJECT_LONG_NAME', - 'PROJECT_SHORT_NAME', 'GOOGLE_MAPS_API_KEY', ] -PARENT_PROJECT_NAME = config('PARENT_PROJECT_NAME', - default='Parent Project Name') -PROJECT_LONG_NAME = config('PROJECT_LONG_NAME', default='Project Long Name') -PROJECT_SHORT_NAME = config('PROJECT_SHORT_NAME', default='shortname') # Build paths inside the project like this: BASE_DIR.joinpath(...) BASE_DIR = pathlib.Path(__file__).parent.parent @@ -165,7 +146,6 @@ THIRD_PARTY_APPS = [ 'post_office', 'bootstrap_datepicker_plus', 'hijack', - 'compat', ] FIRST_PARTY_APPS = [ @@ -184,6 +164,7 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'hijack.middleware.HijackUserMiddleware', ] ROOT_URLCONF = 'breccia_mapper.urls' @@ -297,6 +278,10 @@ STATIC_ROOT = BASE_DIR.joinpath('static') STATICFILES_DIRS = [BASE_DIR.joinpath('breccia_mapper', 'static')] +# Media uploads +MEDIA_ROOT = BASE_DIR.joinpath('media') +MEDIA_URL = "/media/" + # Logging - NB the logger name is empty to capture all output LOGGING = { @@ -340,6 +325,10 @@ logger = logging.getLogger(__name__) # pylint: disable=invalid-name # Admin panel variables +CONSTANCE_ADDITIONAL_FIELDS = { + 'image_field': ['django.forms.ImageField', {}] +} + CONSTANCE_CONFIG = { 'NOTICE_TEXT': ( '', @@ -359,21 +348,105 @@ CONSTANCE_CONFIG = { 'RELATIONSHIP_FORM_HELP': ( '', 'Help text to display at the top of relationship forms.'), + 'DEPLOYMENT_URL': ( + 'http://localhost', + 'URL at which this mapper tool is accessible'), + 'PARENT_PROJECT_NAME': ( + '', + 'Parent project name'), + 'PROJECT_LONG_NAME': ( + 'Project Network Mapper', + 'Project long name'), + 'PROJECT_SHORT_NAME': ( + 'Network Mapper', + 'Project short name'), + 'PROJECT_LEAD': ( + 'Project Lead', + 'Project lead'), + 'PROJECT_TAGLINE': ( + 'Here is your project\'s tagline.', + 'Project tagline'), + 'HOMEPAGE_HEADER_IMAGE': ( + '800x500.png', + 'Homepage header image', + 'image_field'), + 'HOMEPAGE_CARD_1_TITLE': ( + 'Step 1', + 'Homepage card #1 title'), + 'HOMEPAGE_CARD_1_DESCRIPTION': ( + 'Tell us about your position within the project', + 'Homepage card #1 description'), + 'HOMEPAGE_CARD_1_ICON': ( + 'building-user', + 'Homepage card #1 icon'), + 'HOMEPAGE_CARD_2_TITLE': ( + 'Step 2', + 'Homepage card #2 title'), + 'HOMEPAGE_CARD_2_DESCRIPTION': ( + 'Describe your relationships with other stakeholders', + 'Homepage card #2 description'), + 'HOMEPAGE_CARD_2_ICON': ( + 'handshake-simple', + 'Homepage card #2 icon'), + 'HOMEPAGE_CARD_3_TITLE': ( + 'Step 3', + 'Homepage card #3 title'), + 'HOMEPAGE_CARD_3_DESCRIPTION': ( + 'Use the network view to build new relationships', + 'Homepage card #3 description'), + 'HOMEPAGE_CARD_3_ICON': ( + 'diagram-project', + 'Homepage card #3 icon'), + 'HOMEPAGE_ABOUT_TITLE': ( + 'About Us', + 'Homepage about section title'), + 'HOMEPAGE_ABOUT_CONTENT': ( + """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. In massa tempor nec feugiat nisl. Eget dolor morbi non arcu risus quis varius quam quisque. Nisl pretium fusce id velit ut tortor pretium viverra suspendisse. Vitae auctor eu augue ut lectus arcu. Tellus molestie nunc non blandit massa enim nec. At consectetur lorem donec massa sapien. Placerat orci nulla pellentesque dignissim enim sit. Sit amet mauris commodo quis imperdiet. Tellus at urna condimentum mattis pellentesque.
In vitae turpis massa sed. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Ut consequat semper viverra nam libero justo laoreet. Velit ut tortor pretium viverra suspendisse potenti nullam ac tortor. Nunc id cursus metus aliquam eleifend mi in nulla posuere. Aliquam eleifend mi in nulla posuere sollicitudin aliquam. Est ante in nibh mauris cursus mattis molestie a iaculis. Nunc id cursus metus aliquam. Auctor urna nunc id cursus metus aliquam. Porttitor lacus luctus accumsan tortor posuere ac ut consequat semper. Volutpat consequat mauris nunc congue nisi. Leo vel fringilla est ullamcorper eget. Vitae purus faucibus ornare suspendisse sed nisi lacus sed. Massa id neque aliquam vestibulum morbi blandit. Iaculis nunc sed augue lacus viverra vitae congue. Sodales neque sodales ut etiam.""", + 'Homepage about section content'), + 'HOMEPAGE_ABOUT_IMAGE': ( + '400x400.png', + 'Homepage about section image', + 'image_field'), } # yapf: disable CONSTANCE_CONFIG_FIELDSETS = { - 'Notice Banner': ( + 'Project options': ( + 'PARENT_PROJECT_NAME', + 'PROJECT_LONG_NAME', + 'PROJECT_SHORT_NAME', + 'PROJECT_LEAD', + 'PROJECT_TAGLINE', + ), + 'Homepage configuration': ( + 'HOMEPAGE_HEADER_IMAGE', + 'HOMEPAGE_CARD_1_TITLE', + 'HOMEPAGE_CARD_1_DESCRIPTION', + 'HOMEPAGE_CARD_1_ICON', + 'HOMEPAGE_CARD_2_TITLE', + 'HOMEPAGE_CARD_2_DESCRIPTION', + 'HOMEPAGE_CARD_2_ICON', + 'HOMEPAGE_CARD_3_TITLE', + 'HOMEPAGE_CARD_3_DESCRIPTION', + 'HOMEPAGE_CARD_3_ICON', + 'HOMEPAGE_ABOUT_TITLE', + 'HOMEPAGE_ABOUT_CONTENT', + 'HOMEPAGE_ABOUT_IMAGE', + ), + 'Notice banner': ( 'NOTICE_TEXT', 'NOTICE_CLASS', ), 'Data Collection': ( 'CONSENT_TEXT', ), - 'Help Text': ( + 'Help text': ( 'PERSON_LIST_HELP', 'ORGANISATION_LIST_HELP', 'RELATIONSHIP_FORM_HELP', ), + 'Deployment': ( + 'DEPLOYMENT_URL', + ), } # yapf: disable CONSTANCE_BACKEND = 'constance.backends.database.DatabaseBackend' @@ -396,7 +469,7 @@ BOOTSTRAP4 = { EMAIL_HOST = config('EMAIL_HOST', default=None) DEFAULT_FROM_EMAIL = config( 'DEFAULT_FROM_EMAIL', - default=f'{PROJECT_SHORT_NAME}@localhost.localdomain') + default=f'{CONSTANCE_CONFIG["PROJECT_SHORT_NAME"][0]}@localhost.localdomain') SERVER_EMAIL = DEFAULT_FROM_EMAIL if EMAIL_HOST is None: @@ -417,6 +490,18 @@ else: default=(EMAIL_PORT == 465), cast=bool) +# Bootstrap Datepicker Plus Settings +BOOTSTRAP_DATEPICKER_PLUS = { + "variant_options": { + "date": { + "format": "%Y-%m-%d", + }, + } +} + +# Database default automatic primary key +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + # Upstream API keys GOOGLE_MAPS_API_KEY = config('GOOGLE_MAPS_API_KEY', default=None) diff --git a/breccia_mapper/static/css/masthead.css b/breccia_mapper/static/css/masthead.css index 79b9aa6..e4364db 100644 --- a/breccia_mapper/static/css/masthead.css +++ b/breccia_mapper/static/css/masthead.css @@ -1,10 +1,10 @@ header.masthead { position: relative; background: #343a40 no-repeat center; - -webkit-background-size: contain; - -moz-background-size: contain; - -o-background-size: contain; - background-size: contain; + -webkit-background-size: cover; + -moz-background-size: cover; + -o-background-size: cover; + background-size: cover; padding-top: 8rem; padding-bottom: 8rem; min-height: 400px; diff --git a/breccia_mapper/templates/base.html b/breccia_mapper/templates/base.html old mode 100644 new mode 100755 index ef622cb..1f90029 --- a/breccia_mapper/templates/base.html +++ b/breccia_mapper/templates/base.html @@ -10,27 +10,29 @@ - {{ settings.PROJECT_LONG_NAME }} + {{ config.PROJECT_LONG_NAME }} {% bootstrap_css %} + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/fontawesome.min.css" + integrity="sha512-giQeaPns4lQTBMRpOOHsYnGw1tGVzbAIHUyHRgn7+6FmiEgGGjaG0T2LZJmAPMzRCl+Cug0ItQ2xDZpTmEc+CQ==" + crossorigin="anonymous" + referrerpolicy="no-referrer" /> + href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/solid.min.css" + integrity="sha512-6mc0R607di/biCutMUtU9K7NtNewiGQzrvWX4bWTeqmljZdJrwYvKJtnhgR+Ryvj+NRJ8+NnnCM/biGqMe/iRA==" + crossorigin="anonymous" + referrerpolicy="no-referrer" /> - {% load staticfiles %} + {% load static %} + href="{% static 'hijack/hijack.min.css' %}" /> {% if 'javascript_in_head'|bootstrap_setting %} {% if 'include_jquery'|bootstrap_setting %} @@ -56,7 +58,7 @@