diff --git a/fotos/forms.py b/fotos/forms.py
index fc83da7c86136988933c46c020406909c73dad7a..e967a90687e1a8caa42523050ddf2af962d7fbf5 100644
--- a/fotos/forms.py
+++ b/fotos/forms.py
@@ -1,12 +1,12 @@
from django import forms
from django.forms import ClearableFileInput
-from fotos.models import UploadFile
+from fotos.models import UploadedImage
-class FileUpload(forms.ModelForm):
+class UploadedImageForm(forms.ModelForm):
class Meta:
- model = UploadFile
- fields = ['files']
+ model = UploadedImage
+ fields = [ 'files' ]
widgets = {
'files': ClearableFileInput(attrs = { 'multiple': True })
}
diff --git a/fotos/models.py b/fotos/models.py
index 69251d70a48a39ebddd492b699090761ae684902..b6120873c2037dfde868c23ee358d8e57c35263e 100644
--- a/fotos/models.py
+++ b/fotos/models.py
@@ -127,7 +127,7 @@ class Photo(models.Model):
'location']) ]
ordering = ('timestamp',)
-class UploadFile(models.Model):
+class UploadedImage(models.Model):
- files = models.FileField(upload_to=UPLOAD_TMP_DIR,
- blank=True, null=True)
+ files = models.ImageField(upload_to=UPLOAD_TMP_DIR,
+ blank=True, null=True)
diff --git a/fotos/serializers.py b/fotos/serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..dc981f3cb07d1ba6846ad459d472b7a0508da8d0
--- /dev/null
+++ b/fotos/serializers.py
@@ -0,0 +1,29 @@
+from rest_framework import serializers
+
+from fotos.models import Album, Photo, UploadedImage
+
+class AlbumSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = Album
+ fields = [ 'the_parent', 'name', 'slug',
+ 'first_timestamp', 'last_timestamp',
+ 'time_offset' ]
+
+class PhotoSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = Photo
+ fields = [ 'title', 'description', 'the_album',
+ 'latitude', 'longitude', 'width', 'height',
+ 'view_width', 'view_height',
+ 'thumb_width', 'thumb_height', 'timestamp',
+ 'view_timestamp', 'time_offset', 'url',
+ 'view_url', 'thumb_url', 'basename',
+ 'view_latitude', 'view_longitude' ]
+
+class UploadedImageSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = UploadedImage
+ fields = [ 'files' ]
diff --git a/fotos/templates/base.html b/fotos/templates/base.html
index b00d04623e6034cac9f41aab2b51e827d659029d..ff7faccd5ab5ada3ad8cf6063c94eabfa8c695ec 100644
--- a/fotos/templates/base.html
+++ b/fotos/templates/base.html
@@ -19,11 +19,14 @@
{% else %}
diff --git a/fotos/views.py b/fotos/views.py
index 723e053c17db2dea116daa5da0ddedd79d5bcc1a..ed7664e15ff7d52e6a49a0f7f4ccad548cfb1dec 100644
--- a/fotos/views.py
+++ b/fotos/views.py
@@ -1,17 +1,25 @@
import os
+import os.path
import sys
from django.views.generic.edit import FormView
+from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, redirect
from django.contrib.auth import get_user
from django.http import HttpResponse, Http404, JsonResponse
from django.db import transaction
+from rest_framework.parsers import JSONParser
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+
from galeria.settings import GALLERY_INFO, LANGUAGE_CODE
-from fotos.models import Album, Photo, UploadFile
-from fotos.forms import FileUpload
+from fotos.models import Album, Photo, UploadedImage
+from fotos.forms import UploadedImageForm
from fotos.util import get_image_metadata, normalize
+from fotos.serializers import AlbumSerializer, PhotoSerializer, \
+ UploadedImageSerializer
from datetime import datetime
from babel.dates import get_month_names
@@ -50,30 +58,156 @@ def _get_events(ma):
def _get_photos(ea):
return Photo.objects.filter(the_album=ea).order_by('timestamp')
-def _get_prev_year(ya):
+def _get_prev_year_url(ya):
+ prev_year = _get_prev_year(ya)
+ if prev_year is not None:
+ return prev_year.get_url_path()
return None
-def _get_next_year(ya):
+def _get_next_year_url(ya):
+ next_year = _get_next_year(ya)
+ if next_year is not None:
+ return next_year.get_url_path()
return None
-def _get_prev_month(ma):
+def _get_prev_month_url(ma):
+ prev_month = _get_prev_month(ma)
+ if prev_month is not None:
+ return prev_month.get_url_path()
return None
-def _get_next_month(ma):
+def _get_next_month_url(ma):
+ next_month = _get_next_month(ma)
+ if next_month is not None:
+ return next_month.get_url_path()
return None
-def _get_prev_event(ea):
+def _get_prev_event_url(ea):
+ prev_event = _get_prev_event(ea)
+ if prev_event is not None:
+ return prev_event.get_url_path()
return None
-def _get_next_event(ea):
+def _get_next_event_url(ea):
+ next_event = _get_next_event(ea)
+ if next_event is not None:
+ return next_event.get_url_path()
return None
-def _get_prev_photo(ph):
+def _get_prev_photo_url(ph):
+ prev_photo = _get_prev_photo(ph)
+ if prev_photo is not None:
+ return prev_photo.get_url_path()
return None
-def _get_next_photo(ph):
+def _get_next_photo_url(ph):
+ next_photo = _get_next_photo(ph)
+ if next_photo is not None:
+ return next_photo.get_url_path()
return None
+def _get_prev_year(ya):
+ years = _get_years()
+ if years is None or years.first() == ya:
+ return None
+ i = 1
+ while years[i] != ya:
+ i += 1
+ return years[i-1]
+
+def _get_next_year(ya):
+ years = _get_years()
+ if years is None or years.last() == ya:
+ return None
+ i = years.count() - 2
+ while years[i] != ya:
+ i -= 1
+ return years[i+1]
+
+def _get_prev_month(ma):
+ ya = ma.the_parent
+ months = _get_months(ya)
+ if ma == months.first():
+ prev_year = _get_prev_year(ya)
+ if prev_year is None:
+ return None
+ prev_year_months = _get_months(prev_year)
+ return prev_year_months.last()
+ i = 1
+ while months[i] != ma:
+ i += 1
+ return months[i-1]
+
+def _get_next_month(ma):
+ ya = ma.the_parent
+ months = _get_months(ya)
+ if ma == months.last():
+ next_year = _get_next_year(ya)
+ if next_year is None:
+ return None
+ next_year_months = _get_months(next_year)
+ return next_year_months.first()
+ i = months.count() - 2
+ while months[i] != ma:
+ i -= 1
+ return months[i+1]
+
+def _get_prev_event(ea):
+ ma = ea.the_parent
+ events = _get_events(ma)
+ if ea == events.first():
+ prev_month = _get_prev_month(ma)
+ if prev_month is None:
+ return None
+ prev_month_events = _get_events(prev_month)
+ return prev_month_events.last()
+ i = 1
+ while events[i] != ea:
+ i += 1
+ return events[i-1]
+
+def _get_next_event(ea):
+ ma = ea.the_parent
+ events = _get_events(ma)
+ if ea == events.last():
+ next_month = _get_next_month(ma)
+ if next_month is None:
+ return None
+ next_month_events = _get_events(next_month)
+ return next_month_events.first()
+ i = events.count() - 2
+ while events[i] != ea:
+ i -= 1
+ return events[i+1]
+
+def _get_prev_photo(ph):
+ ea = ph.the_album
+ photos = _get_photos(ea)
+ if ph == photos.first():
+ prev_event = _get_prev_event(ea)
+ if prev_event is None:
+ return None
+ prev_event_photos = _get_photos(prev_event)
+ return prev_event_photos.last()
+ i = 1
+ while photos[i] != ph:
+ i += 1
+ return photos[i-1]
+
+def _get_next_photo(ph):
+ ea = ph.the_album
+ photos = _get_photos(ea)
+ if ph == photos.last():
+ next_event = _get_next_event(ea)
+ if next_event is None:
+ return None
+ next_event_photos = _get_photos(next_event)
+ return next_event_photos.first()
+ i = photos.count() - 2
+ while photos[i] != ph:
+ i -= 1
+ return photos[i+1]
+
def main(request):
page_title = 'Años'
albums = _get_years()
@@ -98,8 +232,8 @@ def year(request, year):
'breadcrumbs': [ { 'link': ya.get_url_path(),
'name': 'Año ' + year } ],
'gallery': GALLERY_INFO,
- 'navigation': { 'prev': _get_prev_year(ya),
- 'next': _get_next_year(ya),
+ 'navigation': { 'prev': _get_prev_year_url(ya),
+ 'next': _get_next_year_url(ya),
'up': '/galeria' },
'now_year': datetime.now().year,
'page_title': 'Año ' + year,
@@ -111,7 +245,7 @@ def month(request, year, month):
ya = _get_year_album(year)
ma = _get_month_album(ya, month)
if ya is None or ma is None:
- raise Http404(year + '/' + month + ' does not exist')
+ raise Http404(os.path.join(year, month) + ' does not exist')
attrs = { 'album': ma,
'albums': _get_events(ma),
'breadcrumbs': [ { 'link': ya.get_url_path(),
@@ -119,14 +253,14 @@ def month(request, year, month):
{ 'link': ma.get_url_path(),
'name': ma.name } ],
'gallery': GALLERY_INFO,
- 'navigation': { 'prev': _get_prev_month(ma),
- 'next': _get_next_month(ma),
+ 'navigation': { 'prev': _get_prev_month_url(ma),
+ 'next': _get_next_month_url(ma),
'up': ya.get_url_path() },
'now_year': datetime.now().year,
'page_title': ('Mes de %s, %d' % \
( get_month_name(ma.first_timestamp),
ya.first_timestamp.year )),
- 'prefix': '/galeria/' + year + '/' + month,
+ 'prefix': os.path.join('/galeria/', year, month),
'user': get_user(request) }
return render(request, 'album.html', attrs)
@@ -135,7 +269,7 @@ def event(request, year, month, event):
ma = _get_month_album(ya, month)
ea = _get_event_album(ma, event)
if ya is None or ma is None or ea is None:
- raise Http404(year + '/' + month + '/' + event + ' does not exist')
+ raise Http404(os.path.join(year, month, event) + ' does not exist')
attrs = { 'album': ea,
'breadcrumbs': [ { 'link': ya.get_url_path(),
'name': ya.name },
@@ -144,13 +278,13 @@ def event(request, year, month, event):
{ 'link': ea.get_url_path(),
'name': ea.name } ],
'gallery': GALLERY_INFO,
- 'navigation': { 'prev': _get_prev_event(ea),
- 'next': _get_next_event(ea),
+ 'navigation': { 'prev': _get_prev_event_url(ea),
+ 'next': _get_next_event_url(ea),
'up': ma.get_url_path() },
'now_year': datetime.now().year,
'page_title': ea.name,
'photos': _get_photos(ea),
- 'prefix': '/galeria/' + year + '/' + month + '/' + event,
+ 'prefix': os.path.join('/galeria', year, month, event),
'user': get_user(request) }
return render(request, 'album.html', attrs)
@@ -160,8 +294,8 @@ def photo(request, year, month, event, photo):
ea = _get_event_album(ma, event)
ph = _get_photo(ea, photo)
if ya is None or ma is None or ea is None or ph is None:
- raise Http404(year + '/' + month + '/' +
- event + '/' + photo + ' does not exist')
+ raise Http404(os.path.join(year, month, event, photo +
+ ' does not exist'))
attrs = { 'breadcrumbs': [ { 'link': ya.get_url_path(),
'name': ya.name },
{ 'link': ma.get_url_path(),
@@ -171,13 +305,13 @@ def photo(request, year, month, event, photo):
{ 'link': ph.get_url_path(),
'name': ph.title } ],
'gallery': GALLERY_INFO,
- 'navigation': { 'prev': _get_prev_photo(ph),
- 'next': _get_next_photo(ph),
+ 'navigation': { 'prev': _get_prev_photo_url(ph),
+ 'next': _get_next_photo_url(ph),
'up': ea.get_url_path() },
'now_year': datetime.now().year,
'page_title': ph.title,
'photo': ph,
- 'prefix': '/galeria/' + year + '/' + month + '/' + event,
+ 'prefix': os.path.join('/galeria', year, month, event),
'user': get_user(request) }
return render(request, 'photo.html', attrs)
@@ -221,9 +355,9 @@ def _get_photo_album(metadata):
return year_album, month_album, event_album
@transaction.atomic
-def _add_photo(upload_file):
- path = '/' + upload_file.files.name
- upload_file.delete()
+def _add_photo(uploaded_image):
+ path = '/' + uploaded_image.files.name
+ uploaded_image.delete()
md = get_image_metadata(path)
if md is None:
os.remove(path)
@@ -252,28 +386,36 @@ def _add_photo(upload_file):
if event_album.the_photo is None:
event_album.the_photo = photo
event_album.save()
+ if month_album.first_timestamp > event_album.first_timestamp:
+ month_album.first_timestamp = event_album.first_timestamp
+ if month_album.last_timestamp < event_album.last_timestamp:
+ month_album.last_timestamp = event_album.last_timestamp
if month_album.the_photo is None:
month_album.the_photo = photo
- month_album.save()
+ month_album.save()
+ if year_album.first_timestamp > month_album.first_timestamp:
+ year_album.first_timestamp = month_album.first_timestamp
+ if year_album.last_timestamp < month_album.last_timestamp:
+ year_album.last_timestamp = month_album.last_timestamp
if year_album.the_photo is None:
year_album.the_photo = photo
- year_album.save()
+ year_album.save()
os.remove(path)
-def admin_upload(request):
+def upload(request):
if request.method == 'POST':
- form = FileUpload(request.POST, request.FILES)
+ form = UploadedImageForm(request.POST, request.FILES)
files = request.FILES.getlist('files')
if form.is_valid():
for f in files:
- upload_file = UploadFile(files=f)
- upload_file.save()
- _add_photo(upload_file)
+ uploaded_image = UploadedImage(files=f)
+ uploaded_image.save()
+ _add_photo(uploaded_image)
return JsonResponse({'data': 'Data uploaded'})
else:
return JsonResponse({'data': 'Something went wrong'})
else:
- form = FileUpload()
+ form = UploadedImageForm()
return render(request, 'upload.html',
{ 'form': form,
'breadcrumbs': [],
@@ -283,3 +425,88 @@ def admin_upload(request):
'up': None },
'now_year': datetime.now().year,
'page_title': 'Subir fotos' })
+
+def highlight_month(request, year, month):
+ ya = _get_year_album(year)
+ ma = _get_month_album(ya, month)
+ if ya is None or ma is None:
+ raise Http404(os.path.join(year, month) + ' does not exist')
+
+ if ya.the_photo == ma.the_photo:
+ return redirect(os.path.join('/galeria', year, month))
+
+ ya.the_photo = ma.the_photo
+ ya.save()
+ return redirect(os.path.join('/galeria', year))
+
+def highlight_event(request, year, month, event):
+ ya = _get_year_album(year)
+ ma = _get_month_album(ya, month)
+ ea = _get_event_album(ma, event)
+ if ya is None or ma is None or ea is None:
+ raise Http404(os.path.join(year, month, event) + ' does not exist')
+
+ if ma.the_photo == ea.the_photo:
+ return redirect(os.path.join('/galeria', year, month, event))
+
+ old_ph = ma.the_photo
+ if ya.the_photo == old_ph:
+ ya.the_photo = ea.the_photo
+ ya.save()
+ ma.the_photo = ea.the_photo
+ ma.save()
+ return redirect(os.path.join('/galeria', year, month))
+
+def highlight_photo(request, year, month, event, photo):
+ ya = _get_year_album(year)
+ ma = _get_month_album(ya, month)
+ ea = _get_event_album(ma, event)
+ ph = _get_photo(ea, photo)
+ if ya is None or ma is None or ea is None or ph is None:
+ raise Http404(os.path.join(year, month, event, photo) +
+ ' does not exist')
+
+ if ea.the_photo == ph:
+ return redirect(os.path.join('/galeria', year, month, event, photo))
+
+ old_ph = ea.the_photo
+ if ya.the_photo == old_ph:
+ ya.the_photo = ph
+ ya.save()
+ if ma.the_photo == old_ph:
+ ma.the_photo = ph
+ ma.save()
+ ea.the_photo = ph
+ ea.save()
+ return redirect(os.path.join('/galeria', year, month, event))
+
+@api_view(['GET'])
+def album_list(request):
+
+ if request.method == 'GET':
+ albums = Album.objects.all()
+ serializer = AlbumSerializer(albums, many=True)
+ return Response(serializer.data)
+
+@api_view(['GET'])
+def photo_list(request):
+
+ if request.method == 'GET':
+ photos = Photo.objects.all()
+ serializer = PhotoSerializer(photos, many=True)
+ return Response(serializer.data)
+
+@api_view(['POST'])
+def upload_image(request):
+
+ if request.method == 'POST':
+ form = UploadedImageSerializer(request.POST, request.FILES)
+ files = request.FILES.getlist('files')
+ if form.is_valid():
+ for f in files:
+ uploaded_image = UploadedImage(files=f)
+ uploaded_image.save()
+ _add_photo(uploaded_image)
+ return JsonResponse({'response':'OK'})
+ else:
+ return JsonResponse({'response':'Error'})
diff --git a/galeria/settings.py b/galeria/settings.py
index 049073f0950c94f5c9219963cd3d05ee04ea7b8b..587e616e335d27466e9486206a81e6fc87951679 100644
--- a/galeria/settings.py
+++ b/galeria/settings.py
@@ -23,7 +23,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-4=b2093i%q)ypq1smb^00p7(=ap*1(7q4pi82g%!o3+a63vs23'
# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = True
+DEBUG = False
ALLOWED_HOSTS = [ '127.0.0.1', 'localhost', 'aztlan.fciencias.unam.mx' ]
@@ -37,6 +37,7 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'rest_framework',
]
MIDDLEWARE = [
diff --git a/galeria/urls.py b/galeria/urls.py
index 53cc4644beb871ff6db3cfc0722a650ebb75fd51..57c33db20f32813c5a26d4a50ba5b61c2a9e7ce2 100644
--- a/galeria/urls.py
+++ b/galeria/urls.py
@@ -24,6 +24,15 @@ urlpatterns = [
url(r'^(\d{4})/(\d{2})/$', views.month),
url(r'^(\d{4})/(\d{2})/([\w-]+)/$', views.event),
url(r'^(\d{4})/(\d{2})/([\w-]+)/([\w\.-]+)$', views.photo),
- url(r'^admin/upload', views.admin_upload),
+ url(r'^admin/upload', views.upload),
+ url(r'^admin/highlight/(\d{4})/(\d{2})/$',
+ views.highlight_month),
+ url(r'^admin/highlight/(\d{4})/(\d{2})/([\w-]+)/$',
+ views.highlight_event),
+ url(r'^admin/highlight/(\d{4})/(\d{2})/([\w-]+)/([\w\.-]+)$',
+ views.highlight_photo),
+ url(r'^rest/album_list$', views.album_list),
+ url(r'^rest/photo_list$', views.photo_list),
+ url(r'^rest/upload_image$', views.upload_image),
url('admin/', admin.site.urls),
]
diff --git a/static/galeria.js b/static/galeria.js
index 72d418eb1c58e7789f5867f5014097818588f556..b4beea2ff09d9b5a5dac3646f6f67ad42d090c2a 100644
--- a/static/galeria.js
+++ b/static/galeria.js
@@ -1,4 +1,16 @@
-/* When the user clicks on the button,
+function goTo(url) {
+ window.location.href=url;
+}
+
+function highlight(breadcrumbs) {
+ breadcrumbs = breadcrumbs.replaceAll("'", '"');
+ var bc = JSON.parse(breadcrumbs);
+ var leaf = bc.at(-1);
+ var link = leaf['link'].replaceAll('/galeria/', '/galeria/admin/highlight/');
+ goTo(link);
+}
+
+/* When the user clicks on the menu Administration,
toggle between hiding and showing the dropdown content */
function adminDropdown() {
document.getElementById("admin-dropdown").classList.toggle("show");
diff --git a/static/progress-bar.js b/static/progress-bar.js
index 8f951ad63162aa0cf80528d4105c285e2b30274b..035f355b2055961bc39bc9caeae50d7b6cc6e656 100644
--- a/static/progress-bar.js
+++ b/static/progress-bar.js
@@ -10,7 +10,8 @@ $("#upload_form").submit(function(e) {
const media_data = input_file.files[0];
if (media_data != null) {
console.log(media_data);
- progress_bar.classList.remove("not-visible");
+ progress_bar.classList.remove('not-visible');
+ result_message.classList.add('not-visible');
}
$.ajax({
@@ -45,6 +46,11 @@ $("#upload_form").submit(function(e) {
},
error: function(err){
console.log(err);
+ uploadForm.reset()
+ progress_bar.classList.add('not-visible')
+ result_message.classList.remove('not-visible')
+ result_message.innerHTML =
+ 'Ocurrió un error al subier los archivos.';
},
cache: false,
contentType: false,
diff --git a/static/style.css b/static/style.css
index 491212817f18374243022c72b40099ac97cb2a07..2be19135e17125bfa5c2dc2fcb30e8ff90ecabb2 100644
--- a/static/style.css
+++ b/static/style.css
@@ -268,6 +268,14 @@ form.upload {
display: block;
}
+button.menu-item {
+ border: none;
+ color: black;
+ padding: 6px 8px;
+ text-decoration: none;
+ display: block;
+}
+
div.signin-link {
position: absolute;
right: 20px;