diff --git a/jobs/admin.py b/jobs/admin.py
index 862418b..e84b391 100644
--- a/jobs/admin.py
+++ b/jobs/admin.py
@@ -1,5 +1,5 @@
from django.contrib import admin
-from .models import Job, Application, Tag, Question, Answer
+from .models import Job, Application, Tag, Question, Answer, JobMessage
-admin.site.register([Job, Application, Tag, Question, Answer])
+admin.site.register([Job, Application, Tag, Question, Answer, JobMessage])
diff --git a/jobs/forms.py b/jobs/forms.py
index 66bb207..c94d06e 100644
--- a/jobs/forms.py
+++ b/jobs/forms.py
@@ -2,7 +2,7 @@ from django import forms
from django.forms import inlineformset_factory
from dal.autocomplete import ModelSelect2Multiple
-from .models import Job, Question, Application, Answer
+from .models import Job, Question, Application, Answer, JobMessage
class JobForm(forms.ModelForm):
@@ -13,6 +13,12 @@ class JobForm(forms.ModelForm):
'tags': ModelSelect2Multiple(url='jobs:tag-autocomplete')
}
+class MessageForm(forms.ModelForm):
+ text = forms.CharField(label='', widget=forms.TextInput(attrs={"placeholder": "Write your message"}))
+ class Meta:
+ model = JobMessage
+ fields = ('text',)
+
class QuestionForm(forms.ModelForm):
class Meta:
diff --git a/jobs/migrations/0004_jobmessage.py b/jobs/migrations/0004_jobmessage.py
new file mode 100644
index 0000000..9b8f655
--- /dev/null
+++ b/jobs/migrations/0004_jobmessage.py
@@ -0,0 +1,26 @@
+# Generated by Django 2.1.2 on 2019-03-16 03:23
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('jobs', '0003_auto_20181020_0622'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='JobMessage',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('text', models.TextField()),
+ ('date', models.DateTimeField(auto_now_add=True)),
+ ('receiver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='receiver', to=settings.AUTH_USER_MODEL)),
+ ('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sender', to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/jobs/migrations/0005_job_active.py b/jobs/migrations/0005_job_active.py
new file mode 100644
index 0000000..bb7c165
--- /dev/null
+++ b/jobs/migrations/0005_job_active.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1.2 on 2019-03-17 02:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('jobs', '0004_jobmessage'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='job',
+ name='active',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/jobs/models.py b/jobs/models.py
index 7c16664..2e2efeb 100644
--- a/jobs/models.py
+++ b/jobs/models.py
@@ -42,6 +42,8 @@ class Job(models.Model):
posted_by = models.ForeignKey(
User, related_name="jobs", on_delete=models.CASCADE)
+ active = models.BooleanField(default=True)
+
def renew(self):
self.expires = after_30_days()
self.save()
@@ -96,3 +98,13 @@ class Answer(models.Model):
def __str__(self):
return "Answer for : {0} - {1}".format(self.question, self.application)
+
+class JobMessage(models.Model):
+ """Basic user to user messaging app"""
+ sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
+ receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name="receiver")
+ text = models.TextField()
+ date = models.DateTimeField(auto_now_add=True)
+
+ def __str__(self):
+ return '{}, {}'.format(self.sender, self.text[:50])
diff --git a/jobs/templates/base.html b/jobs/templates/base.html
index 94d2aa2..7f3a117 100644
--- a/jobs/templates/base.html
+++ b/jobs/templates/base.html
@@ -13,19 +13,43 @@
{% block title %}{% endblock %}
-
+
-

-
-
Post a job
- {% if request.user.is_authenticated %}
-
Logout
- {% endif %}
-
{% for message in messages %}
@@ -34,23 +58,43 @@
{{ message }}
{% endfor %}
- {% block body_content %}{% endblock %}
+
+ {% block body_content %}
+
+
+
+ {% endblock %}
+
+
diff --git a/jobs/templates/jobs/application_list.html b/jobs/templates/jobs/application_list.html
index 0a8c89c..e69de29 100644
--- a/jobs/templates/jobs/application_list.html
+++ b/jobs/templates/jobs/application_list.html
@@ -1,31 +0,0 @@
-{% extends 'base.html' %}
-{% block body_content %}
-
-
-
- {% for application in applications %}
-
-
-
-
- Submitted: {{ application.created }}
-
-
- {{ application.cover_text }}
-
-
- {% for answer in application.answers.all %}
- - {{answer.question}}
{{answer.text}}
- {% endfor %}
-
-
-
- {% endfor %}
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/jobs/templates/jobs/conversation.html b/jobs/templates/jobs/conversation.html
new file mode 100644
index 0000000..e1adeb3
--- /dev/null
+++ b/jobs/templates/jobs/conversation.html
@@ -0,0 +1,36 @@
+{% extends 'base.html' %}
+{% block body_content %}
+{% load crispy_forms_tags %}
+
+
+
+
+
+{% endblock %}
diff --git a/jobs/templates/jobs/index.html b/jobs/templates/jobs/index.html
index d1dda09..9695386 100644
--- a/jobs/templates/jobs/index.html
+++ b/jobs/templates/jobs/index.html
@@ -11,12 +11,12 @@
{% for job in jobs %}
-
+
-
+
-
{{job.description|truncatewords:20}}
+
{{job.description|truncatechars:120}}
Apply
diff --git a/jobs/templates/jobs/job.html b/jobs/templates/jobs/job.html
new file mode 100644
index 0000000..528a404
--- /dev/null
+++ b/jobs/templates/jobs/job.html
@@ -0,0 +1,42 @@
+{% extends 'base.html' %}
+{% block body_content %}
+
+
+
+
+
+
+ Job Title |
+ Date |
+ Action |
+
+
+
+ {% for job in jobs %}
+
+ {{ job.title|truncatechars:65 }} |
+ {{ job.created.date }} |
+
+
+ |
+
+ {% endfor %}
+
+
+
+{% endblock %}
+
diff --git a/jobs/templates/jobs/job_form.html b/jobs/templates/jobs/job_form.html
index 63aa129..f152232 100644
--- a/jobs/templates/jobs/job_form.html
+++ b/jobs/templates/jobs/job_form.html
@@ -8,12 +8,12 @@
diff --git a/jobs/templates/jobs/jobmessage_list.html b/jobs/templates/jobs/jobmessage_list.html
new file mode 100644
index 0000000..cbf384b
--- /dev/null
+++ b/jobs/templates/jobs/jobmessage_list.html
@@ -0,0 +1,30 @@
+{% extends 'base.html' %}
+{% block body_content %}
+
+
+
+{% endblock %}
diff --git a/jobs/templates/jobs/list_applications_others.html b/jobs/templates/jobs/list_applications_others.html
new file mode 100644
index 0000000..e8ef25e
--- /dev/null
+++ b/jobs/templates/jobs/list_applications_others.html
@@ -0,0 +1,57 @@
+{% extends 'base.html' %}
+{% load crispy_forms_tags %}
+{% block body_content %}
+
+
+
+ {% for application in applications %}
+
+
+
+
+ Submitted: {{ application.created }}
+
+
+ {{ application.cover_text }}
+
+
+ {% for answer in application.answers.all %}
+ - {{answer.question}}
{{answer.text}}
+ {% endfor %}
+
+
+
+
+
+
+ {% endfor %}
+
+
+
+{% endblock %}
diff --git a/jobs/templates/jobs/list_own_applications.html b/jobs/templates/jobs/list_own_applications.html
new file mode 100644
index 0000000..3779345
--- /dev/null
+++ b/jobs/templates/jobs/list_own_applications.html
@@ -0,0 +1,31 @@
+{% extends 'base.html' %}
+{% block body_content %}
+
+
+
+ {% for application in applications %}
+
+
+
+
+ Submitted: {{ application.created }}
+
+
+ {{ application.cover_text }}
+
+
+ {% for answer in application.answers.all %}
+ - {{answer.question}}
{{answer.text}}
+ {% endfor %}
+
+
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/jobs/urls.py b/jobs/urls.py
index 526ed99..fd22039 100644
--- a/jobs/urls.py
+++ b/jobs/urls.py
@@ -7,22 +7,20 @@ app_name = 'jobs'
urlpatterns = [
path('', views.Index.as_view(), name='index'),
path('jobs/create/', views.JobCreate.as_view(), name='job_create'),
- path('jobs/', views.JobList.as_view(), name='job_list'),
- path(
- 'jobs/
/detail/', views.JobDetail.as_view(), name='job_detail'),
+ path('jobs/me/', views.MyJobs.as_view(), name='my_jobs'),
+ path('jobs//detail/', views.JobDetail.as_view(), name='job_detail'),
path('jobs//renew/', views.JobRenew.as_view(), name='job_renew'),
+ path('jobs/messages//', views.Conversation.as_view(), name='conversation'),
+ path('jobs/toggle/', views.change_status, name='job_toggle'),
path('jobs//applications/', views.ApplicationList.as_view(), name='job_applications'),
- path(
- 'jobs//apply/',
- views.ApplicationCreate.as_view(),
- name='job_apply'),
+ path('jobs/applications/me/', views.ListOwnApplications.as_view(), name='my_applications'),
+ path('jobs/applications/', views.ListJobApplications.as_view(), name='applications_others'),
+ path('jobs/messages/', views.MesssageInbox.as_view(), name='messages_inbox'),
+ path('jobs/messages/send/', views.send_message, name='send_message'),
+ path('jobs//apply/', views.ApplicationCreate.as_view(), name='job_apply'),
]
# autocomplete endpoints
urlpatterns += [
- path(
- 'tag-autocomplete/',
- autocomplete_views.TagAutocomplete.as_view(create_field='name'),
- name='tag-autocomplete',
- ),
+ path('tag-autocomplete/', autocomplete_views.TagAutocomplete.as_view(create_field='name'), name='tag-autocomplete',),
]
\ No newline at end of file
diff --git a/jobs/views.py b/jobs/views.py
index a61db16..1c58106 100644
--- a/jobs/views.py
+++ b/jobs/views.py
@@ -1,31 +1,28 @@
from django.urls import reverse_lazy
from django.http import HttpResponseRedirect
from django.contrib.auth.mixins import LoginRequiredMixin
-from django.views.generic import (View, TemplateView, ListView, CreateView,
- DetailView)
+from django.views.generic import (View, TemplateView, ListView, CreateView, DetailView)
from django.views.generic.detail import SingleObjectMixin
from django.contrib import messages
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, redirect
from rules.contrib.views import PermissionRequiredMixin
+from django.contrib.auth import get_user_model
-from .models import Job, Application, Question
-from .forms import JobForm, QuestionFormSet, ApplicationForm, AnswerForm
+from .models import Job, Application, Question, JobMessage
+from .forms import JobForm, QuestionFormSet, ApplicationForm, AnswerForm, MessageForm
+User = get_user_model()
+
class Index(TemplateView):
template_name = 'jobs/index.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
- context['jobs'] = Job.objects.all().order_by('-updated')[:6]
+ context['jobs'] = Job.objects.filter(active=True).order_by('-updated')[:6]
return context
-class JobList(ListView):
- context_object_name = 'jobs'
- model = Job
-
-
class JobDetail(DetailView):
context_object_name = 'job'
model = Job
@@ -88,7 +85,7 @@ class ApplicationCreate(LoginRequiredMixin, CreateView):
# TODO: restrict users from re-application
model = Application
form_class = ApplicationForm
- success_url = reverse_lazy("jobs:job_list")
+ success_url = reverse_lazy("jobs:index")
def get_question_queryset(self):
# filter questions for particular job and order it so same queryset
@@ -153,5 +150,113 @@ class ApplicationList(LoginRequiredMixin, PermissionRequiredMixin, ListView):
return super().get_queryset().filter(job_id=self.kwargs['job_pk'])
+class ListOwnApplications(ListView):
+ model = Application
+ context_object_name = 'applications'
+ template_name = 'jobs/list_own_applications.html'
+
+ def get_queryset(self):
+ return Application.objects.filter(applicant=self.request.user)
+
+ def get_context_data(self, **kwargs):
+ context = super(ListOwnApplications, self).get_context_data(**kwargs)
+ context['title'] = 'Your Applications'
+ return context
+
+
+class ListJobApplications(ListView):
+ model = Application
+ context_object_name = 'applications'
+ template_name = 'jobs/list_applications_others.html'
+
+ def get_queryset(self):
+ return Application.objects.filter(job__posted_by=self.request.user)
+
+ def get_context_data(self, **kwargs):
+ context = super(ListJobApplications, self).get_context_data(**kwargs)
+ context['title'] = 'Applications to your jobs'
+ context['form'] = MessageForm
+ return context
+
+
class ApplicationDetail(LoginRequiredMixin, DetailView):
model = Application
+
+
+class MesssageInbox(LoginRequiredMixin, ListView):
+ model = JobMessage
+ context_object_name = 'jobmessage'
+
+ def get_queryset(self):
+ qs_list = []
+ qs = JobMessage.objects.filter(receiver=self.request.user).order_by('-date')
+ lookup = set(qs.values_list('sender', flat=True))
+ for sender_id in lookup:
+ for elem in qs:
+ if elem.sender_id == sender_id:
+ qs_list.append(elem.id)
+ break
+ return JobMessage.objects.filter(pk__in=qs_list).order_by('-date')
+
+ def get_context_data(self, **kwargs):
+ context = super(MesssageInbox, self).get_context_data(**kwargs)
+ context['title'] = 'Messages Inbox'
+ return context
+
+
+class Conversation(LoginRequiredMixin, ListView):
+ model = JobMessage
+ context_object_name = 'jobmessage'
+ template_name = 'jobs/conversation.html'
+
+ def get_queryset(self):
+ queryset1 = JobMessage.objects.filter(
+ receiver=self.request.user, sender=self.kwargs['pk'])
+ queryset2 = JobMessage.objects.filter(
+ receiver=self.kwargs['pk'], sender=self.request.user)
+ return queryset1.union(queryset2).order_by('date')
+
+ def get_context_data(self, **kwargs):
+ context = super(Conversation, self).get_context_data(**kwargs)
+ context['form'] = MessageForm
+ context['person'] = User.objects.get(id=self.kwargs['pk'])
+ return context
+
+
+class MyJobs(LoginRequiredMixin, ListView):
+ model = Job
+ context_object_name = 'jobs'
+ template_name = 'jobs/job.html'
+
+ def get_queryset(self):
+ return Job.objects.filter(posted_by=self.request.user).order_by('expires')
+
+
+ def get_context_data(self, **kwargs):
+ context = super(MyJobs, self).get_context_data(**kwargs)
+ context['title'] = 'Your jobs'
+ return context
+
+
+def send_message(request):
+ form = MessageForm(request.POST)
+ if form.is_valid():
+ user = User.objects.get(id=request.POST.get('receiver_id'))
+ JobMessage.objects.create(sender=request.user, receiver=user, text=request.POST.get('text'))
+ else:
+ print("Error")
+ return redirect('jobs:conversation', pk=request.POST.get('receiver_id'))
+
+def change_status(request):
+ print(request.POST)
+ job_id = request.POST.get('job_id')
+ if job_id is not None:
+ job = Job.objects.get(id=job_id)
+ if job.active:
+ job.active = False
+ else:
+ job.active = True
+ job.save()
+ else:
+ print("Error")
+ return redirect('jobs:my_jobs')
diff --git a/requirements.txt b/requirements.txt
index 2f7ff63..b90504f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-Django==2.1.2
+Django==2.2.16
django-auth-ldap==1.7.0
django-autocomplete-light==3.3.2
django-crispy-forms==1.7.2
diff --git a/templates/registration/login.html b/templates/registration/login.html
index d273b60..8cd75a7 100644
--- a/templates/registration/login.html
+++ b/templates/registration/login.html
@@ -1,10 +1,18 @@
-{% extends 'base.html' %}
+{% extends 'base.html' %}
+{% load crispy_forms_tags %}
{% block title %}Login{% endblock %}
{% block body_content %}
-Login
-
-Don't have an account? Sign up here.
+
+
+
+
Login
+
+
+ Don't have an account? Sign up
here.
+
+
+
{% endblock %}
\ No newline at end of file