Large diffs are not rendered by default.

@@ -20,17 +20,108 @@
# You should have received a copy of the GNU General Public License
# along with Pups. If not, see <http://www.gnu.org/licenses/>.

import datetime
from django.db import models
from django.db.models import F
from django.core.exceptions import ObjectDoesNotExist
from django.core import serializers
from django.utils import timezone
from pups import settings


# Status codes
_issueDoesNotExist, _issuesIsLocked, _issueIsNotLocked, \
_failure = range(1,5)

class Issue(models.Model):
issue_id = models.CharField(max_length=64)
text = models.TextField()
frequency = models.IntegerField()
frequency = models.IntegerField(default=1)
created_by = models.CharField(max_length=64)
is_locked = models.BooleanField()
created_at = models.DateTimeField(auto_now_add=True)
last_edited_at = models.DateTimeField(auto_now_add=True)
is_locked = models.BooleanField(default=False)
locked_by = models.CharField(max_length=64)
locked_since = models.DateTimeField(null=True)
last_edited_by = models.CharField(max_length=64)

def __unicode__(self):
return u'ID: %s Owner: %s Text: %s' % (self.issue_id, self.owner, self.text)
return u'ID: %s Text: %s Freq: %s locked: %s' % \
(self.pk, self.text, self.frequency, self.is_locked)

@staticmethod
def get_issues_json():
return serializers.serialize(
"json",
Issue.objects.all().order_by('-frequency'))

@staticmethod
def create_issue(user, text):
q = Issue(
text=text,
created_by=user
)
q.save()

return q.id is not None

@staticmethod
def delete_issue(id):
issue = Issue.objects.filter(id=id)
issue.delete()

@staticmethod
def save_edit(id, edited_text, user):
issue = Issue.objects.filter(id=id)

if not issue:
return _failure

issue.update(text=edited_text)
issue.update(last_edited_by=user)

@staticmethod
def plus_one(id):
issue = Issue.objects.filter(id=id)
issue.update(frequency=F('frequency')+1)

@staticmethod
def lock(id, user):
issue = Issue.objects.filter(id=id)
issue.update(is_locked=True)
issue.update(locked_since=timezone.now())
issue.update(locked_by=user)

@staticmethod
def unlock(id):
issue = Issue.objects.filter(id=id)

if not issue:
return _failure

issue.update(is_locked=False)
issue.update(locked_by="")

@staticmethod
def lock_status(id):
'''
Returns a single issue's status code and unlocks it if it's
less than timezone.now() < locked_until
'''
try:
issue_obj = Issue.objects.get(id=id)
except ObjectDoesNotExist:
return [_issueDoesNotExist]

if issue_obj.is_locked:
locked_until = issue_obj.locked_since + datetime.timedelta(
minutes=settings.CONFIG['edit_lock_expiration'])

if timezone.now() < locked_until:
return [_issuesIsLocked, issue_obj.locked_by,
(locked_until - timezone.now()).seconds / 60,
settings.CONFIG['edit_lock_expiration']]
else:
Issue.unlock(id)

return [_issueIsNotLocked,
settings.CONFIG['edit_lock_expiration']]
@@ -15,31 +15,66 @@

{% block content %}
{% include "nav_bar.html" %}
<div id="newq">
<input id="new_issue_text" class="form-control" placeholder="Warning: This is not ready yet!" type="text" style="display: inline-block;">
<input id="save_issue" class="btn btn-default" type="button" value="Save">

<form role="form" method="post" action="/create_issue">
{% csrf_token %}
<input id="new_issue_text" name="new_issue_text" class="form-control" placeholder="Give me some text..." type="text" style="display: inline-block;">
<button id="save_issue" class="btn btn-default" type="submit">Save</button>
<div id="newq">
</div>
</form>

<div id="issues">
</div>

<!-- Issue Edit Modal -->
<div class="modal fade" id="EditIssue" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Edit issue</h4>
</div>
<div class="modal-body">
<textarea id="edit_text" rows="8" cols="65"></textarea>
</div>
<div id="lock_limit"></div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" id="close_edit">Close</button>
<button type="button" class="btn btn-primary" id="save_edit">Save changes</button>
</div>
</div>
</div>
</div>

<div id="issues"></div>

<!-- Issue Edit Modal -->
<div class="modal fade" id="EditIssue" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">Edit issue</h4>
</div>
<div class="modal-body">
<textarea id="edit_text" rows="8" cols="65"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" id="close_edit">Close</button>
<button type="button" class="btn btn-primary" id="save_edit">Save changes</button>
</div>
<!-- Alert Modal -->
<div class="modal fade" id="Alert" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="AlertLable"></h4>
</div>
<div class="alert-body" id="alert-text"></div>
<div class="modal-footer">
<button id="alert-ok" type="button" class="btn btn-default" data-dismiss="modal">OK</button>
</div>
</div>
</div>
</div>

<!-- Server unreachable Modal -->
<div class="modal fade" id="alert-server-unreachable" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="AlertLable"></h4>
</div>
<div class="alert-body" id="alert-server-unreachable-text"></div>
<div class="modal-footer">
<button id="reconnect" type="button" class="btn btn-default" data-dismiss="modal">Reconnect</button>
</div>
</div>
</div>
</div>

{% include "footer.html" %}
{% endblock content %}
@@ -25,5 +25,13 @@

urlpatterns = patterns(
'',
url(r'^stats$', 'stats.views.stats')
url(r'^stats$', 'stats.views.stats_page'),
url(r'^create_issue$', 'stats.views.create_issue'),
url(r'^delete_issue$', 'stats.views.delete_issue_ajax'),
url(r'^edit_issue$', 'stats.views.edit_issue_ajax'),
url(r'^save_issue_edit$', 'stats.views.save_issue_edit_ajax'),
url(r'^unlock_issue$', 'stats.views.unlock_issue_ajax'),
url(r'^plus_one$', 'stats.views.plus_one_ajax'),
url(r'^stats_data_ajax$', 'stats.views.stats_data_ajax'),

)
@@ -20,12 +20,110 @@
# You should have received a copy of the GNU General Public License
# along with Pups. If not, see <http://www.gnu.org/licenses/>.

import json
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from stats.models import Issue
from pups import settings


@login_required
def stats(request):
# Status codes
_issueDoesNotExist, _issuesIsLocked, _issueIsNotLocked, \
_failure = range(1,5)


@login_required
def stats_page(request):
return render(request, 'stats.html')


@login_required
def stats_data_ajax(request):
return HttpResponse(json.dumps(Issue.get_issues_json()),
content_type="application/json")


@login_required
def create_issue(request):

Issue.create_issue(request.user.username, request.POST['new_issue_text'])
return redirect("/stats")


@login_required
def plus_one_ajax(request):
'''
This method increases an issue by one in the db
'''
response = get_response(request.POST['id'])

if response['status_code'] == _issueIsNotLocked:
Issue.plus_one(request.POST['id'])

return HttpResponse(json.dumps(response),
content_type="application/json")


@login_required
def edit_issue_ajax(request):
'''
Attempts to lock issue row in db and reports back if row
was locked by another user or does not exist.
'''
response = get_response(request.POST['id'])

if response['status_code'] == _issueIsNotLocked:
Issue.lock(request.POST['id'], request.user.username)

return HttpResponse(json.dumps(response),
content_type="application/json")


@login_required
def save_issue_edit_ajax(request):
return HttpResponse(json.dumps(Issue.save_edit(
request.POST['id'],
request.POST['edited_text'],
request.user.username)),
content_type="application/json")


@login_required
def unlock_issue_ajax(request):
return HttpResponse(json.dumps(Issue.unlock(request.POST['id'])),
content_type="application/json")


@login_required
def delete_issue_ajax(request):
'''
Deletes one issue (db row) and gives back feedback in json
'''
response = get_response(request.POST['id'])

if response['status_code'] == _issueIsNotLocked:
Issue.delete_issue(request.POST['id'])

return HttpResponse(json.dumps(response),
content_type="application/json")


def get_response(id):
lock_status = Issue.lock_status(int(id))
response = {}

if lock_status:
if lock_status[0] == _issuesIsLocked:
response['status_code'] = lock_status[0]
response['locked_by'] = lock_status[1]
response['expires_in'] = lock_status[2]

elif lock_status[0] == _issueDoesNotExist:
response['status_code'] = lock_status[0]

elif lock_status[0] == _issueIsNotLocked:
response['status_code'] = lock_status[0]
response['lock_limit'] = lock_status[1]

return response