Compare commits
16 Commits
develop
...
e64cc4bd5f
| Author | SHA1 | Date | |
|---|---|---|---|
| e64cc4bd5f | |||
| a498971d66 | |||
| 527ab07b6f | |||
| e2e236d650 | |||
| c4100a9ade | |||
| 9511b46af0 | |||
| 5b906a7708 | |||
| d68e836b57 | |||
| fe77f1da8d | |||
| 78b71690c0 | |||
| 3b9ee95abc | |||
| b4e50364de | |||
| b014b3b227 | |||
| 99bfe460ee | |||
| d4c7caa42d | |||
| 32c8fc88cf |
@@ -9,15 +9,14 @@ RUN apt install gettext -y
|
|||||||
RUN apt install libpq-dev gcc -y
|
RUN apt install libpq-dev gcc -y
|
||||||
COPY . /app
|
COPY . /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN mkdir /app/data
|
RUN mkdir /app/data/static -p
|
||||||
RUN mkdir /app/data/static
|
|
||||||
RUN mkdir /app/data/media
|
RUN mkdir /app/data/media
|
||||||
RUN pip install -e . # Without the -e the library static folder will not be copied by collectstatic!
|
RUN pip install --no-cache-dir -e . # Without the -e the library static folder will not be copied by collectstatic!
|
||||||
|
|
||||||
RUN nf collectstatic --noinput
|
RUN nf collectstatic --noinput
|
||||||
RUN nf compilemessages --ignore venv
|
RUN nf compilemessages --ignore venv
|
||||||
|
|
||||||
COPY docker/notfellchen.bash /bin/notfellchen
|
COPY docker/entrypoint.sh /bin/notfellchen
|
||||||
|
|
||||||
EXPOSE 7345
|
EXPOSE 7345
|
||||||
CMD ["notfellchen"]
|
CMD ["notfellchen"]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ host=localhost
|
|||||||
secret=CHANGE-ME
|
secret=CHANGE-ME
|
||||||
debug=True
|
debug=True
|
||||||
internal_ips=["127.0.0.1"]
|
internal_ips=["127.0.0.1"]
|
||||||
|
cache=False
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
backend=sqlite3
|
backend=sqlite3
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ dependencies = [
|
|||||||
"django-widget-tweaks",
|
"django-widget-tweaks",
|
||||||
"django-super-deduper",
|
"django-super-deduper",
|
||||||
"django-allauth[mfa]",
|
"django-allauth[mfa]",
|
||||||
|
"django_debug_toolbar",
|
||||||
|
"django-admin-extra-buttons"
|
||||||
]
|
]
|
||||||
|
|
||||||
dynamic = ["version", "readme"]
|
dynamic = ["version", "readme"]
|
||||||
@@ -49,7 +51,6 @@ develop = [
|
|||||||
"pytest",
|
"pytest",
|
||||||
"coverage",
|
"coverage",
|
||||||
"model_bakery",
|
"model_bakery",
|
||||||
"debug_toolbar",
|
|
||||||
]
|
]
|
||||||
docs = [
|
docs = [
|
||||||
"sphinx",
|
"sphinx",
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ from django.utils.html import format_html
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
|
||||||
|
from admin_extra_buttons.api import ExtraButtonsMixin, button, link
|
||||||
|
|
||||||
from .models import Language, Text, ReportComment, ReportAdoptionNotice, Log, Timestamp, SearchSubscription, \
|
from .models import Language, Text, ReportComment, ReportAdoptionNotice, Log, Timestamp, SearchSubscription, \
|
||||||
SpeciesSpecificURL, ImportantLocation, SocialMediaPost
|
SpeciesSpecificURL, ImportantLocation, SocialMediaPost
|
||||||
|
|
||||||
@@ -17,6 +19,21 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from .tools.model_helpers import AdoptionNoticeStatusChoices
|
from .tools.model_helpers import AdoptionNoticeStatusChoices
|
||||||
|
|
||||||
|
|
||||||
|
def export_to_csv_generic(model, queryset):
|
||||||
|
meta = model._meta
|
||||||
|
field_names = [field.name for field in meta.fields]
|
||||||
|
|
||||||
|
response = HttpResponse(content_type='text/csv')
|
||||||
|
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
|
||||||
|
writer = csv.writer(response)
|
||||||
|
|
||||||
|
writer.writerow(field_names)
|
||||||
|
for obj in queryset:
|
||||||
|
row = writer.writerow([getattr(obj, field) for field in field_names])
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AdoptionNotice)
|
@admin.register(AdoptionNotice)
|
||||||
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
class AdoptionNoticeAdmin(admin.ModelAdmin):
|
||||||
search_fields = ("name__icontains", "description__icontains")
|
search_fields = ("name__icontains", "description__icontains")
|
||||||
@@ -49,17 +66,7 @@ class UserAdmin(admin.ModelAdmin):
|
|||||||
return format_html('<a href="{}">{} Adoption Notices</a>', url, count)
|
return format_html('<a href="{}">{} Adoption Notices</a>', url, count)
|
||||||
|
|
||||||
def export_as_csv(self, request, queryset):
|
def export_as_csv(self, request, queryset):
|
||||||
meta = self.model._meta
|
response = export_to_csv_generic(self.model, queryset)
|
||||||
field_names = [field.name for field in meta.fields]
|
|
||||||
|
|
||||||
response = HttpResponse(content_type='text/csv')
|
|
||||||
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
|
|
||||||
writer = csv.writer(response)
|
|
||||||
|
|
||||||
writer.writerow(field_names)
|
|
||||||
for obj in queryset:
|
|
||||||
row = writer.writerow([getattr(obj, field) for field in field_names])
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
export_as_csv.short_description = _("Ausgewählte User exportieren")
|
export_as_csv.short_description = _("Ausgewählte User exportieren")
|
||||||
@@ -165,11 +172,26 @@ class SocialMediaPostAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
@admin.register(Log)
|
@admin.register(Log)
|
||||||
class LogAdmin(admin.ModelAdmin):
|
class LogAdmin(ExtraButtonsMixin, admin.ModelAdmin):
|
||||||
ordering = ["-created_at"]
|
ordering = ["-created_at"]
|
||||||
list_filter = ("action",)
|
list_filter = ("action",)
|
||||||
list_display = ("action", "user", "created_at")
|
list_display = ("action", "user", "created_at")
|
||||||
|
actions = ("export_as_csv",)
|
||||||
|
|
||||||
|
@admin.action(description=_("Ausgewählte Logs exportieren"))
|
||||||
|
def export_as_csv(self, request, queryset):
|
||||||
|
response = export_to_csv_generic(Log, queryset)
|
||||||
|
return response
|
||||||
|
|
||||||
|
@button()
|
||||||
|
def export_all_as_csv(self, request):
|
||||||
|
actual_queryset = Log.objects.all()
|
||||||
|
response = export_to_csv_generic(Log, actual_queryset)
|
||||||
|
return response
|
||||||
|
|
||||||
|
@link(href="https://www.google.com/", visible=lambda btn: True)
|
||||||
|
def invisible(self, button):
|
||||||
|
button.visible = False
|
||||||
|
|
||||||
admin.site.register(Animal)
|
admin.site.register(Animal)
|
||||||
admin.site.register(Species)
|
admin.site.register(Species)
|
||||||
|
|||||||
13
src/fellchensammlung/management/commands/print-settings.py
Normal file
13
src/fellchensammlung/management/commands/print-settings.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from django.core.management import BaseCommand
|
||||||
|
|
||||||
|
from notfellchen import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Print the current settings'
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
for key in settings.__dir__():
|
||||||
|
if key.startswith("_") or key == "SECRET_KEY":
|
||||||
|
continue
|
||||||
|
print(f"{key} = {getattr(settings, key)}")
|
||||||
@@ -419,7 +419,6 @@ class AdoptionNotice(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def num_per_sex(self):
|
def num_per_sex(self):
|
||||||
print(f"{self.pk} x")
|
|
||||||
num_per_sex = dict()
|
num_per_sex = dict()
|
||||||
for sex in SexChoices:
|
for sex in SexChoices:
|
||||||
num_per_sex[sex] = len([animal for animal in self.animals if animal.sex == sex])
|
num_per_sex[sex] = len([animal for animal in self.animals if animal.sex == sex])
|
||||||
|
|||||||
@@ -71,7 +71,22 @@
|
|||||||
value="{{ rescue_org.pk }}">
|
value="{{ rescue_org.pk }}">
|
||||||
<input type="hidden" name="action" value="checked">
|
<input type="hidden" name="action" value="checked">
|
||||||
<button class="" type="submit">{% translate "Organisation geprüft" %}</button>
|
<button class="" type="submit">{% translate "Organisation geprüft" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer-item is-warning">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden"
|
||||||
|
name="rescue_organization_id"
|
||||||
|
value="{{ rescue_org.pk }}">
|
||||||
|
<input type="hidden" name="action" value="toggle_active_communication">
|
||||||
|
<button class="" type="submit">
|
||||||
|
{% if rescue_org.ongoing_communication %}
|
||||||
|
{% translate "Aktive Kommunikation beendet" %}
|
||||||
|
{% else %}
|
||||||
|
{% translate "Aktive Kommunikation" %}
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer-item is-danger">
|
<div class="card-footer-item is-danger">
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ def is_404(url):
|
|||||||
class RequestProfiler:
|
class RequestProfiler:
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.data = []
|
||||||
|
|
||||||
def add_status(self, status):
|
def add_status(self, status):
|
||||||
self.data.append((time.time(), status))
|
self.data.append((time.time(), status))
|
||||||
|
|
||||||
|
|||||||
@@ -867,6 +867,9 @@ def rescue_organization_check(request, context=None):
|
|||||||
if action == "checked":
|
if action == "checked":
|
||||||
rescue_org.set_checked()
|
rescue_org.set_checked()
|
||||||
Log.objects.create(user=request.user, action="rescue_organization_checked", )
|
Log.objects.create(user=request.user, action="rescue_organization_checked", )
|
||||||
|
elif action == "toggle_active_communication":
|
||||||
|
rescue_org.ongoing_communication = not rescue_org.ongoing_communication
|
||||||
|
rescue_org.save()
|
||||||
elif action == "set_species_url":
|
elif action == "set_species_url":
|
||||||
species_url_form = SpeciesURLForm(request.POST)
|
species_url_form = SpeciesURLForm(request.POST)
|
||||||
|
|
||||||
@@ -929,7 +932,7 @@ def exclude_from_regular_check(request, rescue_organization_id, source="organiza
|
|||||||
if to_be_excluded:
|
if to_be_excluded:
|
||||||
Log.objects.create(user=request.user,
|
Log.objects.create(user=request.user,
|
||||||
action="rescue_organization_excluded_from_check",
|
action="rescue_organization_excluded_from_check",
|
||||||
text=f"New status: {form.cleaned_data["regular_check_status"]}")
|
text=f"New status: {form.cleaned_data['regular_check_status']}")
|
||||||
|
|
||||||
return redirect(reverse(source))
|
return redirect(reverse(source))
|
||||||
else:
|
else:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -75,8 +75,18 @@ except configparser.NoSectionError:
|
|||||||
DEBUG = config.getboolean('django', 'debug', fallback=False)
|
DEBUG = config.getboolean('django', 'debug', fallback=False)
|
||||||
|
|
||||||
# Internal IPs
|
# Internal IPs
|
||||||
raw_config_value = config.get("django", "internal_ips", fallback=[])
|
internal_ip_raw_config_value = config.get("django", "internal_ips", fallback=None)
|
||||||
INTERNAL_IPS = json.loads(raw_config_value)
|
if internal_ip_raw_config_value:
|
||||||
|
INTERNAL_IPS = json.loads(internal_ip_raw_config_value)
|
||||||
|
|
||||||
|
# Cache
|
||||||
|
if config.getboolean('django', 'cache', fallback=False):
|
||||||
|
CACHES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||||
|
"LOCATION": "uniques-snowflake",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
""" DATABASE """
|
""" DATABASE """
|
||||||
DB_BACKEND = config.get("database", "backend", fallback="sqlite3")
|
DB_BACKEND = config.get("database", "backend", fallback="sqlite3")
|
||||||
@@ -89,7 +99,6 @@ DB_HOST = config.get("database", "host", fallback='')
|
|||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
|
LOCALE_PATHS = [os.path.join(BASE_DIR, 'locale')]
|
||||||
|
|
||||||
|
|
||||||
""" CELERY + KEYDB """
|
""" CELERY + KEYDB """
|
||||||
CELERY_BROKER_URL = config.get("celery", "broker", fallback="redis://localhost:6379/0")
|
CELERY_BROKER_URL = config.get("celery", "broker", fallback="redis://localhost:6379/0")
|
||||||
CELERY_RESULT_BACKEND = config.get("celery", "backend", fallback="redis://localhost:6379/0")
|
CELERY_RESULT_BACKEND = config.get("celery", "backend", fallback="redis://localhost:6379/0")
|
||||||
@@ -235,6 +244,7 @@ INSTALLED_APPS = [
|
|||||||
'drf_spectacular_sidecar', # required for Django collectstatic discovery
|
'drf_spectacular_sidecar', # required for Django collectstatic discovery
|
||||||
'widget_tweaks',
|
'widget_tweaks',
|
||||||
"debug_toolbar",
|
"debug_toolbar",
|
||||||
|
'admin_extra_buttons',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -245,7 +255,9 @@ MIDDLEWARE = [
|
|||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
# Needs to be after SessionMiddleware and before CommonMiddleware
|
# Needs to be after SessionMiddleware and before CommonMiddleware
|
||||||
'django.middleware.locale.LocaleMiddleware',
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
|
"django.middleware.cache.UpdateCacheMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
"django.middleware.cache.FetchFromCacheMiddleware",
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
|||||||
Reference in New Issue
Block a user