Просмотр исходного кода

Unreplied reviews are shown in dashboard and review post method are handled

Mohidul Islam 5 лет назад
Родитель
Сommit
b127a6e1d0

+ 9 - 3
dashboard/templates/_review.html

@@ -2,11 +2,17 @@
     <img class="rounded-circle article-img" src="{{ review.reviewer_photo }}">
     <div class="media-body">
         <div class="article-metadata">
-            <span class="mr-2">{{ review.reviewer_name }}</span>
+            <span class="mr-2"><b>{{ review.reviewer_name }}</b></span>
             <small class="text-muted">{{ review.update_time|date:"F d, Y" }}</small>
             <span style="color: #a41515; float: right">{% for star in "x"|ljust:review.star_rating %} &#9733; {% endfor %}</span>
         </div>
-        <p class="article-content">{{ review.comment }}</p>
-        <p class="article-content">Reply: {{ review.reply.replied_text }}</p>
+        {% if review.comment %}
+            <p class="article-content">{{ review.comment }}</p>
+        {% endif %}
+        {% if review.reply.replied_text %}
+            <p class="article-content alert alert-success">Reply: {{ review.reply.replied_text }}</p>
+        {% else %}
+         <p class="article-content alert alert-warning">Not Replied Yet!</p>
+        {% endif %}
     </div>
 </article>

+ 16 - 7
dashboard/templates/base.html

@@ -16,38 +16,47 @@
     <header class="site-header">
       <nav class="navbar navbar-expand-md navbar-dark bg-steel fixed-top">
         <div class="container">
-          <a class="navbar-brand mr-4" href="#">ER-CARE Review</a>
+          <a class="navbar-brand mr-4" href="{% url 'un-replied-review' %}">ER-CARE Review</a>
           <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarToggle" aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
           <span class="navbar-toggler-icon"></span>
           </button>
           <div class="collapse navbar-collapse" id="navbarToggle">
             <div class="navbar-nav mr-auto">
-              <a class="nav-item nav-link" href="#">Home</a>
-              <a class="nav-item nav-link" href="#">About</a>
+              <a class="nav-item nav-link" href="{% url 'un-replied-review' %}">Home</a>
+              <a class="nav-item nav-link" href="{% url 'review-list' %}">All Reviews</a>
             </div>
             <!-- Navbar Right Side -->
             <div class="navbar-nav">
-                <a class="nav-item nav-link" href="#">New Post</a>
-                <a class="nav-item nav-link" href="#">Login</a>
+                <a class="nav-item nav-link" href="#">Analytics</a>
+                <a class="nav-item nav-link" href="#">App Report</a>
             </div>
           </div>
         </div>
       </nav>
+        {% if messages %}
+        {% for message in messages %}
+              <div class="alert alert-{{ message.tags }}">
+                {{ message }}
+              </div>
+        {% endfor %}
+        {% endif %}
     </header>
-    <main role="main" class="container">
+    <main role="main" class="container mb-5">
       <div class="row">
         <div class="col-md-9">
           {% block content %}{% endblock %}
         </div>
         <div class="col-md-3">
+        {% if locations %}
           <div class="content-section">
             <h3>ER Locations</h3>
               <ul class="list-group">
                   {% for loc in locations %}
-                    <li class="list-group-item list-group-item-light"><a href="{% url 'review-list' loc.location_id %}">{{ loc.care_name }}</a></li>
+                    <li class="list-group-item list-group-item-light"><a href="{% url 'review-list-by-location' loc.location_id %}">{{ loc.care_name }}</a></li>
                   {% endfor %}
               </ul>
           </div>
+        {% endif %}
         </div>
       </div>
     </main>

+ 33 - 0
dashboard/templates/dashboard.html

@@ -0,0 +1,33 @@
+{% extends 'base.html' %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+{% for review in reviews %}
+
+<article class="media content-section">
+    <img class="rounded-circle article-img" src="{{ review.reviewer_photo }}">
+    <div class="media-body">
+        <div class="article-metadata">
+            <span class="mr-2"><b>{{ review.reviewer_name }}</b></span>
+            <small class="text-muted">{{ review.update_time|date:"F d, Y" }}</small>
+            <small class="text-muted"> at <b>{{ review.location.care_name }}</b></small>
+            <span style="color: #a41515; float: right">{% for star in "x"|ljust:review.star_rating %} &#9733; {% endfor %}</span>
+        </div>
+        {% if review.comment %}
+            <p class="article-content">{{ review.comment }}</p>
+        {% endif %}
+        <form method="post" class="form">
+            {% csrf_token %}
+                {{ form|crispy }}
+            <input type="hidden" value="{{ review.review_id }}" name="review_id">
+            <input class="btn btn-primary ml-5" style="float: right" type="submit", value="Submit"/>
+        </form>
+<!--        <div class="row">-->
+<!--            <p class="mr-4">kid: <span class="ml-2" style="color: #0000FF;">{% for star in "x"|ljust:6 %} &#9899; {% endfor %}</span></p>-->
+<!--            <p class="mr-4">staff: <span class="ml-2" style="color: #0000FF;">{% for star in "x"|ljust:3 %} &#9899; {% endfor %}</span></p>-->
+<!--            <p class="mr-4">facility: <span class="ml-2" style="color: #0000FF;">{% for star in "x"|ljust:2 %} &#9899; {% endfor %}</span></p>-->
+<!--        </div>-->
+        </div>
+</article>
+{% endfor %}
+{% endblock %}

+ 1 - 1
dashboard/templates/review_list.html

@@ -4,7 +4,7 @@
         {% include '_review.html' %}
     {% endfor %}
 
-    <div class="mb-5" align="center">
+    <div align="center">
     {% if reviews.has_other_pages %}
 
       {% if reviews.has_previous %}

+ 5 - 3
dashboard/urls.py

@@ -1,6 +1,8 @@
 from django.urls import path
-from .views import ReviewListView
+from .views import ReviewListByLocationView, UnRepliedReviewList, ReviewListView
 
 urlpatterns = [
-    path('list/<location_id>/',ReviewListView.as_view(), name= 'review-list'),
-]
+    path('', UnRepliedReviewList.as_view(), name='un-replied-review'),
+    path('list/', ReviewListView.as_view(), name='review-list'),
+    path('list/<location_id>/', ReviewListByLocationView.as_view(), name='review-list-by-location'),
+]

+ 49 - 3
dashboard/views.py

@@ -1,11 +1,14 @@
-from django.shortcuts import render
-from django.views.generic.list import View
+from django.utils import timezone
+from django.shortcuts import render, redirect
+from django.contrib import messages
+from django.views.generic import View
 from review.models import Review, Reply
+from review.forms import ReplyForm
 from gauth.models import Location
 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 
 
-class ReviewListView(View):
+class ReviewListByLocationView(View):
 
     def get(self, request, location_id, *args, **kwargs):
         reviews = Review.objects.filter(location_id=location_id).order_by('-update_time')
@@ -21,3 +24,46 @@ class ReviewListView(View):
         context = {'reviews': reviews, 'locations': locations}
 
         return render(request, 'review_list.html', context)
+
+
+class ReviewListView(View):
+
+    def get(self, request, *args, **kwargs):
+        reviews = Review.objects.all().order_by('-update_time')
+        locations = Location.objects.all()
+        page = request.GET.get('page', 1)
+        paginator = Paginator(reviews, 50)
+        try:
+            reviews = paginator.page(page)
+        except PageNotAnInteger:
+            reviews = paginator.page(1)
+        except EmptyPage:
+            reviews = paginator.page(paginator.num_pages)
+        context = {'reviews': reviews, 'locations': locations}
+
+        return render(request, 'review_list.html', context)
+
+
+class UnRepliedReviewList(View):
+
+    def post(self, request, *args, **kwargs):
+        form = ReplyForm(self.request.POST)
+        if form.is_valid():
+            reply = form.cleaned_data.get('reply')
+        review_id = self.request.POST['review_id']
+        review = Review.objects.filter(pk=review_id).first()
+        now = timezone.now()
+        if review:
+            review_reply = Reply(replied_text=reply, create_time=now)
+            review_reply.save()
+            review.reply = review_reply
+            review.save()
+            messages.success(request, f'Your reply has been posted!')
+        return redirect('un-replied-review')
+
+    def get(self, request, *args, **kwargs):
+        now = timezone.now()
+        form = ReplyForm()
+        date = now - timezone.timedelta(days=30)
+        reviews = Review.objects.filter(reply=None, update_time__gte=date).order_by('-update_time')
+        return render(request, 'dashboard.html', {'reviews': reviews, 'form': form})

+ 3 - 1
gauth/admin.py

@@ -1,5 +1,6 @@
 from django.contrib import admin
 from .models import UserModel, Location
+from django.contrib.auth.models import Group
 
 
 class UserModelAdmin(admin.ModelAdmin):
@@ -7,9 +8,10 @@ class UserModelAdmin(admin.ModelAdmin):
 
 
 class LocationsAdmin(admin.ModelAdmin):
-    list_display = ['location_id','care_name', 'location_name', 'website_url', 'display_name']
+    list_display = ['location_id', 'care_name', 'location_name', 'website_url', 'display_name']
 
 
 admin.site.register(Location, LocationsAdmin)
 
 admin.site.register(UserModel, UserModelAdmin)
+admin.site.unregister(Group)

+ 0 - 3
gauth/location_utils.py

@@ -40,6 +40,3 @@ def populate_locations():
         loc_display = loc['primaryCategory']['displayName']
         location = Location.objects.create(location_id=loc_id, location_name=loc_name, website_url=loc_website, display_name=loc_display, owner=user)
         location.save()
-
-
-        # loc_display = loc_website.spilt('/')[-1]

+ 1 - 1
gauth/models.py

@@ -20,5 +20,5 @@ class Location(models.Model):
     owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
 
     def __str__(self):
-        return self.display_name
+        return self.location_name
 

+ 2 - 0
requirements.txt

@@ -3,9 +3,11 @@ cachetools==3.1.1
 certifi==2019.11.28
 chardet==3.0.4
 Django==3.0
+django-crispy-forms==1.8.1
 google-auth==1.8.2
 google-auth-oauthlib==0.4.1
 idna==2.8
+mysqlclient==1.4.6
 oauthlib==3.1.0
 pyasn1==0.4.8
 pyasn1-modules==0.2.7

+ 16 - 2
review/admin.py

@@ -1,10 +1,24 @@
 from django.contrib import admin
-from .models import Review, Reply
+from .models import Review, CustomReply, Reply
 
 
 class ReviewAdmin(admin.ModelAdmin):
     list_display = ('reviewer_name', 'star_rating', 'comment')
+    list_filter = ('location', 'star_rating',)
+    ordering = ['-update_time']
+    search_fields = ['star_rating', 'reviewer_name', 'comment', 'review_id']
+
+
+class CustomReplyAdmin(admin.ModelAdmin):
+    list_display = ('reply_category', 'reply')
+    list_filter = ('reply_category',)
+
+
+class ReplyAdmin(admin.ModelAdmin):
+    list_display = ('replied_text',)
+    ordering = ['-create_time']
 
 
 admin.site.register(Review, ReviewAdmin)
-admin.site.register(Reply)
+admin.site.register(CustomReply, CustomReplyAdmin)
+admin.site.register(Reply, ReplyAdmin)

+ 5 - 0
review/forms.py

@@ -0,0 +1,5 @@
+from django import forms
+
+
+class ReplyForm(forms.Form):
+    reply = forms.CharField(max_length=2000, widget=forms.Textarea(attrs={'rows': 3}))

+ 21 - 0
review/migrations/0007_customreply.py

@@ -0,0 +1,21 @@
+# Generated by Django 3.0 on 2020-01-05 11:13
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0006_auto_20200101_0847'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='CustomReply',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('reply', models.TextField()),
+                ('reply_category', models.CharField(max_length=120)),
+            ],
+        ),
+    ]

+ 19 - 0
review/migrations/0008_auto_20200106_0757.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.0 on 2020-01-06 07:57
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('review', '0007_customreply'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='review',
+            name='reply',
+            field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='review.Reply'),
+        ),
+    ]

+ 9 - 1
review/models.py

@@ -20,7 +20,15 @@ class Review(models.Model):
     reviewer_name = models.CharField(max_length=100)
     reviewer_photo = models.URLField()
     location = models.ForeignKey(Location, on_delete=models.DO_NOTHING)
-    reply = models.OneToOneField(Reply, on_delete=models.CASCADE, null=True)
+    reply = models.OneToOneField(Reply, on_delete=models.CASCADE, null=True, blank=True)
 
     def __str__(self):
         return f'{self.reviewer_name} - {self.comment}'
+
+
+class CustomReply(models.Model):
+    reply = models.TextField()
+    reply_category = models.CharField(max_length=120)
+
+    def __str__(self):
+        return f'{self.reply_category} - {self.reply}'

+ 8 - 0
review/review_utils.py

@@ -6,6 +6,8 @@ from django.db.models import Max
 from .models import Review, Reply
 from gauth.models import Location
 
+from django.utils import timezone
+
 
 ACCESS_TOKEN_URI = settings.HOST_URI + '/token'
 
@@ -102,6 +104,12 @@ def fetch_all_review(loc_id):
 
 
 def populate_reviews():
+    start = timezone.now()
     locations = get_all_location_ids()
     for loc_id in locations:
         fetch_all_review(loc_id)
+    end = timezone.now()
+    elapsed = end - start
+    print('-----------------------------------------------------------------------------------------')
+    print(f'Elapsed time: {elapsed.seconds//60} minutes and {elapsed.seconds % 60} secs.')
+    print('-----------------------------------------------------------------------------------------')

+ 3 - 0
review_automation/settings.py

@@ -26,6 +26,8 @@ INSTALLED_APPS = [
     'gauth.apps.GauthConfig',
     'review.apps.ReviewConfig',
     'dashboard.apps.DashboardConfig',
+
+    'crispy_forms',
 ]
 
 MIDDLEWARE = [
@@ -112,6 +114,7 @@ STATIC_URL = '/static/'
 
 LOGIN_URL = '/admin/login'
 LOGIN_REDIRECT_URL = '/'
+CRISPY_TEMPLATE_PACK = 'bootstrap4'
 
 
 # Google credentials