from django.contrib.auth.models import Group, User
from django.core.mail import send_mail
from django.shortcuts import reverse
+from django.utils.html import format_html
from django.utils.translation import ngettext
-from georeport.models import Category, Report
+from georeport.models import Category, Image, Report
+
+from django import forms
+from .minio import get_url
+
+import pdb
+from django.utils.safestring import mark_safe
+
+# TODO: reorder
class CategoryInline(admin.TabularInline):
return qs
+class ImageInline(admin.StackedInline):
+ model = Image
+ extra = 0
+ can_delete = True
+
+ @override
+ def has_change_permission(self, request, obj=None):
+ return False
+
+ def image_preview(self, obj):
+ if obj.file:
+ url = get_url(obj.file)
+ return format_html(f'<img src="{url}" width="300px" height="300px"/>')
+ return ""
+
+ fields = ("image_preview",)
+ readonly_fields = ("image_preview",)
+
+
@admin.register(Report)
class ReportAdmin(admin.ModelAdmin):
+ # TODO: If images are added through admin, they are not in minio. This feature has to be added
exclude = [
"_oldState",
]
- readonly_fields = [
- "created_at",
- "updated_at",
- ]
-
+ readonly_fields = ["created_at", "updated_at"]
list_display = ["title", "category__name", "state", "published"]
list_filter = ["state"]
+ inlines = [ImageInline]
@admin.action(description="Publish selected reports.")
def make_public(self, request, queryset):
obj._oldstate = obj.state
super().save_model(request, obj, form, change)
+ actions = [make_public]
+
def send_update(report):
# TODO: Tests
# GNU General Public License v3.0 (see LICSENE or https://www.gnu.org/license/gpl-3.0.md)
from django.forms import ModelForm
-from .models import Report
+from .models import Image, Report
class ReportForm(ModelForm):
class Meta:
model = Report
- fields = ["title", "description", "email", "category","latitude", "longitude"]
+ fields = ["title", "description", "email", "category", "latitude", "longitude"]
+
+
+class ImageForm(ModelForm):
+ class Meta:
+ model = Image
+ fields = ["file", "report"]
--- /dev/null
+# Generated by Django 5.1.5 on 2025-02-12 11:34
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('georeport', '0004_rename_user_category_users_alter_report_title'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Image',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('file', models.CharField(max_length=255)),
+ ('report', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='georeport.report')),
+ ],
+ ),
+ ]
--- /dev/null
+# Copyright: (c) 2025, Jörn Menne <jmenne@posteo.de>
+# GNU General Public License v3.0 (see LICSENE or https://www.gnu.org/license/gpl-3.0.md)
+
+from django.conf import settings
+import os
+import shutil
+from minio import Minio
+from .models import Report
+from .forms import ImageForm
+
+client = Minio(
+ "localhost:9000", access_key="minio", secret_key="minio123", secure=False
+)
+
+
+def handle_file_uploads(request_files, report_dict):
+ """
+ Handles the upload of files to
+ minio
+ """
+ report = (
+ Report.objects.filter(title=report_dict["title"]) # type:ignore
+ .filter(latitude=report_dict["latitude"])
+ .filter(longitude=report_dict["longitude"])
+ .first()
+ )
+ # client = Minio(
+ # f"{settings.MINIO_HOST}:{settings.MINIO_PORT}",
+ # access_key=settings.MINIO_ACCESS_KEY,
+ # secret_key=settings.SECRET_KEY,
+ # secure=False,
+ # )
+
+ files = request_files.getlist("image")
+ bucketname = settings.BUCKET_NAME
+ if not client.bucket_exists(bucketname):
+ client.make_bucket(bucketname)
+
+ pathprefix = "./georeport/static/georeport/images"
+ for f in files:
+ if not os.path.exists(pathprefix):
+ os.makedirs(pathprefix)
+
+ image = {}
+ image["file"] = f.name
+ path = os.path.join(pathprefix, f.name)
+ with open(path, "wb+") as dest:
+ for chunk in f.chunks():
+ dest.write(chunk)
+ client.fput_object(bucketname, image["file"], path)
+
+ image["report"] = report
+ imgForm = ImageForm(image)
+ imgForm.save()
+ shutil.rmtree(pathprefix)
+
+
+def get_url(filename):
+ """
+ Wrapper for presigned_get_object
+ """
+ return client.presigned_get_object(settings.BUCKET_NAME, filename)
# TODO: Image NEXT
+class Image(models.Model):
+ """
+ Representation of an image located in the minio
+ Each image is connected to exactly one report
+ """
+
+ file = models.CharField(max_length=255)
+
+ report = models.ForeignKey(Report, related_name="images", on_delete=models.CASCADE)
+
+ @override
+ def __str__(self) -> str:
+ return str(self.file)
<p id="p-lng" data-lng="{{ report.longitude }}">Longitude: {{ report.longitude }}</p>
<p>Status: {{ report.get_state_display }} </p>
<p>Kategorie: {{ report.category }} </p>
- <a href="{% url 'georeport:index' %}">Back</a>
<script src="{% static 'georeport/addMarker.js' %}"></script>
<!-- <img src="{{report.image.url}}" alt="Kein Bild vorhanden" scale=0.25>-->
{% for img in report.images.all %}
- <!--<img src="{% static 'georeport/images/' %}{{img.file}}" alt={{img.alt}} scale=0.25 width=500px>-->
- <img src={{urls|key:img.alt}} alt={{img.alt}} width=500px>
+ <img src={{urls|key:img.file}} alt={{img.file}} width=500px>
{% endfor %}
+ <a href="{% url 'georeport:index' %}">Back</a>
</div>
{% endblock %}
A view takes a request and creates a respond for the request.
"""
-from django.core.exceptions import PermissionDenied
-from django.http import HttpResponse, HttpResponseForbidden, JsonResponse
-from django.shortcuts import get_object_or_404, render, redirect
-from django.utils.http import urlsafe_base64_decode
-from django.views.decorators.http import require_GET, require_safe, require_http_methods
-
-from pinpoint_report.settings import DEFAULT_FROM_EMAIL
-from .models import Category, Report
+import os
+import shutil
+from base64 import urlsafe_b64decode
-from .forms import ReportForm
+from Crypto.Cipher import ChaCha20
from django.conf import settings
+from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail
+from django.http import HttpResponse, JsonResponse
+from django.shortcuts import get_object_or_404, redirect, render
+from django.views.decorators.http import require_GET, require_http_methods, require_safe
+from minio import Minio
-from Crypto.Cipher import ChaCha20
-from base64 import urlsafe_b64decode
+from pinpoint_report.settings import DEFAULT_FROM_EMAIL
from .admin import send_update
+from .forms import ImageForm, ReportForm
+from .models import Category, Report
+
+from .minio import handle_file_uploads, get_url
# TODO: test
reportForm.save()
send_creation_confirmation(report)
send_creation_mail(report)
-
+ # TODO: restrict to set number of images
+ handle_file_uploads(request.FILES, report)
return redirect("georeport:index")
return render(
Returns the detail-view page of a single report
"""
report = get_object_or_404(Report, pk=id)
-
+ images = report.images.all()
+ urls = {}
+ for image in images:
+ urls[image.file] = get_url(image.file)
if report.published:
- return render(request, "georeport/detail.html", context={"report": report})
+ return render(
+ request,
+ "georeport/detail.html",
+ context={"report": report, "urls": urls},
+ )
raise PermissionDenied
import sys
from pathlib import Path
from Crypto.Random import get_random_bytes
-from minio import Minio
# Build paths inside the project like this: BASE_DIR / 'subdir'.
MINIO_ACCESS_KEY = "minio"
MINIO_SECRET_KEY = "minio123"
-client = Minio(
- f"{MINIO_HOST}:{MINIO_PORT}", access_key=MINIO_ACCESS_KEY, secret_key=SECRET_KEY
-)
+BUCKET_NAME = "pinpoint"