Restructure Alliance Auth package (#867)

* Refactor allianceauth into its own package

* Add setup

* Add missing default_app_config declarations

* Fix timerboard namespacing

* Remove obsolete future imports

* Remove py2 mock support

* Remove six

* Add experimental 3.7 support and multiple Dj versions

* Remove python_2_unicode_compatible

* Add navhelper as local package

* Update requirements
This commit is contained in:
Basraah
2017-09-19 09:46:40 +10:00
committed by GitHub
parent d10580b56b
commit 786859294d
538 changed files with 1197 additions and 1523 deletions

View File

@@ -0,0 +1 @@
default_app_config = 'allianceauth.hrapplications.apps.HRApplicationsConfig'

View File

@@ -0,0 +1,23 @@
from django.contrib import admin
from .models import Application, ApplicationChoice, ApplicationComment, ApplicationForm, ApplicationQuestion, \
ApplicationResponse
class ChoiceInline(admin.TabularInline):
model = ApplicationChoice
extra = 0
verbose_name_plural = 'Choices (optional)'
verbose_name= 'Choice'
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['title', 'help_text']}),
]
inlines = [ChoiceInline]
admin.site.register(Application)
admin.site.register(ApplicationComment)
admin.site.register(ApplicationQuestion, QuestionAdmin)
admin.site.register(ApplicationForm)
admin.site.register(ApplicationResponse)

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class HRApplicationsConfig(AppConfig):
name = 'allianceauth.hrapplications'
label = 'hrapplications'

View File

@@ -0,0 +1,28 @@
from allianceauth.services.hooks import MenuItemHook, UrlHook
from allianceauth import hooks
from allianceauth.hrapplications import urls
class ApplicationsMenu(MenuItemHook):
def __init__(self):
MenuItemHook.__init__(self,
'Applications',
'fa fa-file-o fa-fw grayiconecolor',
'hrapplications:index',
navactive=['hrapplications:'])
@hooks.register('menu_item_hook')
def register_menu():
return ApplicationsMenu()
class ApplicationsUrls(UrlHook):
def __init__(self):
UrlHook.__init__(self, urls, 'hrapplications', r'^hr/')
@hooks.register('url_hook')
def register_url():
return ApplicationsUrls()

View File

@@ -0,0 +1,10 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
class HRApplicationCommentForm(forms.Form):
comment = forms.CharField(widget=forms.Textarea, required=False, label=_("Comment"))
class HRApplicationSearchForm(forms.Form):
search_string = forms.CharField(max_length=254, required=True, label=_("Search String"))

View File

@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.1 on 2016-09-05 21:39
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('eveonline', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Application',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('approved', models.NullBooleanField(default=None)),
('created', models.DateTimeField(auto_now_add=True)),
],
options={
'permissions': (('approve_application', 'Can approve applications'), ('reject_application', 'Can reject applications'), ('view_apis', 'Can view applicant APIs')),
},
),
migrations.CreateModel(
name='ApplicationComment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField()),
('created', models.DateTimeField(auto_now_add=True)),
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='hrapplications.Application')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='ApplicationForm',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('corp', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCorporationInfo')),
],
),
migrations.CreateModel(
name='ApplicationQuestion',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=254)),
('help_text', models.CharField(blank=True, max_length=254, null=True)),
],
),
migrations.CreateModel(
name='ApplicationResponse',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('answer', models.TextField()),
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='hrapplications.Application')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrapplications.ApplicationQuestion')),
],
),
migrations.CreateModel(
name='HRApplication',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('character_name', models.CharField(default=b'', max_length=254)),
('full_api_id', models.CharField(default=b'', max_length=254)),
('full_api_key', models.CharField(default=b'', max_length=254)),
('is_a_spi', models.CharField(default=b'', max_length=254)),
('about', models.TextField(default=b'')),
('extra', models.TextField(default=b'')),
('approved_denied', models.NullBooleanField()),
('corp', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCorporationInfo')),
('reviewer_character', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCharacter')),
('reviewer_inprogress_character', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='inprogress_character', to='eveonline.EveCharacter')),
('reviewer_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='review_user', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='HRApplicationComment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_on', models.DateTimeField(auto_now_add=True, null=True)),
('comment', models.CharField(default=b'', max_length=254)),
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hrapplications.HRApplication')),
('commenter_character', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='eveonline.EveCharacter')),
('commenter_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='applicationform',
name='questions',
field=models.ManyToManyField(to='hrapplications.ApplicationQuestion'),
),
migrations.AddField(
model_name='application',
name='form',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to='hrapplications.ApplicationForm'),
),
migrations.AddField(
model_name='application',
name='reviewer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='application',
name='reviewer_character',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter'),
),
migrations.AddField(
model_name='application',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='applications', to=settings.AUTH_USER_MODEL),
),
migrations.AlterUniqueTogether(
name='applicationresponse',
unique_together=set([('question', 'application')]),
),
migrations.AlterUniqueTogether(
name='application',
unique_together=set([('form', 'user')]),
),
]

View File

@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-08-23 19:46
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hrapplications', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ApplicationChoice',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('choice_text', models.CharField(max_length=200, verbose_name='Choice')),
],
),
migrations.AlterField(
model_name='applicationquestion',
name='title',
field=models.CharField(max_length=254, verbose_name='Question'),
),
migrations.AddField(
model_name='applicationchoice',
name='question',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='choices', to='hrapplications.ApplicationQuestion'),
),
]

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-22 23:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hrapplications', '0002_choices_for_questions'),
]
operations = [
migrations.AlterField(
model_name='hrapplication',
name='about',
field=models.TextField(default=''),
),
migrations.AlterField(
model_name='hrapplication',
name='character_name',
field=models.CharField(default='', max_length=254),
),
migrations.AlterField(
model_name='hrapplication',
name='extra',
field=models.TextField(default=''),
),
migrations.AlterField(
model_name='hrapplication',
name='full_api_id',
field=models.CharField(default='', max_length=254),
),
migrations.AlterField(
model_name='hrapplication',
name='full_api_key',
field=models.CharField(default='', max_length=254),
),
migrations.AlterField(
model_name='hrapplication',
name='is_a_spi',
field=models.CharField(default='', max_length=254),
),
migrations.AlterField(
model_name='hrapplicationcomment',
name='comment',
field=models.CharField(default='', max_length=254),
),
]

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-27 03:29
from __future__ import unicode_literals
from django.db import migrations
import sortedm2m.fields
from sortedm2m.operations import AlterSortedManyToManyField
class Migration(migrations.Migration):
dependencies = [
('hrapplications', '0003_make_strings_more_stringy'),
]
operations = [
AlterSortedManyToManyField(
model_name='applicationform',
name='questions',
field=sortedm2m.fields.SortedManyToManyField(help_text=None, to='hrapplications.ApplicationQuestion'),
),
]

View File

@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-08 02:54
from __future__ import unicode_literals
from django.db import migrations
def delete_permissions(apps, schema_editor):
HRApplication = apps.get_model('hrapplications', 'HRApplication')
HRApplicationComment = apps.get_model('hrapplications', 'HRApplicationComment')
ContentType = apps.get_model('contenttypes', 'ContentType')
Permission = apps.get_model('auth', 'Permission')
ct1 = ContentType.objects.get_for_model(HRApplication)
ct2 = ContentType.objects.get_for_model(HRApplicationComment)
Permission.objects.filter(content_type__in=[ct1, ct2]).delete()
class Migration(migrations.Migration):
dependencies = [
('hrapplications', '0004_sorted_questions'),
]
operations = [
migrations.RemoveField(
model_name='hrapplication',
name='corp',
),
migrations.RemoveField(
model_name='hrapplication',
name='reviewer_character',
),
migrations.RemoveField(
model_name='hrapplication',
name='reviewer_inprogress_character',
),
migrations.RemoveField(
model_name='hrapplication',
name='reviewer_user',
),
migrations.RemoveField(
model_name='hrapplication',
name='user',
),
migrations.RemoveField(
model_name='hrapplicationcomment',
name='application',
),
migrations.RemoveField(
model_name='hrapplicationcomment',
name='commenter_character',
),
migrations.RemoveField(
model_name='hrapplicationcomment',
name='commenter_user',
),
migrations.RunPython(delete_permissions, migrations.RunPython.noop),
migrations.DeleteModel(
name='HRApplication',
),
migrations.DeleteModel(
name='HRApplicationComment',
),
]

View File

@@ -0,0 +1,86 @@
from django.contrib.auth.models import User
from django.db import models
from sortedm2m.fields import SortedManyToManyField
from allianceauth.eveonline.models import EveCharacter
from allianceauth.eveonline.models import EveCorporationInfo
class ApplicationQuestion(models.Model):
title = models.CharField(max_length=254, verbose_name='Question')
help_text = models.CharField(max_length=254, blank=True, null=True)
def __str__(self):
return "Question: " + self.title
class ApplicationChoice(models.Model):
question = models.ForeignKey(ApplicationQuestion,on_delete=models.CASCADE,related_name="choices")
choice_text = models.CharField(max_length=200, verbose_name='Choice')
def __str__(self):
return self.choice_text
class ApplicationForm(models.Model):
questions = SortedManyToManyField(ApplicationQuestion)
corp = models.OneToOneField(EveCorporationInfo)
def __str__(self):
return str(self.corp)
class Application(models.Model):
form = models.ForeignKey(ApplicationForm, on_delete=models.CASCADE, related_name='applications')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='applications')
approved = models.NullBooleanField(blank=True, null=True, default=None)
reviewer = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user) + " Application To " + str(self.form)
class Meta:
permissions = (
('approve_application', 'Can approve applications'), ('reject_application', 'Can reject applications'),
('view_apis', 'Can view applicant APIs'),)
unique_together = ('form', 'user')
@property
def main_character(self):
return self.user.profile.main_character
@property
def characters(self):
return [o.character for o in self.user.character_ownerships.all()]
@property
def reviewer_str(self):
if self.reviewer_character:
return str(self.reviewer_character)
elif self.reviewer:
return "User " + str(self.reviewer)
else:
return None
class ApplicationResponse(models.Model):
question = models.ForeignKey(ApplicationQuestion, on_delete=models.CASCADE)
application = models.ForeignKey(Application, on_delete=models.CASCADE, related_name='responses')
answer = models.TextField()
def __str__(self):
return str(self.application) + " Answer To " + str(self.question)
class Meta:
unique_together = ('question', 'application')
class ApplicationComment(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user) + " comment on " + str(self.application)

View File

@@ -0,0 +1,27 @@
{% extends "registered/base.html" %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Choose a Corp{% endblock %}
{% block page_title %}{% trans "Choose a Corp" %}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Choose a Corp" %}</h1>
{% if choices %}
<div class="panel panel-primary">
<div class="panel-heading">{% trans "Available Corps" %}</div>
<table class="table table-responsive">
{% for choice in choices %}
<tr>
<td class="text-center">
<a href="{% url 'hrapplications:create_view' choice.0 %}" class="btn btn-primary" title="Apply">{{ choice.1 }}</a>
</td>
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div class="alert alert-danger">{% trans "No corps are accepting applications at this time." %}</div>
{% endif %}
</div>
{% endblock content %}

View File

@@ -0,0 +1,37 @@
{% extends "registered/base.html" %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Apply To {{ corp.corporation_name }}{% endblock title %}
{% block page_title %}{% trans "Apply To" %} {{ corp.corporation_name }}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Apply To" %} {{ corp.corporation_name }}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<form class="form-signin">
{% csrf_token %}
{% for question in questions %}
<div class="form-group">
<label class="control-label" for="id_{{ question.pk }}">{{ question.title }}</label>
<div class=" ">
{% if question.help_text %}
<div cass="text-center">{{ question.help_text }}</div>
{% endif %}
{% for choice in question.choices.all %}
<input type="radio" name="{{ question.pk }}" id="id_{{ question.pk }}" value="{{ choice.choice_text }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
{% empty %}
<textarea class="form-control" cols="30" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="4"></textarea>
{% endfor %}
</div>
</div>
{% endfor %}
<button class="btn btn-lg btn-primary btn-block" type="submit" formmethod="post">{% trans "Submit" %}</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,198 @@
{% extends "registered/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}{% trans "HR Application Management" %}{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "Personal Applications" %}
<div class="text-right">
{% if create %}
<a href="{% url 'hrapplications:create_view' %}">
<button type="button" class="btn btn-success">{% trans "Create Application" %}</button>
</a>
{% else %}
<button type="button" class="btn btn-success" disabled>{% trans "Create Application" %}</button>
{% endif %}
</div>
</h1>
{% if personal_apps %}
<div class="panel panel-default">
<table class="table table-condensed">
<tr>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Corporation" %}
<th class="text-center">{% trans "Status" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
{% for personal_app in personal_apps %}
<tr>
<td class="text-center">{{ personal_app.user.username }}</td>
<td class="text-center">{{ personal_app.form.corp.corporation_name }}</td>
<td class="text-center">
{% if personal_app.approved == None %}
<div class="label label-warning">{% trans "Pending" %}</div>
{% elif personal_app.approved == True %}
<div class="label label-success">{% trans "Approved" %}</div>
{% else %}
<div class="label label-danger">{% trans "Rejected" %}</div>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'hrapplications:personal_view' personal_app.id %}"
class="btn btn-primary">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
{% if personal_app.approved == None %}
<a href="{% url 'hrapplications:personal_removal' personal_app.id %}"
class="btn btn-danger">
<span class="glyphicon glyphicon-remove"></span>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% if perms.auth.human_resources %}
<h1 class="page-header text-center">{% trans "Application Management" %}
<div class="text-right">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#myModal">
{% trans "Search Applications" %}
</button>
</div>
</h1>
<ul class="nav nav-tabs">
<li class="active"><a data-toggle="tab" href="#pending">{% trans "Pending" %}</a></li>
<li><a data-toggle="tab" href="#reviewed">{% trans "Reviewed" %}</a></li>
</ul>
<div class="tab-content">
<div id="pending" class="tab-pane fade in active panel panel-default">
<div class="panel-body">
{% if applications %}
<table class="table">
<tr>
<th class="text-center">{% trans "Date" %}</th>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Main Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th>
<th class="text-center">{% trans "Status" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
{% for app in applications %}
<tr>
<td class="text-center">{{ app.created }}</td>
<td class="text-center">{{ app.user.username }}</td>
<td class="text-center">{{ app.main_character }}</td>
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
<td class="text-center">
{% if app.approved == None %}
{% if app.reviewer_str %}
<div class="label label-info">{% trans "Reviewer:" %} {{ app.reviewer_str }}</div>
{% else %}
<div class="label label-warning">{% trans "Pending" %}</div>
{% endif %}
{% elif app.approved == True %}
<div class="label label-success">{% trans "Approved" %}</div>
{% else %}
<div class="label label-danger">{% trans "Rejected" %}</div>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'hrapplications:view' app.id %}"
class="btn btn-primary">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-warning text-center">{% trans "No pending applications." %}</div>
{% endif %}
</div>
</div>
<div id="reviewed" class="tab-pane fade panel panel-default">
<div class="panel-body">
{% if finished_applications %}
<table class="table">
<tr>
<th class="text-center">{% trans "Date" %}</th>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Main Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th>
<th class="text-center">{% trans "Status" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
{% for app in finished_applications %}
<tr>
<td class="text-center">{{ app.created }}</td>
<td class="text-center">{{ app.user.username }}</td>
<td class="text-center">{{ app.main_character }}</td>
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
<td class="text-center">
{% if app.approved == None %}
{% if app.reviewer_str %}
<div class="label label-info">{% trans "Reviewer:" %} {{ app.reviewer_str }}</div>
{% else %}
<div class="label label-warning">{% trans "Pending" %}</div>
{% endif %}
{% elif app.approved == True %}
<div class="label label-success">{% trans "Approved" %}</div>
{% else %}
<div class="label label-danger">{% trans "Rejected" %}</div>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'hrapplications:view' app.id %}"
class="btn btn-primary">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<div class="alert alert-warning text-center">{% trans "No reviewed applications." %}</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
</div>
{% if perms.auth.human_resources %}
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span
class="sr-only">{% trans "Close" %}</span></button>
<h4 class="modal-title" id="myModalLabel">{% trans "Application Search" %}</h4>
</div>
<div class="modal-body">
<form class="form-signin" role="form"
action={% url 'hrapplications:search' %} method="POST">
{% csrf_token %}
{{ search_form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Search" %}</button>
</form>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
{% endif %}
{% endblock content %}

View File

@@ -0,0 +1,85 @@
{% extends "registered/base.html" %}
{% load bootstrap %}
{% load staticfiles %}
{% load i18n %}
{% block title %}Alliance Auth{% endblock %}
{% block page_title %}HR Application Management{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %}
{% block content %}
<div class="col-lg-12">
{% if perms.auth.human_resources %}
<h1 class="page-header text-center">{% trans "Application Search Results" %}
<div class="text-right">
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary btn-sm" data-toggle="modal" data-target="#myModal">
{% trans "Search Applications" %}
</button>
</div>
</h1>
<div class="container-fluid">
<table class="table table-bordered">
<tr>
<th class="text-center">{% trans "Application ID" %}</th>
<th class="text-center">{% trans "Username" %}</th>
<th class="text-center">{% trans "Main Character" %}</th>
<th class="text-center">{% trans "Corporation" %}</th>
<th class="text-center">{% trans "Status" %}</th>
<th class="text-center">{% trans "Actions" %}</th>
</tr>
{% for app in applications %}
<tr>
<td class="text-center">{{ app.id }}</td>
<td class="text-center">{{ app.user }}</td>
<td class="text-center">{{ app.main_character }}</td>
<td class="text-center">{{ app.form.corp }}</td>
<td class="text-center">
{% if app.approved == None %}
<div class="label label-warning">{% trans "Pending" %}</div>
{% elif app.approved == True %}
<div class="label label-success">{% trans "Approved" %}</div>
{% else %}
<div class="label label-danger">{% trans "Rejected" %}</div>
{% endif %}
</td>
<td class="text-center">
<a href="{% url 'hrapplications:view' app.id %}" class="btn btn-primary">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
</div>
{% if perms.auth.human_resources %}
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span
class="sr-only">{% trans "Close" %}</span></button>
<h4 class="modal-title" id="myModalLabel">{% trans "Application Search" %}</h4>
</div>
<div class="modal-body">
<form class="form-signin" role="form"
action={% url 'hrapplications:search' %} method="POST">
{% csrf_token %}
{{ search_form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Search" %}</button>
</form>
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>
{% endif %}
{% endblock content %}

View File

@@ -0,0 +1,164 @@
{% extends "registered/base.html" %}
{% load staticfiles %}
{% load bootstrap %}
{% load i18n %}
{% load eveonline_extras %}
{% block title %}Alliance Auth - {% trans "View Application" %}{% endblock %}
{% block page_title %}{% trans "View Application" %}{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">{% trans "View Application" %}</h1>
<div class="container-fluid">
<div class="col-md-6 col-md-offset-3">
<div class="row">
{% if app.approved %}
<div class="alert alert-success text-center">{% trans "Approved" %}</div>
{% elif app.approved == False %}
<div class="alert alert-danger text-center">{% trans "Denied" %}</div>
{% else %}
<div class="alert alert-warning text-center">{% trans "Pending" %}</div>
{% endif %}
{% if app.reviewer_str %}
<div class="alert alert-info text-center">{% trans "Reviewer:" %} {{ app.reviewer_str }}</div>
{% endif %}
</div>
<div class="row">
<div class="panel panel-info">
<div class="panel-heading">{% trans "Applicant" %}</div>
<table class="table">
<tr>
<th class="text-center">{% trans "User" %}</th>
<th class="text-center">{% trans "Main Character" %}</th>
</tr>
<tr>
<td class="text-center">{{ app.user }}</td>
<td class="text-center">{{ app.main_character }}</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">{% trans "Characters" %}</div>
<table class="table">
<tr>
<th class="text-center"></th>
<th class="text-center">{% trans "Name" %}</th>
<th class="text-center">{% trans "Corp" %}</th>
<th class="text-center">{% trans "Alliance" %}</th>
</tr>
{% for char in app.characters %}
<tr>
<td class="text-center">
<img class="ra-avatar img-responsive img-circle"
src="https://image.eveonline.com/Character/{{ char.character_id }}_32.jpg">
</td>
<td class="text-center">{{ char.character_name }}</td>
<td class="text-center">{{ char.corporation_name }}</td>
<td class="text-center">{{ char.alliance_name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="row">
{% for response in responses %}
<div class="panel panel-default">
<div class="panel-heading">{{ response.question.title }}</div>
<div class="alert">{{ response.answer|linebreaksbr }}</div>
</div>
{% endfor %}
</div>
{% if buttons %}
{% if perms.auth.human_resources %}
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">{% trans "Actions" %}</div>
<div class="panel-body text-center">
{% if app.approved == None %}
{% if app.reviewer == user %}
{% if perms.hrapplications.approve_application %}
<a href="{% url 'hrapplications:approve' app.id %}"
class="btn btn-success">{% trans "Approve" %}</a>
{% endif %}
{% if perms.hrapplications.reject_application %}
<a href="{% url 'hrapplications:reject' app.id %}"
class="btn btn-danger">{% trans "Reject" %}</a>
{% endif %}
{% if perms.hrapplications.delete_application %}
<a href="{% url 'hrapplications:remove' app.id %}"
class="btn btn-danger">{% trans "Delete" %}</a>
{% endif %}
{% elif not app.reviewer %}
<a href="{% url 'hrapplications:mark_in_progress' app.id %}"
class="btn btn-warning">{% trans "Mark in Progress" %}</a>
{% endif %}
{% endif %}
{% if perms.hrapplications.add_applicationcomment %}
<button type="button" class="btn btn-primary" data-toggle="modal"
data-target="#myModal">{% trans "Comment" %}</button>
{% endif %}
</div>
</div>
</div>
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#accordion"
href="#collapseThree" aria-expanded="false"
aria-controls="collapseThree">
{% trans 'Comments' %} ({{ comments.count }})
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="headingThree">
<div class="panel-body">
{% for comment in comments %}
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="">
<div class="panel-title">
<div class="pull-right">{{ comment.created }}</div>
<div class="pull-left">{% if comment.user.profile.main_character %}{{ comment.user.profile.main_character }}{% else %}{{ comment.user }}{% endif %}</div>
<div class="clearfix"></div>
</div>
</div>
<div class="panel-body">{{ comment.text|linebreaks }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% if perms.hrapplications.add_applicationcomment %}
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">
<span aria-hidden="true">&times;</span><span class="sr-only">{% trans "Close" %}</span>
</button>
<h4 class="modal-title" id="myModalLabel">{% trans "Add Comment" %}</h4>
</div>
<div class="modal-body">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ comment_form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">{% trans "Add Comment" %}</button>
</form>
</div>
<div class="modal-footer"></div>
</div>
</div>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1 @@
# Create your tests here.

View File

@@ -0,0 +1,31 @@
from django.conf.urls import url
from . import views
app_name = 'hrapplications'
urlpatterns = [
url(r'^$', views.hr_application_management_view,
name="index"),
url(r'^create/$', views.hr_application_create_view,
name="create_view"),
url(r'^create/(\d+)', views.hr_application_create_view,
name="create_view"),
url(r'^remove/(\w+)', views.hr_application_remove,
name="remove"),
url(r'view/(\w+)', views.hr_application_view,
name="view"),
url(r'personal/view/(\w+)', views.hr_application_personal_view,
name="personal_view"),
url(r'personal/removal/(\w+)',
views.hr_application_personal_removal,
name="personal_removal"),
url(r'approve/(\w+)', views.hr_application_approve,
name="approve"),
url(r'reject/(\w+)', views.hr_application_reject,
name="reject"),
url(r'search/', views.hr_application_search,
name="search"),
url(r'mark_in_progress/(\w+)', views.hr_application_mark_in_progress,
name="mark_in_progress"),
]

View File

@@ -0,0 +1,260 @@
import logging
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import render, get_object_or_404, redirect
from .models import Application
from .models import ApplicationComment
from .models import ApplicationForm
from .models import ApplicationResponse
from allianceauth.notifications import notify
from .forms import HRApplicationCommentForm
from .forms import HRApplicationSearchForm
logger = logging.getLogger(__name__)
def create_application_test(user):
return bool(user.profile.main_character)
@login_required
def hr_application_management_view(request):
logger.debug("hr_application_management_view called by user %s" % request.user)
corp_applications = []
finished_corp_applications = []
main_char = request.user.profile.main_character
if request.user.is_superuser:
corp_applications = Application.objects.filter(approved=None)
finished_corp_applications = Application.objects.exclude(approved=None)
elif request.user.has_perm('auth.human_resources') and main_char:
if ApplicationForm.objects.filter(corp__corporation_id=main_char.corporation_id).exists():
app_form = ApplicationForm.objects.get(corp__corporation_id=main_char.corporation_id)
corp_applications = Application.objects.filter(form=app_form).filter(approved=None)
finished_corp_applications = Application.objects.filter(form=app_form).filter(approved__in=[True, False])
logger.debug("Retrieved %s personal, %s corp applications for %s" % (
len(request.user.applications.all()), len(corp_applications), request.user))
context = {
'personal_apps': request.user.applications.all(),
'applications': corp_applications,
'finished_applications': finished_corp_applications,
'search_form': HRApplicationSearchForm(),
'create': create_application_test(request.user)
}
return render(request, 'hrapplications/management.html', context=context)
@login_required
@user_passes_test(create_application_test)
def hr_application_create_view(request, form_id=None):
if form_id:
app_form = get_object_or_404(ApplicationForm, id=form_id)
if request.method == "POST":
if Application.objects.filter(user=request.user).filter(form=app_form).exists():
logger.warn("User %s attempting to duplicate application to %s" % (request.user, app_form.corp))
else:
application = Application(user=request.user, form=app_form)
application.save()
for question in app_form.questions.all():
response = ApplicationResponse(question=question, application=application)
response.answer = request.POST.get(str(question.pk),
"Failed to retrieve answer provided by applicant.")
response.save()
logger.info("%s created %s" % (request.user, application))
return redirect('auth_hrapplications_view')
else:
questions = app_form.questions.all()
return render(request, 'hrapplications/create.html',
context={'questions': questions, 'corp': app_form.corp})
else:
choices = []
for app_form in ApplicationForm.objects.all():
if not Application.objects.filter(user=request.user).filter(form=app_form).exists():
choices.append((app_form.id, app_form.corp.corporation_name))
return render(request, 'hrapplications/corpchoice.html', context={'choices': choices})
@login_required
def hr_application_personal_view(request, app_id):
logger.debug("hr_application_personal_view called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if app.user == request.user:
context = {
'app': app,
'responses': ApplicationResponse.objects.filter(application=app),
'buttons': False,
'comments': ApplicationComment.objects.filter(application=app),
'comment_form': HRApplicationCommentForm(),
}
return render(request, 'hrapplications/view.html', context=context)
else:
logger.warn("User %s not authorized to view %s" % (request.user, app))
return redirect('auth_hrapplications_view')
@login_required
def hr_application_personal_removal(request, app_id):
logger.debug("hr_application_personal_removal called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if app.user == request.user:
if app.approved is None:
logger.info("User %s deleting %s" % (request.user, app))
app.delete()
else:
logger.warn("User %s attempting to delete reviewed app %s" % (request.user, app))
else:
logger.warn("User %s not authorized to delete %s" % (request.user, app))
return redirect('auth_hrapplications_view')
@login_required
@permission_required('auth.human_resources')
def hr_application_view(request, app_id):
logger.debug("hr_application_view called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if request.method == 'POST':
if request.user.has_perm('hrapplications.add_applicationcomment'):
form = HRApplicationCommentForm(request.POST)
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
if form.is_valid():
comment = ApplicationComment()
comment.application = app
comment.user = request.user
comment.text = form.cleaned_data['comment']
comment.save()
logger.info("Saved comment by user %s to %s" % (request.user, app))
return redirect(hr_application_view, app_id)
else:
logger.warn("User %s does not have permission to add ApplicationComments" % request.user)
return redirect(hr_application_view, app_id)
else:
logger.debug("Returning blank HRApplication comment form.")
form = HRApplicationCommentForm()
context = {
'app': app,
'responses': ApplicationResponse.objects.filter(application=app),
'buttons': True,
'comments': ApplicationComment.objects.filter(application=app),
'comment_form': form,
}
return render(request, 'hrapplications/view.html', context=context)
@login_required
@permission_required('auth.human_resources')
@permission_required('hrapplications.delete_application')
def hr_application_remove(request, app_id):
logger.debug("hr_application_remove called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
logger.info("User %s deleting %s" % (request.user, app))
app.delete()
notify(app.user, "Application Deleted", message="Your application to %s was deleted." % app.form.corp)
return redirect('auth_hrapplications_view')
@login_required
@permission_required('auth.human_resources')
@permission_required('hrapplications.approve_application')
def hr_application_approve(request, app_id):
logger.debug("hr_application_approve called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if request.user.is_superuser or request.user == app.reviewer:
logger.info("User %s approving %s" % (request.user, app))
app.approved = True
app.save()
notify(app.user, "Application Accepted", message="Your application to %s has been approved." % app.form.corp,
level="success")
else:
logger.warn("User %s not authorized to approve %s" % (request.user, app))
return redirect('auth_hrapplications_view')
@login_required
@permission_required('auth.human_resources')
@permission_required('hrapplications.reject_application')
def hr_application_reject(request, app_id):
logger.debug("hr_application_reject called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if request.user.is_superuser or request.user == app.reviewer:
logger.info("User %s rejecting %s" % (request.user, app))
app.approved = False
app.save()
notify(app.user, "Application Rejected", message="Your application to %s has been rejected." % app.form.corp,
level="danger")
else:
logger.warn("User %s not authorized to reject %s" % (request.user, app))
return redirect('auth_hrapplications_view')
@login_required
@permission_required('auth.human_resources')
def hr_application_search(request):
logger.debug("hr_application_search called by user %s" % request.user)
if request.method == 'POST':
form = HRApplicationSearchForm(request.POST)
logger.debug("Request type POST contains form valid: %s" % form.is_valid())
if form.is_valid():
searchstring = form.cleaned_data['search_string'].lower()
applications = set([])
logger.debug("Searching for application with character name %s for user %s" % (searchstring, request.user))
app_list = []
if request.user.is_superuser:
app_list = Application.objects.all()
else:
try:
app_list = Application.objects.filter(form__corp__corporation_id=request.user.profile.main_character.corporation_id)
except AttributeError:
logger.warn(
"User %s missing main character model: unable to filter applications to search" % request.user)
for application in app_list:
if application.main_character:
if searchstring in application.main_character.character_name.lower():
applications.add(application)
if searchstring in application.main_character.corporation_name.lower():
applications.add(application)
if application.main_character.alliance_name \
and searchstring in application.main_character.alliance_name.lower():
applications.add(application)
for character in application.characters:
if searchstring in character.character_name.lower():
applications.add(application)
if searchstring in character.corporation_name.lower():
applications.add(application)
if character.alliance_name and searchstring in character.alliance_name.lower():
applications.add(application)
if searchstring in application.user.username.lower():
applications.add(application)
logger.info("Found %s Applications for user %s matching search string %s" % (
len(applications), request.user, searchstring))
context = {'applications': applications, 'search_form': HRApplicationSearchForm()}
return render(request, 'hrapplications/searchview.html', context=context)
else:
logger.debug("Form invalid - returning for user %s to retry." % request.user)
context = {'applications': None, 'search_form': form}
return render(request, 'hrapplications/searchview.html', context=context)
else:
logger.debug("Returning empty search form for user %s" % request.user)
return redirect("auth_hrapplications_view")
@login_required
@permission_required('auth.human_resources')
def hr_application_mark_in_progress(request, app_id):
logger.debug("hr_application_mark_in_progress called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if not app.reviewer:
logger.info("User %s marking %s in progress" % (request.user, app))
app.reviewer = request.user
app.reviewer_character = request.user.profile.main_character
app.save()
notify(app.user, "Application In Progress",
message="Your application to %s is being reviewed by %s" % (app.form.corp, app.reviewer_str))
else:
logger.warn(
"User %s unable to mark %s in progress: already being reviewed by %s" % (request.user, app, app.reviewer))
return redirect("hrapplications:view", app_id)