mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-18 13:04:05 +01:00
Add search
This commit is contained in:
parent
3a5d8dacbf
commit
5dd381c44d
@ -37,4 +37,11 @@ class SignUpForm(UserCreationForm):
|
||||
|
||||
error_messages = {
|
||||
'password_mismatch': "The two password fields didn't match.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PollSearchForm(forms.Form):
|
||||
q = forms.CharField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
<header class="flex items-center justify-center">
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<!--Searchhhh-->
|
||||
<form class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<form action="{% url "polls:search_poll" %}" method="get" class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<div class="flex w-full items-center rounded-lg bg-neutral-200 py-1 px-2 focus-within:outline-none focus-within:ring-2 focus-within:ring-purple-400 md:rounded-full">
|
||||
<div>🔎</div>
|
||||
<input type="text" name="q" class="font-semibold w-full border-none bg-transparent focus:outline-none focus:ring focus:ring-transparent text-sm">
|
||||
|
||||
110
polls/templates/polls/search.html
Normal file
110
polls/templates/polls/search.html
Normal file
@ -0,0 +1,110 @@
|
||||
{% extends "polls/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Navbar -->
|
||||
<nav class="bg-blue-500 p-4 shadow-xl border-b-2 border-solid border-neutral-700">
|
||||
<div class="container mx-auto flex items-center justify-between">
|
||||
<div class="text-2xl font-bold text-white">
|
||||
{% if user.is_authenticated %}
|
||||
<a href={% url "polls:index" %}>📝BACK TO KU POLL</a> | 👋 Hi! {{ user.username }}
|
||||
{% else %}
|
||||
<a href={% url "polls:index" %}>📝BACK TO KU POLL</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
{% comment %}
|
||||
<button class="mr-4 rounded-md bg-green-500 px-4 py-2 text-white">New Poll</button>
|
||||
{% endcomment %}
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<header class="flex items-center justify-center">
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<!--Searchhhh-->
|
||||
<form action="{% url "polls:search_poll" %}" method="get" class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<div class="flex w-full items-center rounded-lg bg-neutral-200 py-1 px-2 focus-within:outline-none focus-within:ring-2 focus-within:ring-purple-400 md:rounded-full">
|
||||
<div>🔎</div>
|
||||
<input type="text" name="q" class="font-semibold w-full border-none bg-transparent focus:outline-none focus:ring focus:ring-transparent text-sm">
|
||||
</div>
|
||||
<button type="submit">
|
||||
<span class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
Search
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
<!--End-->
|
||||
</div>
|
||||
</header>
|
||||
{% if user.is_authenticated %}
|
||||
<button class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
New Poll
|
||||
</button>
|
||||
<a href="{% url 'logout' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-red-600 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-red-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-neutral-900">
|
||||
<span>Sign out <span class="hidden sm:inline-block">😭</span></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-neutral-800 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-neutral-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-red-900">
|
||||
<span>Sign in <span class="hidden sm:inline-block">😎</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="bg-white p-4 rounded-lg shadow-md mb-4">
|
||||
{% if q %}
|
||||
{% with results.count as total_result %}
|
||||
<h2 class="mb-4 text-2xl font-bold">Found {{ total_result }} Polls!</h2>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{% for question in results %}
|
||||
<div class="relative">
|
||||
<!-- INFO -->
|
||||
<div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-neutral-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
<h2 class="mb-2 text-xl font-semibold truncate">{{ question.question_text }}</h2>
|
||||
<hr class="h-px my-2 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<p class="mb-2 text-gray-600">{{ question.short_description }}</p>
|
||||
<div class="mb-2 flex items-center text-gray-600">
|
||||
<span class="mr-2">👍</span>
|
||||
<span>{{ question.up_vote_percentage }}% Upvoted</span>
|
||||
|
||||
<span class="ml-4 mr-2">👎</span>
|
||||
<span>{{ question.down_vote_percentage }}% Downvoted</span>
|
||||
</div>
|
||||
<!-- Tag / Time -->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<span class="mr-2 rounded-md bg-green-500 px-2 py-1 text-white">🕒 {{ question.time_left }}</span>
|
||||
<span class="mr-2 rounded-md bg-orange-100 px-2 py-1 text-black">{{ question.participants }} Participants 👤</span>
|
||||
</div>
|
||||
<div class="flex items-center text-gray-600 py-4">
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:detail' question.id %}'"
|
||||
class="mr-2 rounded-md bg-white px-2 py-1 text-black border-solid border-2 border-black hover:bg-gray-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
VOTE
|
||||
</button>
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:results' question.id %}'"
|
||||
class="mr-2 rounded-md bg-white px-2 py-1 text-black border-solid border-2 border-black hover:bg-gray-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
VIEW
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if forloop.counter|divisibleby:2 %}
|
||||
<div class="absolute inset-0 mt-1 ml-1 h-full w-full rounded-lg border-2 border-neutral-700 bg-gradient-to-r from-green-400 to-blue-500">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="absolute inset-0 mt-1 ml-1 h-full w-full rounded-lg border-2 border-neutral-700 bg-gradient-to-r from-orange-400 to-red-500">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
25
polls/tests/test_search_poll.py
Normal file
25
polls/tests/test_search_poll.py
Normal file
@ -0,0 +1,25 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from ..models import Question
|
||||
from ..views import search_poll
|
||||
|
||||
|
||||
class SearchPollTest(TestCase):
|
||||
"""Test if user search with normal string. It must return same queryset as filter question objects"""
|
||||
def test_search_normal_poll(self):
|
||||
data_1 = {'q': 'what'}
|
||||
data_2 = {'q': 'prefer'}
|
||||
q_1 = 'what'
|
||||
q_2 = 'prefer'
|
||||
response_1 = self.client.get(reverse("polls:search_poll"), data_1)
|
||||
response_2 = self.client.get(reverse("polls:search_poll"), data_2)
|
||||
self.assertQuerysetEqual(response_1.context['results'], Question.objects.filter(question_text__icontains=q_1))
|
||||
self.assertQuerysetEqual(response_2.context['results'], Question.objects.filter(question_text__icontains=q_2))
|
||||
|
||||
def test_search_with_empty(self):
|
||||
"""Test if user search with empty string. It must return all question"""
|
||||
data = {'q': ''}
|
||||
response = self.client.get(reverse("polls:search_poll"), data)
|
||||
self.assertQuerysetEqual(response.context['results'], Question.objects.all())
|
||||
|
||||
@ -11,4 +11,5 @@ urlpatterns = [
|
||||
path("signup/", views.SignUpView.as_view(), name="signup"),
|
||||
path("upvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'upvote'}, name="upvote"),
|
||||
path("downvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'downvote'}, name="downvote"),
|
||||
path("search", views.search_poll, name="search_poll"),
|
||||
]
|
||||
|
||||
@ -12,7 +12,7 @@ from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
|
||||
from .forms import SignUpForm
|
||||
from .forms import SignUpForm, PollSearchForm
|
||||
from .models import Choice, Question, Vote
|
||||
|
||||
|
||||
@ -189,3 +189,17 @@ def get_client_ip(request):
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
def search_poll(request):
|
||||
form = PollSearchForm
|
||||
|
||||
results = []
|
||||
q = ''
|
||||
if 'q' in request.GET:
|
||||
form = PollSearchForm(request.GET)
|
||||
if form.is_valid():
|
||||
q = form.cleaned_data['q']
|
||||
results = Question.objects.filter(question_text__icontains=q)
|
||||
if q == '':
|
||||
results = Question.objects.all()
|
||||
return render(request, 'polls/search.html', {'form':form, 'results':results, 'q':q})
|
||||
Loading…
Reference in New Issue
Block a user