from django.urls import path, include
from django.contrib.auth import views as auth_views
-from api.views import Login_v2, RegistrationAPI, ListaPercorsiAPI
+from api.views import Login_v2, RegistrationAPI, ListaPercorsiAPI, DetailPercorsoAPI, DetailPoiAPI, SendFeedbackAPI
urlpatterns = [
path('api-login/', Login_v2.as_view(), name='api_login'),
path('api-registration/', RegistrationAPI.as_view(), name='api_registration'),
path('lista_percorsi/', ListaPercorsiAPI.as_view(), name='lista_percorsi'),
+ path('detail_percorso/', DetailPercorsoAPI.as_view(), name='detail_percorso'),
+ path('detail_poi/', DetailPoiAPI.as_view(), name='detail_poi'),
+
+ path('send_feedback/', SendFeedbackAPI.as_view(), name='send_feedback'),
]
+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 rest_framework_simplejwt.views import TokenViewBase
from rest_framework_simplejwt.exceptions import AuthenticationFailed
-from sistema.models import Percorso, PercorsoSerializer
-from socoin_atlas import settings
-from utenti.mixins import AuthorizationRequiredMixin
+from sistema.models import Percorso, PercorsoSerializer, Tappa, TappaSerializer, Feedback, FeedbackSerializer, \
+ PointOfInterest, PoiSerializer, Multimedia, MultimediaSerializer
class TokenObtainLoginSerializer(serializers.Serializer):
if error:
return Response({'errors': errors, 'error': error, 'data': '', 'messages': ['KO']})
else:
- return Response({'errors': errors, 'error': error, 'data': percorsi_serialize.data, 'messages': ['OK']})
\ No newline at end of file
+ return Response({'errors': errors, 'error': error, 'data': percorsi_serialize.data, 'messages': ['OK']})
+
+
+class DetailPercorsoAPI(APIView):
+ def get(self, request):
+ errors = []
+ error = False
+ data = []
+
+ id_percorso = request.data['id_percorso']
+
+ if id_percorso:
+
+ filter = Percorso.objects.filter(id=int(id_percorso), is_active=True)
+
+ if filter:
+ percorsi_serialize = PercorsoSerializer(filter, many=True)
+
+ tappe = Tappa.objects.filter(percorso=filter.first(), is_active=True)
+ tappe_list = TappaSerializer(tappe, many=True)
+
+ feedback = Feedback.objects.filter(percorso=filter.first(), is_active=True)
+ feedback_list = FeedbackSerializer(feedback, many=True)
+
+ data.append({
+ 'percorso': percorsi_serialize.data,
+ 'tappe': tappe_list.data,
+ 'feedback': feedback_list.data
+ })
+
+ return Response({'errors': errors, 'error': error, 'data': data, 'messages': ['OK']})
+ else:
+ return Response({'errors': 'Percorso non presente', 'error': True, 'data': '', 'messages': ['']})
+
+ else:
+ return Response({'errors': 'ID percorso non inviato', 'error': True, 'data': '', 'messages': ['']})
+
+
+class DetailPoiAPI(APIView):
+ def get(self, request):
+ errors = []
+ error = False
+ data = []
+
+ id_poi = request.data['id_poi']
+
+ if id_poi:
+
+ filter = PointOfInterest.objects.filter(id=int(id_poi), is_active=True)
+
+ if filter:
+ poi_serialize = PoiSerializer(filter, many=True)
+
+ multimedia = Multimedia.objects.filter(poi=filter.first(), is_active=True)
+ multimedia_list = MultimediaSerializer(multimedia, many=True)
+
+ data.append({
+ 'poi': poi_serialize.data,
+ 'multimedia': multimedia_list.data,
+ })
+
+ return Response({'errors': errors, 'error': error, 'data': data, 'messages': ['OK']})
+ else:
+ return Response({'errors': 'Punto di interesse non presente', 'error': True, 'data': '', 'messages': ['']})
+
+ else:
+ return Response({'errors': 'ID punto di interesse non inviato', 'error': True, 'data': '', 'messages': ['']})
+
+
+class SendFeedbackAPI(APIView):
+ def post(self, request):
+ errors = []
+ error = False
+ data = []
+
+ id_utente = request.data['pk_utente']
+ id_percorso = request.data['percorso']
+ valutazione = request.data['valutazione']
+ commento = request.data['commento']
+
+ if id_utente and id_percorso and valutazione:
+
+ utente = User.objects.get(pk=int(id_utente))
+ if utente:
+
+ percorso = Percorso.objects.get(pk=int(id_percorso))
+ if percorso:
+
+ try:
+ Feedback.objects.create(
+ utente=utente,
+ percorso=percorso,
+ valutazione=int(valutazione),
+ commento=commento
+ )
+ return Response({'errors': errors, 'error': error, 'data': data, 'messages': ['OK']})
+
+ except Exception as e:
+ return Response(
+ {'errors': str(e), 'error': True, 'data': '', 'messages': ['']})
+ else:
+ return Response(
+ {'errors': 'Percorso non trovato a sistema', 'error': True, 'data': '', 'messages': ['KO']})
+ else:
+ return Response({'errors': 'Utente non trovato a sistema', 'error': True, 'data': '', 'messages': ['KO']})
+ else:
+ return Response(
+ {'errors': 'Assicurati che le informazioni relative all\'utente, percorso e valutazione siano state inviate', 'error': True, 'data': '', 'messages': ['KO']})
if column == 'mod':
return '<a class="btn btn-sm btn-primary" href="/mod_poi/%s/" ><i class="fas fa-edit"></i></a>' % row.pk
if column == 'del':
- return '<a class="btn btn-sm btn-danger" href="DeletePoi(%s)" ><i class="fas fa-trash"></i></a>' % row.pk
+ return '<button class="btn btn-sm btn-danger" onclick="DeletePoi(%s)" ><i class="fas fa-trash"></i></button>' % row.pk
else:
return super(PoiDatatables, self).render_column(row, column)
class FeedbackDatatables(BaseDatatableView):
model = Feedback
- columns = ['id', 'utente', 'valutazione', 'commento', 'mod', 'del']
- order_columns = ['id', 'utente', 'valutazione', 'commento', 'mod', 'del']
+ columns = ['id', 'utente', 'valutazione', 'commento', 'del']
+ order_columns = ['id', 'utente', 'valutazione', 'commento', 'del']
def get_initial_queryset(self):
return self.model.objects.filter(is_active=True)
if column == 'utente':
return escape('{0}'.format(row.utente.username))
if column == 'valutazione':
- return escape('{0}'.format(row.valutazione))
+ value = ''
+ for i in range(0, int(row.valutazione)):
+ value += '<i class="fa-solid fa-star"></i>'
+ return f'{value}'
if column == 'commento':
return escape('{0}'.format(row.commento))
- if column == 'mod':
- return '<a class="btn btn-sm btn-primary" href="#" ><i class="fas fa-edit"></i></a>' #% row.pk
if column == 'del':
- return '<a class="btn btn-sm btn-danger" href="#" ><i class="fas fa-trash"></i></a>' #% row.pk
+ return '<button class="btn btn-sm btn-danger" onclick="DeactivateFeedback(%s)" ><i class="fas fa-trash"></i></button>' % row.pk
else:
return super(FeedbackDatatables, self).render_column(row, column)
is_active = models.BooleanField(default=True)
-class PercorsoSerializer(serializers.ModelSerializer):
- class Meta:
- model = Percorso
- fields = ('id', 'nome', 'descrizione', 'testo')
-
-
class Tappa(models.Model):
percorso = models.ForeignKey(Percorso, on_delete=models.DO_NOTHING)
poi = models.ForeignKey(PointOfInterest, on_delete=models.DO_NOTHING)
valutazione = models.IntegerField()
commento = models.TextField(max_length=255, null=True, blank=True)
is_active = models.BooleanField(default=True)
+
+
+class PercorsoSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Percorso
+ fields = ('id', 'nome', 'descrizione', 'testo')
+
+
+class FeedbackSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Feedback
+ fields = ('id', 'utente', 'percorso', 'valutazione', 'commento')
+
+
+class PoiSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = PointOfInterest
+ fields = ('id', 'nome', 'lat', 'long', )
+
+
+class MultimediaSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Multimedia
+ fields = ('id', 'nome', 'tipologia__nome', 'media', 'nome', 'descrizione', 'testo')
\ No newline at end of file
</div>
</div>
</div>
- <div class="card-footer text-right">
- <button class="btn-sm btn-success" type="button" onclick="saveItinerary()">Salva</button>
+ <div class="card-footer text-right">
+ <button class="btn-sm btn-success" type="button" onclick="saveItinerary()">Salva</button>
+ </div>
+ </div>
+
+ <div class="card">
+ <div class="card-header">
+ <h4 class="clr-config">Feedback degli utenti</h4>
+ </div>
+ <div class="card-body">
+ <div class="row">
+ <div class="table-responsive">
+ <table id="feedback-table"
+ class="table table-sm table-bordered table-striped dataTables_wrapper dt-bootstrap4 no-footer" style="width: 100% !important;">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Utente</th>
+ <th>Valutazione</th>
+ <th>Commento</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ </tbody>
+ </table>
+ </div>
</div>
+ </div>
</div>
+
</div>
</div>
<script>
+
var list_tappe = [];
var selected_items = []
$(document).ready(function () {
+ $('#feedback-table').dataTable({
+ processing: true,
+ serverSide: true,
+ ajax: {
+ "url": "{% url 'sistema:feedback_datatables' %}",
+ "type": 'GET',
+ },
+ language: {
+ url: "//cdn.datatables.net/plug-ins/1.10.20/i18n/Italian.json"
+ },
+ columnDefs: [
+ {"targets": 4, "orderable": false, "width": "5%"},
+ ],
+ "order": []
+ });
+
updateSelect();
{% if associated_tappe %}
});
+ function DeactivateFeedback(pk){
+ Swal.fire({
+ title: "Sei sicuro?",
+ text: "Una volta eliminata non sarà più possibile accedere all'informazione!",
+ icon: "warning",
+ showCancelButton: true,
+ confirmButtonColor: '#3085d6',
+ cancelButtonColor: '#d33',
+ confirmButtonText: 'Conferma',
+ cancelButtonText: 'Annulla',
+ }).then((willDelete) => {
+ if (willDelete.value) {
+ $.ajax({
+ type: 'GET',
+ data: {
+ 'pk': pk,
+ 'method': 'del',
+ },
+ url: '{% url 'sistema:feedback' %}',
+ success: function (response) {
+ Swal.fire("Fatto!", response.response, "success")
+ .then((value) => {
+ location.reload();
+ });
+ },
+ error: () => {
+ Swal.fire("Attenzione!", response.response, "error")
+ }
+ })
+ } else if (
+ willDelete.dismiss === Swal.DismissReason.cancel
+ ) {
+ Swal.fire(
+ 'Annullato',
+ 'Rimozione annullata.',
+ 'error'
+ )
+ }
+ });
+ }
+
function getListSelected(){
selected_items = []
$('#sortlist').children('li').each(function () {
<i class="fa-solid fa-house"></i><span>Home</span></a>
</li>
- <li class="menu-header">Sistema</li>
-
- <li class="dropdown">
- <a href="#" class="menu-toggle nav-link has-dropdown color-clients">
- <i class="fa-solid fa-users"></i><span>Utenti</span></a>
-
- <ul class="dropdown-menu" id="menu-drop">
- <li><a class="nav-link" href="{% url 'utenti:admin_list' %}">Amministratori</a></li>
- <li><a class="nav-link" href="{% url 'utenti:touroperator_list' %}">Tour operator</a></li>
- <li><a class="nav-link" href="{% url 'utenti:clienti_list' %}">Clienti</a></li>
- </ul>
-
- </li>
-
- <li class="dropdown voce_menu" id="dipendenti-menu">
- <a href="{% url 'sistema:localita_list' %}" class="nav-link color-dipendenti">
- <i class="fa-solid fa-tree-city"></i><span>Località</span></a>
- </li>
-
- <li class="dropdown">
- <a href="#" class="menu-toggle nav-link has-dropdown color-config">
- <i class="fa-solid fa-location-dot"></i><span>Punti di interesse</span></a>
-
- <ul class="dropdown-menu" id="menu-drop">
- <li><a class="nav-link" href="{% url 'sistema:poi_list' %}">Lista punti di interesse</a></li>
- <li><a class="nav-link" href="{% url 'sistema:tipo_multimedia_list' %}">Tipologia multimedia</a></li>
- </ul>
- </li>
+ {% if 'CLIENTE' not in request.session.roles %}
+ <li class="menu-header">Sistema</li>
+
+ <li class="dropdown">
+ <a href="#" class="menu-toggle nav-link has-dropdown color-clients">
+ <i class="fa-solid fa-users"></i><span>Utenti</span></a>
+
+ <ul class="dropdown-menu" id="menu-drop">
+ {% if 'TOUR_OPERATOR' not in request.session.roles %}
+ <li><a class="nav-link" href="{% url 'utenti:admin_list' %}">Amministratori</a></li>
+ {% endif %}
+ <li><a class="nav-link" href="{% url 'utenti:touroperator_list' %}">Tour operator</a></li>
+ <li><a class="nav-link" href="{% url 'utenti:clienti_list' %}">Clienti</a></li>
+ </ul>
+
+ </li>
+
+ <li class="dropdown voce_menu" id="dipendenti-menu">
+ <a href="{% url 'sistema:localita_list' %}" class="nav-link color-dipendenti">
+ <i class="fa-solid fa-tree-city"></i><span>Località</span></a>
+ </li>
+
+ <li class="dropdown">
+ <a href="#" class="menu-toggle nav-link has-dropdown color-config">
+ <i class="fa-solid fa-location-dot"></i><span>Punti di interesse</span>
+ </a>
+
+ <ul class="dropdown-menu" id="menu-drop">
+ <li><a class="nav-link" href="{% url 'sistema:poi_list' %}">Lista punti di interesse</a></li>
+ <li><a class="nav-link" href="{% url 'sistema:tipo_multimedia_list' %}">Tipologia multimedia</a></li>
+ </ul>
+ </li>
+ {% endif %}
<li class="dropdown voce_menu" id="poi-menu">
<a href="{% url 'sistema:percorsi_list' %}" class="nav-link color-listini">
<i class="fa-solid fa-route"></i><span>Percorsi</span></a>
</li>
+
</ul>
</aside>
</div>
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
+ TipologiaMultimediaListView, PercorsiListView, LocalitaView, TipoMultimediaView, PoiView, PercorsoView, FeedbackView
urlpatterns = [
path('', Home.as_view(), name='home'),
## PERCORSO ##
path('percorso/', PercorsoView.as_view(), name='percorso'),
path('mod_percorso/<int:pk>/', PercorsoView.as_view(), name='mod_percorso'),
+
+ ## FEEDBACK ##
+ path('feedback/', FeedbackView.as_view(), name='feedback'),
]
from rest_framework import status
from sistema.forms import LocalitaForm, TipoMultimediaForm, PoiForm, PercorsoForm, MultimediaForm
-from sistema.models import Localita, TipologiaMultimedia, PointOfInterest, Percorso, Tappa, TappaSerializer, Multimedia
+from sistema.models import Localita, TipologiaMultimedia, PointOfInterest, Percorso, Tappa, TappaSerializer, Multimedia, \
+ Feedback, FeedbackSerializer
from utenti.mixins import CustomLoginRequiredMixin
elif 'pk' in kwargs:
return render(request, 'add_mod_percorso.html', {'poi_list': PointOfInterest.objects.filter(is_active=True).values('id', 'nome'),
'form': PercorsoForm(instance=Percorso.objects.get(pk=int(self.kwargs['pk']))),
- 'associated_tappe': Tappa.objects.filter(percorso_id=int(self.kwargs['pk'])).values('poi__id', 'poi__nome', 'is_partenza', 'is_arrivo', 'is_tappa')})
+ 'associated_tappe': Tappa.objects.filter(percorso_id=int(self.kwargs['pk'])).values('poi__id', 'poi__nome', 'is_partenza', 'is_arrivo', 'is_tappa'),
+ 'feedback': Feedback.objects.filter(percorso_id=int(self.kwargs['pk']), is_active=True)})
else:
return render(request, 'add_mod_percorso.html', {'poi_list': PointOfInterest.objects.filter(is_active=True).values('id', 'nome'),
'form': PercorsoForm()})
self.add_percorso(request)
return redirect(reverse('sistema:percorsi_list'))
+
+
+
+class FeedbackView(View):#PermissionRequiredMixin
+ #permission_required = [settings.TOUR_OPERATOR_GROUPS, settings.ADMIN_GROUPS]
+
+ def del_feedback(self, request):
+ feedback = Feedback.objects.get(pk=int(request.GET.get('pk')))
+ feedback.is_active = False
+ feedback.save()
+
+ 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