Compare commits

..

58 commits

Author SHA1 Message Date
44bab85ba7 person deletion method overload 2022-10-04 19:21:57 +05:00
PCoder
3b30b9666a Update Changlog 2022-09-29 08:58:00 +05:30
PCoder
30f0743c41 #11013: filter persons only with profile_on_web true for public search 2022-09-29 07:40:52 +05:30
PCoder
db54e3c3d7 Merge remote-tracking branch 'ungleich-public/master' 2022-09-26 10:48:17 +05:30
PCoder
d7199827da Fix infinite redirections in production 2022-09-25 21:21:42 +05:30
app
0574694d38 Revert back 2022-09-25 14:20:51 +00:00
app
f88a037edc First working version 2022-09-25 10:19:08 +00:00
app
bfe6fe610a Test: disable contents of init.sh 2022-09-24 13:45:40 +00:00
app
19b115434f ++init.sh 2022-09-24 11:51:49 +00:00
PCoder
674ab5d288 Copy init.sh to appropriate path 2022-09-24 16:45:41 +05:30
app
2c870df36c Add init.sh 2022-09-23 03:37:54 +00:00
PCoder
929d279b24 Use storage under /data 2022-09-23 08:18:38 +05:30
app
127a9ab8ec Merge branch 'master' of code.ungleich.ch:ungleich-public/gmba_django 2022-09-23 01:32:03 +00:00
app
b7dfb676ec Fix collectstatic 2022-09-23 01:29:27 +00:00
app
079762250b nginx.conf: add missing terminating semicolon 2022-09-22 03:24:21 +00:00
app
c7b525875d entrypoint.sh: Avoid using deep link 2022-09-22 03:23:40 +00:00
app
4734789a25 gunicorn: Listen on both ipv6 and ipv4 interfaces 2022-09-22 03:10:45 +00:00
app
3a5cbc6325 Use POSTGRES_HOST and POSTGRES_PORT instead of SQL_* 2022-09-22 03:06:24 +00:00
app
a80158da66 Merge branch '10884-dockerify' of code.ungleich.ch:ungleich-public/gmba_django into 10884-dockerify 2022-09-20 04:21:57 +00:00
app
5326ea7ab0 Use psql for dev as well 2022-09-20 04:21:04 +00:00
app
854ce75993 Fix location of manage.py 2022-09-20 04:20:44 +00:00
app
02aaccffed Attempt collectstatic in Dockerfile itselfw 2022-09-20 04:02:47 +00:00
app
5e50d3320e Use the same env file for db and web (.env.prod) 2022-09-20 03:50:41 +00:00
app
0140a65602 Align constants 2022-09-20 03:50:02 +00:00
app
8f2f8b86e2 Use POSTGRES_* constants for uniformity 2022-09-20 03:48:07 +00:00
app
e798679c6c Change default db to psql 2022-09-20 03:42:17 +00:00
f85846cd48 Merge pull request '10884-dockerify' (#6) from 10884-dockerify into master
Reviewed-on: #6
2022-09-19 08:27:31 +00:00
PCoder
0123fd2eef Add media files config 2022-09-19 13:35:02 +05:30
PCoder
3eb5ba12d9 Add missing configs for nginx 2022-09-19 13:30:56 +05:30
PCoder
bc4061f12a Prepare changelog for 2.0 2022-09-19 12:36:21 +05:30
PCoder
4cc03b6372 Remove unwanted files 2022-09-19 12:22:52 +05:30
PCoder
00c03e7bec Merge remote-tracking branch 'ungleich-public/master' into 10884-dockerify 2022-09-19 10:24:47 +05:30
PCoder
600ae8e5de Fix static files path in nginx.conf 2022-09-19 09:57:57 +05:30
PCoder
13b7918de1 Fix staticfiles path as per code 2022-09-19 09:54:17 +05:30
PCoder
a91d2b16b6 Fix static files path and create the directory 2022-09-19 09:43:27 +05:30
app
5b64c4ee15 Use correct container name 2022-09-19 03:57:24 +00:00
app
5446daa87f Use hardcoded instance name for now 2022-09-19 03:35:14 +00:00
48a2585829 person save method overload 2022-09-08 22:44:24 +05:00
2c4fb66176 resource and relevant objs del 2022-09-07 23:48:58 +05:00
app
f960c4e779 Add docker-compose.prod.yaml and nginx confs 2022-08-25 08:27:48 +00:00
app
3b9498d13d WIP: fixing docker compose scritps 2022-08-25 08:26:59 +00:00
PCoder
7fd30a5dcd Add production ready Dockerfle and entrypoint files 2022-08-25 11:03:25 +05:30
PCoder
c5bfc46e33 Add first version of docker files 2022-08-25 10:26:17 +05:30
b7f8e237fa missing search field organisation_english at organizationadmin 2022-05-18 22:12:18 +05:00
c84ae636c5 added organisation and acronyms in admin query field 2022-04-12 13:31:14 +05:00
8e409d1a38 space changed too < in __str__ person 2022-04-12 12:47:17 +05:00
808f1d94bb alphabatecial order for query in person admin addition for country and dept 2022-04-12 12:01:31 +05:00
9dc9daa1e0 form to override autocomplete organization changed 2022-03-28 10:46:30 +05:00
e8f8ee8ad3 form to override autocomplete organization field for Person class in admin 2022-03-27 23:32:26 +05:00
d436650d2c [Persons] Offer caculated autocomplete identifiers 10146 2022-03-27 17:44:32 +05:00
e9ec1909b4 download_csv name changed to export (.csv) 2022-03-25 23:35:38 +05:00
0136386ad0 Enable CSV export for the 4 main tables Organisations, Persons, Resources, Mountain ranges 2022-03-25 23:08:02 +05:00
cc66cfd4d8 newform submission to client at https://formspree.io/f/xvolqjpe 2022-03-22 23:16:20 +05:00
dffd00d8cb form submission to client at https://formspree.io/f/gmba@ips.unibe.ch 2022-03-22 23:05:59 +05:00
7efa5b3741 formspree emails sent to test.gamba.ungleich@gmail.com and form hides 2022-03-17 16:27:42 +05:00
PCoder
f2d27bd791 Merge remote-tracking branch 'ungleich-public/master' 2022-03-17 14:04:53 +05:30
PCoder
202b6c0092 Some pending fixes 2022-03-17 14:03:35 +05:30
907ae0c256 Merge pull request 'fix_refresh_data' (#5) from fix_refresh_data into master
Reviewed-on: #5
2022-03-11 11:25:35 +00:00
18 changed files with 555 additions and 81 deletions

View file

@ -1,5 +1,40 @@
# CHANGELOG.md
## 2.1 (2022-09-29)
- issue#11013: non-public entries appearing as public
## 2.0 (2022-09-19) Dockerify https://redmine.ungleich.ch/issues/10884
Steps:
- Create .env.prod and .env.prod.db files
#### .env.prod
```
DEBUG=0
SECRET_KEY=secret_key_here
DJANGO_SETTINGS_MODULE=gmba_django.settings.production
ALLOWED_HOSTS=localhost,127.0.0.1,[::1]
POSTGRES_USER=user
POSTGRES_PASSWORD=password
POSTGRES_DB=app
POSTGRES_HOST=db
POSTGRES_PORT=5432
```
Building containers
- docker-compose -f docker-compose.prod.yml down -v --remove-orphans
- docker-compose -f docker-compose.prod.yml up -d --build
Copy db and adust
- docker cp gd.sql gmba_django_db_1:/gd.sql
- docker-compose exec db psql --username=psql_username_name --dbname=psql_db_name < /gd.sql
Migrate / check
- docker-compose exec web python manage.py migrate --noinput
Static resources
- docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic --no-input --clear
## 1.12 (2022-01-19)
Bugfixes:

85
Dockerfile.prod Normal file
View file

@ -0,0 +1,85 @@
###########
# BUILDER #
###########
# pull official base image
FROM python:3.9.6-alpine as builder
# set work directory
WORKDIR /usr/src/gmba_django
# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
# install psycopg2 dependencies
RUN apk update \
&& apk add postgresql-dev gcc python3-dev musl-dev
# lint
RUN pip install --upgrade pip
RUN pip install flake8==3.9.2
COPY . .
#RUN flake8 --ignore=E501,F401 .
# install dependencies
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/gmba_django/wheels -r requirements.txt
#########
# FINAL #
#########
# pull official base image
FROM python:3.9.6-alpine
# create directory for the app user
RUN mkdir -p /home/app
# create the app user
RUN addgroup -S app && adduser -S app -G app
# create the appropriate directories
ENV HOME=/home/app
ENV APP_HOME=/home/app/gmba_django
RUN mkdir $APP_HOME
RUN mkdir $APP_HOME/static
RUN mkdir $APP_HOME/media
#ADD gmba_django /data/app
#WORKDIR /data/app/gmba_django
# install dependencies
RUN apk update && apk add libpq
COPY --from=builder /usr/src/gmba_django/wheels /wheels
COPY --from=builder /usr/src/gmba_django/requirements.txt .
RUN pip install --no-cache /wheels/*
# copy entrypoint.prod.sh
COPY ./entrypoint.prod.sh /entrypoint.prod.sh
COPY ./init.sh /init.sh
RUN sed -i 's/\r$//g' /entrypoint.prod.sh
RUN chmod +x /entrypoint.prod.sh
# copy project
#COPY . $APP_HOME
ADD . /home/app/gmba_django
WORKDIR /home/app/gmba_django
# chown all the files to the app user
RUN chown -R app:app $APP_HOME
# change to the app user
USER app
#RUN python manage.py collectstatic --noinput
# run entrypoint.prod.sh
ENTRYPOINT ["/entrypoint.prod.sh"]
EXPOSE 8000
CMD ["gunicorn", "--bind", "[::]:8000", "--chdir", "/home/app/gmba_django", "--workers", "3", "gmba_django.wsgi:application"]

View file

@ -1,8 +1,53 @@
from re import A
from unicodedata import name
from django.contrib import admin
from django.utils.html import format_html
from .models import *
from django.forms import TextInput
from django.conf import settings
from django.http import HttpResponse
import csv
from django import forms
from django.contrib.admin.widgets import AutocompleteSelect
from django.contrib import messages # import messages
class autocomplete_fields_form(forms.ModelForm):
"""This form overrides autocomplete
organization field for Person class
"""
def __init__(self, *args, **kwargs):
super(autocomplete_fields_form, self).__init__(*args, **kwargs)
# self.fields['organization'].label = 'My new label'
class Meta:
model = Person
fields = '__all__'
widgets = {
'organization': forms.Select(attrs={'style': 'width:750px'})
}
def download_csv(modeladmin, request, queryset):
name = queryset[0].__class__.__name__
response = HttpResponse()
response['Content-Disposition'] = "attachment; filename="+name+"_table.csv"
first_row = []
for i in queryset[0]._meta.__dict__.get("fields"):
temp = str(i).split('.')
first_row.append(temp[-1])
writer = csv.writer(response)
writer.writerow(first_row)
for dic in queryset.values():
temp_list = (list(dic.values()))
writer.writerow(temp_list)
return response
download_csv.short_description = 'export (.csv)'
class PeopleResourceInline(admin.TabularInline):
@ -23,7 +68,8 @@ class PersonAdmin(admin.ModelAdmin):
PeopleRangeInline
]
readonly_fields = ['id']
search_fields = ['title', 'first_name', 'last_name', 'organisation', 'position', 'country__short_name', 'contact_email']
search_fields = ['title', 'first_name', 'last_name', 'organisation',
'position', 'country__short_name', 'contact_email']
fieldsets = (
(None, {
'fields': (
@ -64,10 +110,95 @@ class PersonAdmin(admin.ModelAdmin):
}),
)
list_display = ['id', 'full_name', 'organization']
autocomplete_fields = ['organization']
#autocomplete_fields = ['organization']
form = autocomplete_fields_form # adjust width of autocomplete_fields organization
list_per_page = settings.ADMIN_LIST_PER_PAGE
ordering = ['full_name']
# list_display_links = ['id']
actions = [download_csv]
def get_field_queryset(self, db, db_field, request):
queryset = super().get_field_queryset(db, db_field, request)
if db_field.name == 'organization':
queryset = queryset.order_by(
'country', 'organisation_english', 'organisation_2', 'organisation_3', 'acronym')
return queryset
def get_actions(self, request):
actions = super().get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
def delete_model(self, request, obj):
self.delete_person(request, obj)
return super(PersonAdmin, self).delete_model(request, obj)
def delete_person(self, request, obj):
taxa_people = TaxaPeople.objects.filter(person=obj.id)
for i in taxa_people:
i.delete()
ranges_people = RangesPeople.objects.filter(person=obj.id)
for i in ranges_people:
i.delete()
resources_people = ResourcesPeople.objects.filter(person=obj.id)
for i in resources_people:
i.delete()
scales_people = ScalesPeople.objects.filter(person=obj.id)
for i in scales_people:
i.delete()
people_function = PeopleFunction.objects.filter(person=obj.id)
for i in people_function:
i.delete()
people_range = PeopleRange.objects.filter(person=obj.id)
for i in people_range:
i.delete()
people_resources = PeopleResource.objects.filter(person=obj.id)
for i in people_resources:
i.delete()
fields_people = FieldsPeople.objects.filter(person=obj.id)
for i in fields_people:
i.delete()
methods_people = MethodsPeople.objects.filter(person=obj.id)
for i in methods_people:
i.delete()
def delete_persons(self, request, obj):
for o in obj.all():
taxa_people = TaxaPeople.objects.filter(person=o.id)
for i in taxa_people:
i.delete()
ranges_people = RangesPeople.objects.filter(person=o.id)
for i in ranges_people:
i.delete()
resources_people = ResourcesPeople.objects.filter(person=o.id)
for i in resources_people:
i.delete()
scales_people = ScalesPeople.objects.filter(person=o.id)
for i in scales_people:
i.delete()
people_function = PeopleFunction.objects.filter(person=o.id)
for i in people_function:
i.delete()
people_range = PeopleRange.objects.filter(person=o.id)
for i in people_range:
i.delete()
people_resources = PeopleResource.objects.filter(person=o.id)
for i in people_resources:
i.delete()
fields_people = FieldsPeople.objects.filter(person=o.id)
for i in fields_people:
i.delete()
methods_people = MethodsPeople.objects.filter(person=o.id)
for i in methods_people:
i.delete()
o.delete()
messages.success(request, "Successfully deleted")
delete_persons.short_description = 'Delete Person & Related Fields'
actions = [download_csv, delete_persons]
class ResourceKeywordInline(admin.TabularInline):
@ -131,6 +262,52 @@ class ResourceAdmin(admin.ModelAdmin):
)
list_per_page = settings.ADMIN_LIST_PER_PAGE
def get_actions(self, request):
actions = super().get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions
def delete_model(self, request, obj):
self.delete_resource(request, obj)
return super(ResourceAdmin, self).delete_model(request, obj)
def delete_resource(self, request, obj):
resource_keyword = ResourceKeyword.objects.filter(resource=obj.id)
for i in resource_keyword:
i.delete()
resource_range = ResourceRange.objects.filter(resource_title=obj.id)
for i in resource_range:
i.delete()
resource_people = ResourcesPeople.objects.filter(resource=obj.id)
for i in resource_people:
i.delete()
resource_people_resource = PeopleResource.objects.filter(
resource=obj.id)
for i in resource_people_resource:
i.delete()
def delete_resources(self, request, obj):
for o in obj.all():
resource_keyword = ResourceKeyword.objects.filter(resource=o.id)
for i in resource_keyword:
i.delete()
resource_range = ResourceRange.objects.filter(resource_title=o.id)
for i in resource_range:
i.delete()
resource_people = ResourcesPeople.objects.filter(resource=o.id)
for i in resource_people:
i.delete()
resource_people_resource = PeopleResource.objects.filter(
resource=o.id)
for i in resource_people_resource:
i.delete()
o.delete()
messages.success(request, "Successfully deleted")
delete_resources.short_description = 'Delete Resource & Related Fields'
actions = [download_csv, delete_resources]
class RangeNameTranslationInline(admin.TabularInline):
autocomplete_fields = ['language_translation']
@ -185,6 +362,8 @@ class RangeAdmin(admin.ModelAdmin):
ordering = ['range_name']
list_per_page = settings.ADMIN_LIST_PER_PAGE
actions = [download_csv]
class PeopleOrganizationInline(admin.TabularInline):
model = Person
@ -197,8 +376,10 @@ class OrganizationAdmin(admin.ModelAdmin):
PeopleOrganizationInline,
]
readonly_fields = ('org_num1',)
search_fields = ['organisation_search', 'org_alpha_search', 'organisation_2', 'organisation_3', 'subject']
list_display = ['org_num1', 'organisation_english', 'organisation_2', 'country']
search_fields = ['organisation_search', 'org_alpha_search',
'organisation_2', 'organisation_3', 'subject', 'organisation_english']
list_display = ['org_num1', 'organisation_english',
'organisation_2', 'country']
fieldsets = (
(None, {
'fields': (
@ -225,6 +406,7 @@ class OrganizationAdmin(admin.ModelAdmin):
})
)
ordering = ['organisation_english']
actions = [download_csv]
def org_url(self, instance):
return format_html('<a href="{0}" target="_blank">{1}</a>',
@ -317,7 +499,8 @@ class SpeciesAdmin(admin.ModelAdmin):
class TaxonRangeAdmin(admin.ModelAdmin):
model = TaxonRange
search_fields = ['range__range_name', 'taxon', 'subrange_or_region', 'distribution', 'source', 'remarks']
search_fields = ['range__range_name', 'taxon',
'subrange_or_region', 'distribution', 'source', 'remarks']
list_display = ['range_name', 'taxon']
def range_name(self, obj):
@ -391,6 +574,7 @@ admin.site.register(Keyword, KeywordAdmin)
# admin.site.register(Scale)
# admin.site.register(Taxon)
# admin.site.register(Field, FieldAdmin)
admin.site.disable_action('delete_selected')
# LU models

View file

@ -356,7 +356,7 @@ class Command(BaseCommand):
for k, v in self.csv_files_models_dict.items():
if v.strip().lower() == model_name.strip().lower():
csv_file_name = k
if k == '':
if csv_file_name == '':
raise Exception('Could not find a csv file name for model %s' % model_name)
if csv_folder_path.endswith('/'):
file_path = '%s%s' % (csv_folder_path, csv_file_name)

View file

@ -31,7 +31,8 @@ class Field(models.Model):
class FieldsPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey('Person', models.DO_NOTHING, blank=True, null=True)
person = models.ForeignKey(
'Person', models.DO_NOTHING, blank=True, null=True)
field = models.ForeignKey(Field, models.DO_NOTHING, blank=True, null=True)
class Meta:
@ -53,8 +54,10 @@ class Method(models.Model):
class MethodsPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey('Person', models.DO_NOTHING, blank=True, null=True)
method = models.ForeignKey(Method, models.DO_NOTHING, blank=True, null=True)
person = models.ForeignKey(
'Person', models.DO_NOTHING, blank=True, null=True)
method = models.ForeignKey(
Method, models.DO_NOTHING, blank=True, null=True)
class Meta:
db_table = 'methods_people'
@ -92,10 +95,14 @@ class MountainRange(models.Model):
#range_name_map = models.CharField(blank=True, null=True, unique=True, max_length=128)
range_name = models.CharField(blank=True, null=True, max_length=128)
range_name_ascii = models.CharField(blank=True, null=True, max_length=128)
range_name_language = models.ForeignKey(Language, models.DO_NOTHING, blank=True, null=True, to_field='id')
mother_range = models.ForeignKey("self", models.DO_NOTHING, blank=True, null=True, to_field='id')
feature = models.ForeignKey(RangeType, models.DO_NOTHING, blank=True, null=True, to_field='id')
map_unit = models.TextField(blank=True, null=True, choices=MAP_UNIT_CHOICES)
range_name_language = models.ForeignKey(
Language, models.DO_NOTHING, blank=True, null=True, to_field='id')
mother_range = models.ForeignKey(
"self", models.DO_NOTHING, blank=True, null=True, to_field='id')
feature = models.ForeignKey(
RangeType, models.DO_NOTHING, blank=True, null=True, to_field='id')
map_unit = models.TextField(
blank=True, null=True, choices=MAP_UNIT_CHOICES)
level = models.TextField(blank=True, null=True)
level_text = models.TextField(blank=True, null=True)
level_1 = models.TextField(blank=True, null=True)
@ -113,14 +120,16 @@ class MountainRange(models.Model):
comments = models.TextField(blank=True, null=True)
checked = models.BooleanField(default=False)
source = models.TextField(blank=True, null=True)
range_alternate_id = models.CharField(blank=True, null=True, max_length=128)
range_alternate_id = models.CharField(
blank=True, null=True, max_length=128)
geologic_region = models.TextField(blank=True, null=True)
gmba_v2_id = models.PositiveIntegerField(blank=True, null=True)
gmba_v2_id_str = models.TextField(blank=True, null=True)
wiki_data_id = models.CharField(blank=True, null=True, max_length=25)
wiki_data_url = models.URLField(blank=True, null=True)
select_300 = models.BooleanField(default=False)
gmba_narrow = models.BooleanField(default=False, verbose_name="GMBA Standard")
gmba_narrow = models.BooleanField(
default=False, verbose_name="GMBA Standard")
name_fr = models.CharField(blank=True, null=True, max_length=128)
name_de = models.CharField(blank=True, null=True, max_length=128)
name_es = models.CharField(blank=True, null=True, max_length=128)
@ -201,7 +210,8 @@ class Resource(models.Model):
PEGASuS_Check_map_with_author = models.BooleanField(default=False)
PEGASuS_polygon_ID = models.CharField(max_length=32, blank=True, null=True)
PEGASuS_Polygon_comments = models.TextField(blank=True, null=True)
PEGASuS_Assessment_ID = models.CharField(max_length=32, blank=True, null=True)
PEGASuS_Assessment_ID = models.CharField(
max_length=32, blank=True, null=True)
gloria = models.BooleanField(default=False)
gnomo = models.BooleanField(default=False)
lter = models.BooleanField(default=False)
@ -352,15 +362,20 @@ class GMBA_function(models.Model):
class Organisation(models.Model):
CATEGORY_CHOICES = (
('Independent', 'Independent'),
('Public administration > Research Institution', 'Public administration > Research Institution'),
('Public administration > Research Institution',
'Public administration > Research Institution'),
('Academia > University > Institute', 'Academia > University > Institute'),
('Academia > University', 'Academia > University'),
('Public administration > Other Agency', 'Public administration > Other Agency'),
('Public administration > Other Agency',
'Public administration > Other Agency'),
('Private > NGO (not for profit)', 'Private > NGO (not for profit)'),
('Academia > Academy of Sciences', 'Academia > Academy of Sciences'),
('Academia > Academy of Sciences > Institute', 'Academia > Academy of Sciences > Institute'),
('Public administration > Government', 'Public administration > Government'),
('Intergovernmental Agency > Research Institution', 'Intergovernmental Agency > Research Institution'),
('Academia > Academy of Sciences > Institute',
'Academia > Academy of Sciences > Institute'),
('Public administration > Government',
'Public administration > Government'),
('Intergovernmental Agency > Research Institution',
'Intergovernmental Agency > Research Institution'),
('Intergovernmental Agency', 'Intergovernmental Agency '),
('Private > Research Institution', 'Private > Research Institution'),
('Collection > Museum', 'Collection > Museum'),
@ -384,7 +399,8 @@ class Organisation(models.Model):
('Culture / arts', 'Culture / arts'),
('Geography', 'Geography'),
('Zoology', 'Zoology'),
('Development / poverty / human rights', 'Development / poverty / human rights'),
('Development / poverty / human rights',
'Development / poverty / human rights'),
('Social Sciences', 'Social Sciences'),
('Earth Sciences', 'Earth Sciences'),
('Health', 'Health'),
@ -399,9 +415,12 @@ class Organisation(models.Model):
org_num1 = models.AutoField(primary_key=True, verbose_name='ID')
organisation_search = models.TextField(blank=True, null=True)
org_alpha_search = models.TextField(blank=True, null=True)
organisation_english = models.CharField(max_length=256, blank=True, null=True, verbose_name='Organisation')
organisation_2 = models.CharField(max_length=256, blank=True, null=True, verbose_name='Department 1')
organisation_3 = models.CharField(max_length=256, blank=True, null=True, verbose_name='Department 2')
organisation_english = models.CharField(
max_length=256, blank=True, null=True, verbose_name='Organisation')
organisation_2 = models.CharField(
max_length=256, blank=True, null=True, verbose_name='Department 1')
organisation_3 = models.CharField(
max_length=256, blank=True, null=True, verbose_name='Department 2')
organisation_original = models.TextField(blank=True, null=True)
acronym = models.CharField(max_length=128, blank=True, null=True)
street = models.CharField(max_length=256, blank=True, null=True)
@ -414,27 +433,33 @@ class Organisation(models.Model):
url = models.URLField(blank=True, null=True)
tel = models.CharField(max_length=128, blank=True, null=True)
email = models.EmailField(blank=True, null=True)
country = models.ForeignKey(Country, models.DO_NOTHING, blank=True, null=True, to_field='id')
country = models.ForeignKey(
Country, models.DO_NOTHING, blank=True, null=True, to_field='id')
tags = models.TextField(blank=True, null=True)
description = models.TextField(blank=True, null=True)
northing = models.TextField(blank=True, null=True)
easting = models.TextField(blank=True, null=True)
category = models.TextField(blank=True, null=True, choices=CATEGORY_CHOICES)
subject = models.CharField(max_length=128, blank=True, null=True, choices=SUBJECT_CHOICES)
category = models.TextField(
blank=True, null=True, choices=CATEGORY_CHOICES)
subject = models.CharField(
max_length=128, blank=True, null=True, choices=SUBJECT_CHOICES)
def __str__(self):
if self.organisation_english:
if self.organisation_2:
name = f"{self.organisation_english} ({self.organisation_2})"
name = f"{self.country} < {self.organisation_english} < {self.organisation_2} < {self.organisation_3} < {self.acronym}"
else:
name = f"{self.organisation_english}"
name = f"{self.country} < {self.organisation_english} < {self.organisation_2} < {self.organisation_3} < {self.acronym}"
else:
name = ' ---'
return name
class RangeOnlineInfo(models.Model):
id = models.AutoField(primary_key=True)
# This is supposed to be linked to MountainRange via range_name_map, but the range_name_map
@ -451,8 +476,10 @@ class RangeOnlineInfo(models.Model):
class RangeNameTranslation(models.Model):
id = models.AutoField(primary_key=True)
range_name = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
language_translation = models.ForeignKey(Language, models.DO_NOTHING, blank=True, null=True)
range_name = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
language_translation = models.ForeignKey(
Language, models.DO_NOTHING, blank=True, null=True)
range_name_translation = models.TextField(blank=True, null=True)
def __str__(self):
@ -505,8 +532,10 @@ class Keyword(models.Model):
class ResourceKeyword(models.Model):
id = models.AutoField(primary_key=True)
resource = models.ForeignKey(Resource, models.DO_NOTHING, blank=True, null=True)
keyword = models.ForeignKey(Keyword, models.DO_NOTHING, blank=True, null=True, to_field='keyword_id')
resource = models.ForeignKey(
Resource, models.DO_NOTHING, blank=True, null=True)
keyword = models.ForeignKey(
Keyword, models.DO_NOTHING, blank=True, null=True, to_field='keyword_id')
def __str__(self):
return '%s - %s' % (self.resource.title, self.keyword.keyword)
@ -529,8 +558,10 @@ class NamesImport(models.Model):
class ResourceRange(models.Model):
id = models.AutoField(primary_key=True)
resource_title = models.ForeignKey(Resource, models.DO_NOTHING, blank=True, null=True)
range_name = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
resource_title = models.ForeignKey(
Resource, models.DO_NOTHING, blank=True, null=True)
range_name = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
def __str__(self):
return '%s, %s' % (self.resource_title.title, self.range_name.name)
@ -538,7 +569,8 @@ class ResourceRange(models.Model):
class LanguageLink(models.Model):
id = models.AutoField(primary_key=True)
language_number_code = models.ForeignKey(Language, models.DO_NOTHING, blank=True, null=True)
language_number_code = models.ForeignKey(
Language, models.DO_NOTHING, blank=True, null=True)
language_letter_code = models.TextField(blank=True, null=True)
def __str__(self):
@ -560,8 +592,10 @@ class Peak(models.Model):
class Search(models.Model):
id = models.AutoField(primary_key=True)
range_name = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
repository = models.ForeignKey(Repository, models.DO_NOTHING, blank=True, null=True)
range_name = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
repository = models.ForeignKey(
Repository, models.DO_NOTHING, blank=True, null=True)
search_string = models.TextField(blank=True, null=True)
search_date = models.TextField(blank=True, null=True)
result = models.TextField(blank=True, null=True)
@ -600,7 +634,9 @@ Involved scientist''')
contact_email = models.EmailField(blank=True, null=True) # email_1
personal_url = models.URLField(blank=True, null=True) # url ?
biography = models.TextField(blank=True, null=True)
field_indexer = models.TextField(db_column='_indexer', blank=True, null=True) # Field renamed because it started with '_'.
# Field renamed because it started with '_'.
field_indexer = models.TextField(
db_column='_indexer', blank=True, null=True)
mr_mrs = models.TextField(blank=True, null=True, choices=MR_MRS_CHOICES)
full_name = models.CharField(blank=True, null=True, max_length=100)
@ -611,12 +647,16 @@ Involved scientist''')
mobile_number = models.CharField(blank=True, null=True, max_length=50)
url = models.URLField(blank=True, null=True)
field_of_expertise = models.TextField(blank=True, null=True)
status = models.ForeignKey(PeopleStatus, models.DO_NOTHING, blank=True, null=True, to_field='id')
status = models.ForeignKey(
PeopleStatus, models.DO_NOTHING, blank=True, null=True, to_field='id')
entry_date = models.TextField(blank=True, null=True)
gmba_function = models.TextField(blank=True, null=True, choices=GMBA_FUNCTION_CHOICES)
gmba_function = models.TextField(
blank=True, null=True, choices=GMBA_FUNCTION_CHOICES)
news_letter = models.BooleanField(default=False)
country = models.ForeignKey(Country, models.DO_NOTHING, blank=True, null=True, to_field='id')
organization = models.ForeignKey(Organisation, models.DO_NOTHING, blank=True, null=True)
country = models.ForeignKey(
Country, models.DO_NOTHING, blank=True, null=True, to_field='id')
organization = models.ForeignKey(
Organisation, models.DO_NOTHING, blank=True, null=True)
birds = models.BooleanField(default=False)
mammals = models.BooleanField(default=False)
reptiles = models.BooleanField(default=False)
@ -641,7 +681,8 @@ Involved scientist''')
assessment = models.BooleanField(default=False)
meta_analysis = models.BooleanField(default=False)
synthesis = models.BooleanField(default=False)
qualitative_ssm = models.BooleanField(default=False) #"Qualitative social science methods (interviews, surveys)"
# "Qualitative social science methods (interviews, surveys)"
qualitative_ssm = models.BooleanField(default=False)
genetic_analyses = models.BooleanField(default=False)
field_site = models.BooleanField(default=False)
transect = models.BooleanField(default=False)
@ -674,7 +715,7 @@ Involved scientist''')
self.position if self and self.position else "",
self.biography if self and self.biography else ""
] if self else "")
self.save()
# self.save()
return True
else:
return False
@ -699,11 +740,16 @@ Involved scientist''')
'biography': self.biography or '',
}
def save(self, *args, **kwargs):
self.index()
super(Person, self).save(*args, **kwargs)
class TaxaPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
taxon = models.ForeignKey('Taxon', models.DO_NOTHING, blank=True, null=True)
taxon = models.ForeignKey(
'Taxon', models.DO_NOTHING, blank=True, null=True)
class Meta:
db_table = 'taxa_people'
@ -715,7 +761,8 @@ class TaxaPeople(models.Model):
class RangesPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
range = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
class Meta:
db_table = 'ranges_people'
@ -727,7 +774,8 @@ class RangesPeople(models.Model):
class ResourcesPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
resource = models.ForeignKey(Resource, models.CASCADE, blank=True, null=True)
resource = models.ForeignKey(
Resource, models.CASCADE, blank=True, null=True)
class Meta:
db_table = 'resources_people'
@ -738,7 +786,8 @@ class ResourcesPeople(models.Model):
class ScalesPeople(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.DO_NOTHING, blank=True, null=True)
person = models.ForeignKey(
Person, models.DO_NOTHING, blank=True, null=True)
scale = models.ForeignKey(Scale, models.DO_NOTHING, blank=True, null=True)
class Meta:
@ -762,7 +811,8 @@ class Species(models.Model):
class PeopleFunction(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
function = models.ForeignKey(GMBA_function, models.DO_NOTHING, blank=True, null=True)
function = models.ForeignKey(
GMBA_function, models.DO_NOTHING, blank=True, null=True)
def __str__(self):
return self.person.full_name
@ -770,8 +820,10 @@ class PeopleFunction(models.Model):
class SpeciesRange(models.Model):
id = models.AutoField(primary_key=True)
scientific_name = models.ForeignKey(Species, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
scientific_name = models.ForeignKey(
Species, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
endemic = models.TextField(blank=True, null=True)
source_url = models.TextField(blank=True, null=True)
@ -782,7 +834,8 @@ class SpeciesRange(models.Model):
class PeopleRange(models.Model):
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
range = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
def __str__(self):
return str(self.id)
@ -790,7 +843,8 @@ class PeopleRange(models.Model):
class TaxonRange(models.Model):
id = models.AutoField(primary_key=True)
range = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
taxon = models.TextField(blank=True, null=True)
subrange_or_region = models.TextField(blank=True, null=True)
taxon_status = models.TextField(blank=True, null=True)
@ -821,7 +875,8 @@ class PeopleResource(models.Model):
)
id = models.AutoField(primary_key=True)
person = models.ForeignKey(Person, models.CASCADE, blank=True, null=True)
resource = models.ForeignKey(Resource, models.DO_NOTHING, blank=True, null=True)
resource = models.ForeignKey(
Resource, models.DO_NOTHING, blank=True, null=True)
role = models.TextField(blank=True, null=True, choices=ROLES_CHOICES)
def __str__(self):
@ -830,8 +885,10 @@ class PeopleResource(models.Model):
class RangeCountry(models.Model):
id = models.AutoField(primary_key=True)
range = models.ForeignKey(MountainRange, models.DO_NOTHING, blank=True, null=True)
country = models.ForeignKey(Country, models.DO_NOTHING, blank=True, null=True)
range = models.ForeignKey(
MountainRange, models.DO_NOTHING, blank=True, null=True)
country = models.ForeignKey(
Country, models.DO_NOTHING, blank=True, null=True)
def __str__(self):
return '%s --- %s' % (self.range.range_name if self.range else 'None', self.country.short_name if self.country else 'None')

View file

@ -248,7 +248,7 @@
</div><!-- /person-tags -->
<a name="contact"></a><h3>Contact</h3>
<form action="https://formspree.io/&#103;&#109;&#098;&#097;&#064;&#105;&#112;&#115;&#046;&#117;&#110;&#105;&#098;&#101;&#046;&#099;&#104;"
<form onsubmit="{ FormSubmitted }" action="https://formspree.io/f/xvolqjpe"
target="_blank" method="POST" class="contact-form">
<input type="hidden" name="subject" value="Contact request from GMBA Connect">
<input type="hidden" name="person" value={ person.data.fullname }>
@ -287,6 +287,12 @@
const FILTER_BLANK = {
'country': [], 'range': [], 'field': [], 'taxon': []
}
FormSubmitted(){
console.log("i got executed")
var self = this
self.closedetails()
}
this.filters_shown = FILTER_BLANK
search(e, nextpage) {
@ -481,6 +487,9 @@
}
})
</script>
</gmba-search>

View file

@ -9,6 +9,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<meta name="referrer" content="origin">
<link href="{% static 'app/admin/bootstrap/bootstrap3/swatch/default/bootstrap.min.css' %}?v=3.3.5" rel="stylesheet">

View file

@ -6,6 +6,8 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="referrer" content="origin">
<!--[if lte IE 8]><script src="/static/assets/js/ie/html5shiv.js"></script><![endif]-->
<!--[if lte IE 8]><link rel="stylesheet" href="/static/assets/css/ie8.css" /><![endif]-->

View file

@ -5,6 +5,8 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="referrer" content="origin">
<!--[if lte IE 8]><script src="{% static 'app/assets/js/ie/html5shiv.js' %}"></script><![endif]-->
<!--[if lte IE 8]><link rel="stylesheet" href="{% static 'app/assets/ss/ie8.css' %}" /><![endif]-->

View file

@ -213,10 +213,10 @@ class SearchView(View):
per_page = int(self.request.GET.get('per_page', 10))
q = self.request.GET.get('q', '').strip()
if not q or len(q) < 3:
query_set = Person.objects.all()
query_set = Person.objects.filter(profile_on_web=True).all()
else:
query = reduce(operator.or_, (Q(field_indexer__icontains=item) for item in q.split(" ")))
query_set = Person.objects.filter(query)
query_set = Person.objects.filter(query).filter(profile_on_web=True)
q_country = self.request.GET.get('country', '')
q_range = self.request.GET.get('range', '')
@ -245,8 +245,8 @@ class PeopleDetailView(View):
person = get_object_or_404(Person, id=people_id)
return_data = {
'data': person.dict(),
'resources': [r.resource.dict() for r in person.resourcespeople_set.all()],
'ranges': [r.range.dict() for r in person.rangespeople_set.all()],
'resources': [r.resource.dict() for r in person.peopleresource_set.all()],
'ranges': [r.range.dict() for r in person.peoplerange_set.all()],
'fields': [r.field.name for r in person.fieldspeople_set.all()],
'methods': [r.method.name for r in person.methodspeople_set.all()],
'scales': [r.scale.name for r in person.scalespeople_set.all()],

37
docker-compose.prod.yml Normal file
View file

@ -0,0 +1,37 @@
version: '3.8'
services:
web:
build:
context: ./
dockerfile: Dockerfile.prod
command: gunicorn gmba_django.wsgi:application --bind [::]:8000
volumes:
- static_volume:/data/app/app/static
- media_volume:/data/app/app/media
expose:
- 8000
env_file:
- ./.env.prod
depends_on:
- db
db:
image: postgres:13.0-alpine
volumes:
- postgres_data:/data/var/lib/postgresql/data/
env_file:
- ./.env.prod
nginx:
build: ./nginx
volumes:
- static_volume:/data/app/app/static
- media_volume:/data/app/app/media
ports:
- 1337:80
depends_on:
- web
volumes:
postgres_data:
static_volume:
media_volume:

15
entrypoint.prod.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/sh
if [ "$DATABASE" = "postgres" ]
then
echo "Waiting for postgres..."
while ! nc -z $SQL_HOST $SQL_PORT; do
sleep 0.1
done
echo "PostgreSQL started"
fi
exec "$@"

View file

@ -4,7 +4,11 @@ DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.postgresql'),
'NAME': os.environ.get('POSTGRES_DB', 'app'),
'USER': os.environ.get('POSTGRES_USER', 'app'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', 'localhost'),
'PORT': os.environ.get('POSTGRES_PORT', 5432),
}
}

View file

@ -6,17 +6,20 @@ ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', "").split(",")
DATABASES = {
'default': {
'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.sqlite3'),
'NAME': os.environ.get('SQL_DATABASE', os.path.join(BASE_DIR, 'db.sqlite3')),
# 'USER': os.environ.get('SQL_USER', 'user'),
# 'PASSWORD': os.environ.get('SQL_PASSWORD', 'password'),
# 'HOST': os.environ.get('SQL_HOST', 'localhost'),
# 'PORT': os.environ.get('SQL_PORT', ''),
'ENGINE': os.environ.get('SQL_ENGINE', 'django.db.backends.postgresql'),
'NAME': os.environ.get('POSTGRES_DB', 'app'),
'USER': os.environ.get('POSTGRES_USER', 'app'),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', 'localhost'),
'PORT': os.environ.get('POSTGRES_PORT', 5432),
}
}
SECURE_SSL_REDIRECT = True
# See https://docs.djangoproject.com/en/2.2/ref/settings/#secure-proxy-ssl-header
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

6
init.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/ash
#python manage.py migrate
python manage.py collectstatic --noinput
#
#gunicorn gmba_django.wsgi:application --bind [::]:8000

4
nginx/Dockerfile Normal file
View file

@ -0,0 +1,4 @@
FROM nginx:1.21-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d

28
nginx/nginx.conf Normal file
View file

@ -0,0 +1,28 @@
upstream gmba_django {
server web:8000;
}
server {
listen 80;
client_max_body_size 256m;
location / {
proxy_pass http://gmba_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /home/app/app/static/;
expires max;
}
location /media/ {
alias /home/app/app/media/;
expires max;
}
add_header Content-Security-Policy "frame-ancestors https://www.gmba.unibe.ch";
}

View file

@ -6,3 +6,4 @@ sqlparse==0.4.1
typing-extensions==3.10.0.0
psycopg2==2.9.1
Werkzeug==2.0.2
gunicorn==20.1.0