Skip to content
This repository was archived by the owner on Mar 7, 2022. It is now read-only.

Commit 850f7d7

Browse files
committed
Merge branch 'release/07-August-2017'
2 parents 0f47c3c + 947bd70 commit 850f7d7

File tree

13 files changed

+287
-15
lines changed

13 files changed

+287
-15
lines changed

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,10 @@ ENV/
9595
.idea/
9696

9797
# Bower
98-
pyconkr/static/components/*
98+
pyconkr/static/components/*
99+
100+
# TEST db
101+
db.sqlite3
102+
103+
# macOS
104+
.DS_Store

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# A git repository for PyCon Korea 2017
2-
## Version 02-August-2017
2+
## Version 07-August-2017
33

44
[![Build Status](https://travis-ci.org/pythonkr/pyconkr-2017.svg?branch=master)](https://travis-ci.org/pythonkr/pyconkr-2017)
55

bower.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
"bootstrap-social": "~5.1.1",
1414
"typed.js": "~1.1.1",
1515
"bootstrap-side-navbar": "^1.0.1",
16-
"montserrat-webfont": "^1.0.3"
16+
"montserrat-webfont": "^1.0.3",
17+
"datatables.net": "^1.10.15",
18+
"datatables.net-bs": "^2.1.1",
19+
"datatables.net-rowreorder": "^1.2.0",
20+
"datatables.net-rowreorder-bs": "^1.2.0",
21+
"handlebars": "^4.0.10",
22+
"js-cookie": "^2.1.4"
1723
}
1824
}

pyconkr/templates/base.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@
4343
<script src="{% static "components/jquery/dist/jquery.min.js" %}"></script>
4444
<script src="{% static "components/bootstrap/dist/js/bootstrap.min.js" %}"></script>
4545
<script src="{% static "js/rgb-hsv.js" %}"></script>
46+
<script src="{% static "components/js-cookie/src/js.cookie.js" %}" charset="utf-8"></script>
47+
<script src="{% static "components/handlebars/handlebars.min.js" %}" charset="utf-8"></script>
48+
<script>
49+
function csrfSafeMethod(method) {
50+
// these HTTP methods do not require CSRF protection
51+
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
52+
}
53+
</script>
4654
{% block head-include %}{% endblock %}
4755
</head>
4856

registration/admin.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from django.utils import timezone
77
from .iamporter import get_access_token, Iamporter, IamporterError
88

9-
from .models import Registration, Option, ManualPayment
9+
from .models import Registration, Option, ManualPayment, IssueTicket
1010

1111

1212
def send_bankpayment_alert_email(modeladmin, request, queryset):
@@ -114,13 +114,18 @@ class RegistrationAdmin(admin.ModelAdmin):
114114
list_editable = ('payment_status',)
115115
list_filter = ('option', 'payment_method', 'payment_status')
116116
csv_fields = ['name', 'email', 'company', 'option', ]
117-
search_fields = ('name', 'email')
118-
readonly_fields = ('created', )
117+
search_fields = ('name', 'email', 'merchant_uid', 'transaction_code', )
118+
readonly_fields = ('created', 'merchant_uid', 'transaction_code', )
119119
ordering = ('id',)
120120
actions = (send_bankpayment_alert_email, cancel_registration)
121121
admin.site.register(Registration, RegistrationAdmin)
122122

123123

124+
class IssueTicketAdmin(admin.ModelAdmin):
125+
list_display = ('registration', 'issuer', 'issue_date')
126+
ordering = ('issue_date',)
127+
admin.site.register(IssueTicket, IssueTicketAdmin)
128+
124129
class ManualPaymentAdmin(admin.ModelAdmin):
125130
list_display = ('title', 'payment_status', 'user', )
126131
list_filter = ('payment_status', )

registration/forms.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,7 @@ class Meta:
7575
'email': _('Email'),
7676
'payment_method': _('Payment Method'),
7777
}
78+
79+
80+
class IssueSubmitForm(forms.Form):
81+
user_id = forms.IntegerField()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# -*- coding: utf-8 -*-
2+
# Generated by Django 1.11 on 2017-08-01 11:33
3+
from __future__ import unicode_literals
4+
5+
from django.conf import settings
6+
from django.db import migrations, models
7+
import django.db.models.deletion
8+
import django.utils.timezone
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
dependencies = [
14+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15+
('registration', '0022_manualpayment'),
16+
]
17+
18+
operations = [
19+
migrations.CreateModel(
20+
name='IssueTicket',
21+
fields=[
22+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23+
('issue_date', models.DateTimeField(default=django.utils.timezone.now)),
24+
('issuer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
25+
('registration', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='registration.Registration')),
26+
],
27+
),
28+
]

registration/models.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from django.db import models
55
from django.contrib.auth.models import User
6-
6+
from django.utils import timezone
77

88
class Option(models.Model):
99
name = models.CharField(max_length=50)
@@ -17,7 +17,7 @@ class Option(models.Model):
1717

1818
class Meta:
1919
ordering = ['price']
20-
20+
2121
@property
2222
def is_soldout(self):
2323
return self.total <= Registration.objects.filter(option=self, payment_status__in=['paid', 'ready']).count()
@@ -80,6 +80,8 @@ class Registration(models.Model):
8080
confirmed = models.DateTimeField(null=True, blank=True)
8181
canceled = models.DateTimeField(null=True, blank=True)
8282

83+
def __str__(self):
84+
return "{} {} {}".format(self.name, self.email, self.option.name)
8385

8486
class ManualPayment(models.Model):
8587
user = models.ForeignKey(User)
@@ -113,3 +115,9 @@ class ManualPayment(models.Model):
113115

114116
def __str__(self):
115117
return '({}) {} ({})원'.format(self.payment_status.upper(), self.title, self.price)
118+
119+
120+
class IssueTicket(models.Model):
121+
registration = models.ForeignKey(Registration)
122+
issuer = models.ForeignKey(User)
123+
issue_date = models.DateTimeField(default=timezone.now)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
{% extends "base.html" %}
2+
{% load i18n humanize %}
3+
{% load staticfiles %}
4+
{% load crispy_forms_tags %}
5+
6+
{% block content %}
7+
<table id="issue-ticket" class="table table-hover">
8+
<thead>
9+
<tr>
10+
<th>이름</th>
11+
<th>이메일</th>
12+
<th>관리</th>
13+
</tr>
14+
</thead>
15+
<tbody>
16+
{% for user in registration %}
17+
<tr>
18+
<td>{{ user.name }}</td>
19+
<td>{{ user.email }}</td>
20+
<td>
21+
<div class="btn-group">
22+
<button type="button" class="btn btn-success btn-filter" onclick="printLabelPopup('{{ user.name }}', {{ user.id }},'{{ user.top_size }}')">발권</button>
23+
<a class="btn btn-default btn-filter"
24+
href='/admin/registration/registration/{{user.id}}'
25+
target='_blank' role="button">관리</a>
26+
</div>
27+
</td>
28+
</tr>
29+
{% endfor %}
30+
</tbody>
31+
</table>
32+
33+
34+
{% endblock %}
35+
36+
{% block head-include %}
37+
<link href="{% static "components/datatables.net-bs/css/dataTables.bootstrap.min.css" %}" rel="stylesheet">
38+
{% endblock %}
39+
40+
{% block script %}
41+
<script src="{% static "components/datatables.net/js/jquery.dataTables.min.js" %}" charset="utf-8"></script>
42+
<script src="{% static "components/datatables.net-bs/js/dataTables.bootstrap.min.js" %}" charset="utf-8"></script>
43+
44+
{% verbatim %}
45+
<script id="print_template" type="text/x-handlebars-template">
46+
<div class="ui brother-label">
47+
<p class="ui name">{{name}}</p>
48+
</div>
49+
<div class="ui brother-label">
50+
<p class="ui name">{{topSize}}</p>
51+
</div>
52+
<style>
53+
.brother-label {
54+
margin: 0 auto;
55+
padding: 0 auto;
56+
width: 88mm;
57+
height: 27mm;
58+
}
59+
.name{
60+
font-size: {{fontSize}};
61+
font-weight: bold;
62+
text-align: center;
63+
letter-spacing: 1rem;
64+
}
65+
@media print {
66+
body, html, p{
67+
margin: 0mm !important;
68+
padding: 0mm !important;
69+
}
70+
@page{
71+
margin: 0mm !important;
72+
padding: 0mm !important;
73+
}
74+
}
75+
</script>
76+
{% endverbatim %}
77+
78+
<script type="text/javascript">
79+
$(document).ready( function () {
80+
$('#issue-ticket').DataTable({
81+
bInfo: true,
82+
bLengthChange: true,
83+
lengthMenu: [[50, 100, 200], [50, 100, 200]],
84+
order: [[ 0, 'asc' ]],
85+
paging: true,
86+
searching: true,
87+
});
88+
});
89+
90+
function issueTicketConfirm(userId) {
91+
var csrftoken = Cookies.get('csrftoken');
92+
$.ajaxSetup({
93+
beforeSend: function(xhr, settings) {
94+
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
95+
xhr.setRequestHeader("X-CSRFToken", csrftoken);
96+
}
97+
}
98+
});
99+
$.ajax("{% url 'registration_issue_submit' %}", {
100+
data: {'user_id': userId},
101+
method: 'POST',
102+
success: function(data) {
103+
alert('발권처리 기록 되었습니다.');
104+
},
105+
error: function(data) {
106+
alert('발권처리 기록에 실패 하였습니다.');
107+
}
108+
});
109+
}
110+
111+
function printLabelPopup(name, userId, topSize) {
112+
var printWindow = window.open('', '', 'width=600, height=400');
113+
var fontSize = '1em';
114+
var printTemplateScript = $('#print_template').html();
115+
var printTemplate = Handlebars.compile(printTemplateScript);
116+
var context = {
117+
'name': name,
118+
'topSize': topSize,
119+
'fontSize': fontSize
120+
};
121+
printWindow.document.write(printTemplate(context));
122+
printWindow.document.close();
123+
printWindow.focus();
124+
printWindow.print();
125+
printWindow.close();
126+
issueTicketConfirm(userId);
127+
}
128+
</script>
129+
130+
{% endblock %}

registration/tests.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
from unittest import mock
55
from django.test import TestCase
66
from django.contrib.auth import get_user_model
7+
from django.contrib.auth.models import Group
78
from django.core.urlresolvers import reverse
89
from constance.test import override_config
910
from django_dynamic_fixture import G
1011

11-
from .models import Option, Registration
12+
from .models import Option, Registration, IssueTicket
1213

1314
User = get_user_model()
1415

@@ -48,4 +49,33 @@ def test_vbank_status_update_via_iamport_callback(self, Iamporter, get_access_to
4849
response = self.client.post(reverse('registration_callback'), callback_param)
4950
self.assertEqual(response.status_code, 200)
5051
registration = Registration.objects.get(id=registration.id)
51-
self.assertEqual(registration.payment_status, 'paid')
52+
self.assertEqual(registration.payment_status, 'paid')
53+
54+
55+
class IssueTicketTest(TestCase):
56+
def test_inner_group_only(self):
57+
response = self.client.get(reverse('registration_issue'))
58+
self.assertNotEqual(response.status_code, 200)
59+
login_user = User.objects.create_user('test@user.com', 'test@user.com',
60+
'testpass')
61+
self.client.login(username='test@user.com', password='testpass')
62+
response = self.client.get(reverse('registration_issue'))
63+
self.assertNotEqual(response.status_code, 200)
64+
group = G(Group, name='volunteer')
65+
login_user.groups.add(group)
66+
response = self.client.get(reverse('registration_issue'))
67+
self.assertEqual(response.status_code, 200)
68+
69+
def test_incr_issue_count(self):
70+
login_user = User.objects.create_user('test@user.com', 'test@user.com',
71+
'testpass')
72+
G(Registration, payment_status='paid', user=login_user)
73+
group = G(Group, name='volunteer')
74+
login_user.groups.add(group)
75+
self.client.login(username='test@user.com', password='testpass')
76+
response = self.client.get(reverse('registration_issue_submit'))
77+
self.assertEqual(response.status_code, 405) # Because POST only
78+
response = self.client.post(reverse('registration_issue_submit'),
79+
{'user_id': login_user.id})
80+
issue_count = IssueTicket.objects.filter(registration__user=login_user).count()
81+
self.assertEqual(issue_count, 1)

0 commit comments

Comments
 (0)