from django.urls import path, include
from django.contrib.auth import views as auth_views
-from api.views import Login_v2, RegistrationAPI, ListaPercorsiAPI, DetailPercorsoAPI, DetailPoiAPI, SendFeedbackAPI
+from api.views import Login_v2, RegistrationAPI, ListaPercorsiAPI, DetailPercorsoAPI, DetailPoiAPI, SendFeedbackAPI, \
+ ShowQRCode
urlpatterns = [
path('api-login/', Login_v2.as_view(), name='api_login'),
path('detail_poi/', DetailPoiAPI.as_view(), name='detail_poi'),
path('send_feedback/', SendFeedbackAPI.as_view(), name='send_feedback'),
+
+ path('show_qr_code/', ShowQRCode.as_view(), name='show_qr_code'),
+
]
+import os.path
+
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.shortcuts import render
-from django.utils.translation import ugettext as _
+from django.utils.translation import gettext as _
from rest_framework import status
from django.contrib.auth import authenticate
from rest_framework import serializers
if filter:
poi_serialize = PoiSerializer(filter, many=True)
-
multimedia = Multimedia.objects.filter(poi=filter.first(), is_active=True)
multimedia_list = MultimediaSerializer(multimedia, many=True)
else:
return Response(
{'errors': 'Assicurati che le informazioni relative all\'utente, percorso e valutazione siano state inviate', 'error': True, 'data': '', 'messages': ['KO']})
+
+
+class ShowQRCode(APIView):
+ def get(self, request):
+ id = request.query_params['id_criptato']
+
+ errors = []
+ error = False
+ data = []
+ if id:
+ multimedia = Multimedia.objects.get(chiave_qr_code=str(id))
+ multimedia_list = MultimediaSerializer(multimedia)
+
+ data.append({
+ 'multimedia': multimedia_list.data,
+ })
+
+ return Response({'errors': errors, 'error': error, 'data': data, 'messages': ['OK']})
+
+ else:
+ return Response({'errors': 'Multimedia non presente', 'error': True, 'data': '', 'messages': ['']})
+
+
class MultimediaDatatables(BaseDatatableView):
model = Multimedia
- columns = ['id', 'tipologia', 'nome', 'descrizione', 'media', 'del']
- order_columns = ['id', 'tipologia', 'nome', 'descrizione', 'media', 'del']
+ columns = ['id', 'tipologia', 'nome', 'descrizione', 'media', 'qr_code', 'del']
+ order_columns = ['id', 'tipologia', 'nome', 'descrizione', 'media', 'qr_code', 'del']
def get_initial_queryset(self):
poi = self.request.GET.get('pk')
return escape('{0}'.format(row.descrizione))
if column == 'media':
return f'<a href="{MEDIA_URL}{row.media}" target="_blank" title="vedi" class="btn btn-sm btn-secondary"><i class="far fa-eye"></i> visualizza </a>'
+ if column == 'qr_code':
+ if row.qr_code:
+ return f'<a href="{MEDIA_URL}{row.qr_code}" target="_blank" title="vedi" class="btn btn-sm btn-secondary"><i class="fas fa-qrcode"></i></a>'
+ else:
+ return '<button class="btn btn-sm btn-secondary" onclick="crea_qr_code(%s)"><i class="fas fa-qrcode"></i></button>' % row.pk
if column == 'del':
return '<button class="btn btn-sm btn-danger" onclick="DeleteMultimedia(%s)"><i class="fas fa-trash"></i></button>' % row.pk
else:
class PercorsoDatatables(BaseDatatableView):
model = Percorso
- columns = ['id', 'nome', 'descrizione', 'testo', 'mod', 'del']
- order_columns = ['id', 'nome', 'descrizione', 'testo', 'mod', 'del']
+ columns = ['id', 'icona', 'nome', 'descrizione', 'testo', 'add', 'mod', 'del']
+ order_columns = ['id', 'icona', 'nome', 'descrizione', 'testo', 'add','mod', 'del']
def get_initial_queryset(self):
return self.model.objects.filter(is_active=True)
def render_column(self, row, column):
if column == 'id':
return escape('{0}'.format(row.pk))
+ if column == 'icona':
+ return f'<img src="{MEDIA_URL}{row.icona}" class="text-center user-img mr-2" alt="" height="50px">'
if column == 'nome':
return escape('{0}'.format(row.nome))
if column == 'descrizione':
if column == 'mod':
return '<a class="btn btn-sm btn-secondary" href="/mod_percorso/%s/" ><i class="fas fa-edit"></i></a>' % row.pk
if column == 'del':
- return '<a class="btn btn-sm btn-danger" href="DeletePercorso(%s)" ><i class="fas fa-trash"></i></a>' % row.pk
+ return '<button class="btn btn-sm btn-danger" onclick="DeletePercorso(%s)" ><i class="fas fa-trash"></i></a>' % row.pk
+ if column == 'add':
+ return '<button class="btn btn-sm btn-secondary" onclick="showModalIcona(%s)"><i class="fas fa-image"></i></button>' % row.pk
else:
return super(PercorsoDatatables, self).render_column(row, column)
class Meta:
model = PointOfInterest
- exclude = ('is_active',)
+ exclude = ('is_active','url')
def __init__(self, *args, **kwargs):
super(PoiForm, self).__init__(*args, **kwargs)
self.fields['icona'].required = False
+ self.fields['descrizione'].required = False
for f in self.fields:
- if self.fields[f].widget.input_type != 'checkbox':
+ if self.fields[f].label == 'Descrizione':
+ self.fields[f].max_length = 3000
+ elif self.fields[f].widget.input_type != 'checkbox':
self.fields[f].widget.attrs.update({'class': ' form-control'})
class PercorsoForm(forms.ModelForm):
+ # icona = forms.FileField(label='Icona', widget=forms.FileInput(
+ # attrs={'class': 'form-control', 'form': 'percorso_form'}), )
class Meta:
model = Percorso
- exclude = ('is_active',)
+ exclude = ('is_active','url','icona')
def __init__(self, *args, **kwargs):
super(PercorsoForm, self).__init__(*args, **kwargs)
+ for f in self.fields:
+ if self.fields[f].label == 'Testo':
+ self.fields[f].max_length = 3000
+ self.fields[f].widget.attrs.update({'class': 'form-control'})
+
+class IconaPercorsoForm(forms.ModelForm):
+ class Meta:
+ model = Percorso
+ exclude = ('testo','nome','descrizione','prezzo','is_active','url',)
+
+ def __init__(self, *args, **kwargs):
+ super(IconaPercorsoForm, self).__init__(*args, **kwargs)
for f in self.fields:
self.fields[f].widget.attrs.update({'class': 'form-control'})
class Meta:
model = Multimedia
- exclude = ('poi', 'is_active',)
+ exclude = ('poi', 'is_active', 'chiave_qr_code', 'qr_code')
def __init__(self, *args, **kwargs):
super(MultimediaForm, self).__init__(*args, **kwargs)
lat = models.FloatField()
long = models.FloatField()
icona = models.FileField(upload_to='icon/', null=True)
+ url = models.URLField(null=True)
+ descrizione = models.TextField()
is_danger = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
class Percorso(models.Model):
nome = models.CharField(max_length=255, null=False)
descrizione = models.CharField(max_length=255, null=False)
- testo = models.TextField(max_length=255, null=False)
+ testo = models.TextField()
+ icona = models.FileField(upload_to='icon_percorso/', null=True)
+ url = models.URLField(null=True)
prezzo = models.FloatField()
is_active = models.BooleanField(default=True)
nome = models.CharField(max_length=255, null=False)
descrizione = models.CharField(max_length=255, null=False)
testo = models.TextField(max_length=255, null=False)
+ qr_code = models.FileField(upload_to='poi_media_qr_code/', null=True)
+ chiave_qr_code = models.CharField(max_length=255, null=True)
is_active = models.BooleanField(default=True)
class PercorsoSerializer(serializers.ModelSerializer):
class Meta:
model = Percorso
- fields = ('id', 'nome', 'descrizione', 'testo')
+ fields = ('id', 'nome', 'descrizione', 'testo', 'icona', 'url')
class FeedbackSerializer(serializers.ModelSerializer):
class PoiSerializer(serializers.ModelSerializer):
class Meta:
model = PointOfInterest
- fields = ('id', 'nome', 'lat', 'long', 'icona','is_danger')
+ fields = ('id', 'nome', 'lat', 'long', 'icona','is_danger', 'url', 'descrizione')
class MultimediaSerializer(serializers.ModelSerializer):
)
class Meta:
model = Multimedia
- fields = ('id', 'nome', 'tipologia', 'media', 'nome', 'descrizione', 'testo')
\ No newline at end of file
+ fields = ('id', 'nome', 'tipologia', 'media', 'nome', 'descrizione', 'testo', 'qr_code')
\ No newline at end of file
</script>
-{% endblock %}
\ No newline at end of file
+{% endblock %}
<th>Nome</th>
<th>Descrizione</th>
<th>Media</th>
+ <th>QRcode</th>
<th></th>
</tr>
</thead>
url: "//cdn.datatables.net/plug-ins/1.10.20/i18n/Italian.json"
},
columnDefs: [
- {"targets": 5, "orderable": false, "width": "5%"},
+ {"targets": 5, "orderable": false, "width": "8%"},
+ {"targets": 6, "orderable": false, "width": "5%"},
],
"order": []
});
{% endif %}
});
+ function crea_qr_code(pk) {
+ $.ajax({
+ type: "POST",
+ data: {
+ 'pk': pk,
+ },
+ headers: {'X-CSRFToken': '{{ csrf_token }}'},
+ url: '{% url 'sistema:crea_qr_code' %}',
+ success: function(data) {
+ location.reload();
+ var url = window.location.origin;
+ window.open(url+'/'+data.qr_code, '_blank');
+ },
+ error: function(data) {
+ Swal.fire("Attenzione!", data.response , "error")
+ },
+ });
+ }
+
function DeleteMultimedia(pk) {
Swal.fire({
title: "Sei sicuro?",
{% endblock %}
</section>
-
<footer class="footer" id="footer">
<div class="copyright text-center">
<span>Copyright © <b>Dyrecta</b> - 2022</span>
<!-- VARI MODAL CHE VENGONO RICHIAMATI ALL'INTERNO DEL GESTIONALE -->
+ {% include 'modal_add_icona_percorso.html' %}
<!-- General JS Scripts -->
<script src="{% static 'assets/js/app.min.js' %}"></script>
--- /dev/null
+<div class="modal fade" id="modal-add-icona-percorso-dialog" tabindex="-1" aria-labelledby="exampleModalLabel"
+ aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title" id="exampleModalLabel">Aggiungi icona percorso</h5>
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+ </div>
+ <div class="modal-body mt-3">
+ <form method="post" action="{% url 'sistema:add_icona_percorso' %}"
+ enctype="multipart/form-data" id="icona_percorso_form">
+ {% csrf_token %}
+ <input type="hidden" id="pk_percorso_modal" form="icona_percorso_form" name="pk_percorso_icona">
+ {{ form }}
+
+ </div>
+ <div class="modal-footer bg-whitesmoke">
+ <button class="btn btn-dark" type="button" onclick="javascript:this.form.submit();">
+ <i class="fas fa-save mr-1" aria-hidden="true"></i> Salva
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
+
<div class="row">
{% for percorso in list_percorsi %}
<div class="card" style="width: 18rem;">
- <img class="card-img-top" src="{% static 'assets/img/logo.png' %}">
+ {% if percorso.icona %}
+ <img class="card-img-top" src="/media/{{ percorso.icona }}">
+ {% else %}
+ <img class="card-img-top" src="{% static 'assets/img/logo.png' %}">
+ {% endif %}
<div class="card-body">
<h5 class="card-title">{{ percorso.nome}}</h5>
<p class="card-text">{{ percorso.descrizione }}</p>
<thead>
<tr>
<th>#</th>
+ <th>Icona</th>
<th>Nome</th>
<th>Descrizione</th>
<th>Testo</th>
<th></th>
<th></th>
+ <th></th>
</tr>
</thead>
<tbody>
url: "//cdn.datatables.net/plug-ins/1.10.20/i18n/Italian.json"
},
columnDefs: [
- {"targets": 4, "orderable": false, "width": "5%"},
- {"targets": 5, "orderable": false, "width": "5%"},
+ {"targets": 5, "orderable": false, "width": "4%"},
+ {"targets": 6, "orderable": false, "width": "4%"},
+ {"targets": 7, "orderable": false, "width": "4%"},
],
"order": []
});
});
+ function showModalIcona(pk){
+ $("#modal-add-icona-percorso-dialog").modal('show');
+ $("#pk_percorso_modal").val(pk);
+ }
+
function DeletePercorso(pk){
Swal.fire({
title: "Sei sicuro?",
from sistema.datatables import LocalitaDatatables, MultimediaDatatables, TipoMultimediaDatatables, PercorsoDatatables, \
PoiDatatables, FeedbackDatatables, GestisceLocalitaDatatables
from sistema.views import Home, LocalitaListView, MultimediaListView, PuntiInteresseListView, \
- TipologiaMultimediaListView, PercorsiListView, LocalitaView, TipoMultimediaView, PoiView, PercorsoView, FeedbackView, PercorsoInfo
+ TipologiaMultimediaListView, PercorsiListView, LocalitaView, TipoMultimediaView, PoiView, PercorsoView, \
+ FeedbackView, PercorsoInfo, QRCodeView
urlpatterns = [
path('', Home.as_view(), name='home'),
path('percorso/', PercorsoView.as_view(), name='percorso'),
path('mod_percorso/<int:pk>/', PercorsoView.as_view(), name='mod_percorso'),
path('percorso_info/<int:pk>/', PercorsoInfo.as_view(), name='percorso_info'),
+ path('add_icona_percorso/', PercorsoView.add_icona, name='add_icona_percorso'),
+
## FEEDBACK ##
path('feedback/', FeedbackView.as_view(), name='feedback'),
+ path('crea_qr_code/', QRCodeView.crea_qr, name='crea_qr_code'),
]
+import hashlib
import json
+from io import BytesIO
+import qrcode
+from cryptography.fernet import Fernet
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
+from django.core.files.base import ContentFile
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views.generic import TemplateView
from rest_framework import status
-from sistema.forms import LocalitaForm, TipoMultimediaForm, PoiForm, PercorsoForm, MultimediaForm
+from sistema.forms import LocalitaForm, TipoMultimediaForm, PoiForm, PercorsoForm, MultimediaForm, IconaPercorsoForm
from sistema.models import Localita, TipologiaMultimedia, PointOfInterest, Percorso, Tappa, TappaSerializer, Multimedia, \
Feedback, FeedbackSerializer
from socoin_atlas import settings
list_percorsi = Percorso.objects.filter(is_active=True)
return render(request, 'percorsi_cliente_home.html', {'list_percorsi': list_percorsi})
else:
- return render(request, 'percorsi_list.html', {})
+ form = IconaPercorsoForm()
+ return render(request, 'percorsi_list.html', {'form': form})
class Home(CustomLoginRequiredMixin, View): # CustomLoginRequiredMixin
if form.is_valid():
form_save = form.save(commit=False)
form_save.is_danger = is_danger
+
if files:
for f in files:
form_save.icona = f
+ form_save.save()
+ form_save.url = 'http://atlas.dyrecta.com/' + str(form_save.icona)
else:
form_save.icona = os.path.join(MEDIA_ROOT, 'icon/default.png')
+ form_save.save()
+ form_save.url = 'http://atlas.dyrecta.com/' + str(form_save.icona)
form_save.save()
messages.add_message(request, messages.INFO, 'Nuova punto di interesse inserito con successo.')
if files:
for f in files:
form_save.icona = f
+ form_save.save()
+ form_save.url = 'http://atlas.dyrecta.com/' + str(form_save.icona)
else:
if not icona:
form_save.icona = os.path.join(MEDIA_ROOT, 'icon/default.png')
+ form_save.url = 'http://atlas.dyrecta.com/' + str(form_save.icona)
form_save.save()
messages.add_message(request, messages.INFO, 'Punto di interesse aggiornata con successo.')
class PercorsoView(View):#PermissionRequiredMixin
#permission_required = [settings.TOUR_OPERATOR_GROUPS, settings.ADMIN_GROUPS]
+ def add_icona(self):
+ percorso = Percorso.objects.get(pk=int(self.POST.get('pk_percorso_icona')))
+ form = IconaPercorsoForm(self.POST, self.FILES, instance=percorso)
+ if form.is_valid():
+ form_save = form.save(commit=False)
+ if os.path.isfile(os.path.join(os.path.join(MEDIA_ROOT, "icon_percorso/"), str(form_save.icona))):
+ os.remove(os.path.join(os.path.join(MEDIA_ROOT, "icon_percorso/"), str(form_save.icona)))
+ form_save.url = 'http://atlas.dyrecta.com/media/icon_percorso/' + str(form_save.icona)
+ form_save.save()
+
+ return redirect('sistema:percorsi_list')
+
def del_poi(self, request):
percorso = Percorso.objects.get(pk=int(request.GET.get('pk')))
def add_percorso(self, request):
form = PercorsoForm(data=json.loads(request.POST.get('percorso_form')))
if form.is_valid():
- form.save()
-
+ form_save = form.save(commit=False)
+ form_save.save()
partenza = request.POST.get('partenza')
arrivo = request.POST.get('arrivo')
tappe = request.POST.getlist('tappe[]')
+
if partenza and arrivo and tappe:
Tappa.insert_tappe_percorso(partenza, arrivo, tappe, form)
return JsonResponse({'response': 'Percorso inserito con successo'}, status=status.HTTP_200_OK)
def mod_percorso(self, request):
form = PercorsoForm(data=json.loads(request.POST.get('percorso_form')), instance=Percorso.objects.get(pk=int(self.request.POST.get('pk_percorso_form'))))
if form.is_valid():
- form.save()
+ form_save = form.save(commit=False)
+ form_save.save()
tappe = Tappa.objects.filter(percorso_id=int(self.request.POST.get('pk_percorso_form')))
tappe.delete()
def get(self, request, *args, **kwargs):
if request.GET.get('method') == 'del':
self.del_feedback(request)
- return JsonResponse({'response': 'Feedback eliminato con successo'}, status=status.HTTP_200_OK)
\ No newline at end of file
+ return JsonResponse({'response': 'Feedback eliminato con successo'}, status=status.HTTP_200_OK)
+
+
+
+class QRCodeView(View):
+ def crea_qr(self):
+ try:
+ id = str(self.POST.get('pk'))
+ id_criptato = hashlib.sha256(id.encode()).hexdigest()
+ base_url = 'http://atlas.dyrecta.com/api/show_qr_code/?id_criptato='
+ url_completo = f"{base_url}{id_criptato}"
+ qr = qrcode.QRCode(
+ version=1,
+ error_correction=qrcode.constants.ERROR_CORRECT_L,
+ box_size=10,
+ border=4,
+ )
+ qr.add_data(url_completo)
+ qr.make(fit=True)
+ img_bytes = BytesIO()
+ img = qr.make_image(fill_color="black", back_color="white")
+ img.save(img_bytes, format='PNG')
+ filename = 'qr_code_'+id_criptato[:10]+'.png'
+ img_bytes.seek(0)
+
+ multimedia = Multimedia.objects.get(pk = id)
+ multimedia.chiave_qr_code = id_criptato
+ multimedia.save()
+ if os.path.isfile(os.path.join(os.path.join(MEDIA_ROOT, "poi_media_qr_code/"), filename)):
+ os.remove(os.path.join(os.path.join(MEDIA_ROOT, "poi_media_qr_code/"), filename))
+ multimedia.qr_code.save(filename, ContentFile(img_bytes.getvalue()), save=True)
+ if os.path.isfile(filename):
+ os.remove(filename)
+ return JsonResponse({'response': 'OK', 'qr_code': 'media/' + str(multimedia.qr_code)}, status=status.HTTP_200_OK)
+ except Exception as e:
+ return JsonResponse({'response': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
\ No newline at end of file