Browse Source

added hitcount for staff

master
Esther Kleinhenz 5 years ago
parent
commit
d773cba908
30 changed files with 1445 additions and 12 deletions
  1. 9
    1
      application/templates/post_detail.html
  2. 8
    11
      application/views.py
  3. 1
    0
      mysite/settings.py
  4. 38
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/DESCRIPTION.rst
  5. 1
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/INSTALLER
  6. 61
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/METADATA
  7. 73
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/RECORD
  8. 6
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/WHEEL
  9. 1
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/metadata.json
  10. 1
    0
      thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/top_level.txt
  11. 5
    0
      thesisenv/lib/python3.6/site-packages/hitcount/__init__.py
  12. 102
    0
      thesisenv/lib/python3.6/site-packages/hitcount/admin.py
  13. BIN
      thesisenv/lib/python3.6/site-packages/hitcount/locale/ru/LC_MESSAGES/django.mo
  14. 51
    0
      thesisenv/lib/python3.6/site-packages/hitcount/locale/ru/LC_MESSAGES/django.po
  15. 0
    0
      thesisenv/lib/python3.6/site-packages/hitcount/management/__init__.py
  16. 0
    0
      thesisenv/lib/python3.6/site-packages/hitcount/management/commands/__init__.py
  17. 32
    0
      thesisenv/lib/python3.6/site-packages/hitcount/management/commands/hitcount_cleanup.py
  18. 45
    0
      thesisenv/lib/python3.6/site-packages/hitcount/managers.py
  19. 94
    0
      thesisenv/lib/python3.6/site-packages/hitcount/migrations/0001_initial.py
  20. 29
    0
      thesisenv/lib/python3.6/site-packages/hitcount/migrations/0002_index_ip_and_session.py
  21. 0
    0
      thesisenv/lib/python3.6/site-packages/hitcount/migrations/__init__.py
  22. 197
    0
      thesisenv/lib/python3.6/site-packages/hitcount/models.py
  23. 5
    0
      thesisenv/lib/python3.6/site-packages/hitcount/signals.py
  24. 60
    0
      thesisenv/lib/python3.6/site-packages/hitcount/static/hitcount/hitcount-jquery.js
  25. 59
    0
      thesisenv/lib/python3.6/site-packages/hitcount/static/hitcount/jquery.postcsrf.js
  26. 0
    0
      thesisenv/lib/python3.6/site-packages/hitcount/templatetags/__init__.py
  27. 319
    0
      thesisenv/lib/python3.6/site-packages/hitcount/templatetags/hitcount_tags.py
  28. 12
    0
      thesisenv/lib/python3.6/site-packages/hitcount/urls.py
  29. 47
    0
      thesisenv/lib/python3.6/site-packages/hitcount/utils.py
  30. 189
    0
      thesisenv/lib/python3.6/site-packages/hitcount/views.py

+ 9
- 1
application/templates/post_detail.html View File

@@ -1,5 +1,7 @@
{% extends 'base.html' %} {% block content %}
{% extends 'base.html' %}{% block content %}
{% load hitcount_tags %}
<div class="post">

{% if post.published_date %}
<div class="date">
{{ post.published_date }}
@@ -17,6 +19,7 @@
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
<p>
Tags:
{% for tag in post.tags.all %}
<a href="{% url 'blog_search_list_view' %}">{{ tag.name }}, </a>
{% endfor %}
@@ -24,5 +27,10 @@
<p>
Autor: {{ post.author }}
</p>
{% if user.is_staff %}
<p>
{% get_hit_count for post %} Benutzer haben diesen Post bereits gelesen!
</p>
{% endif %}
</div>
{% endblock %}

+ 8
- 11
application/views.py View File

@@ -16,6 +16,8 @@ from django.contrib import messages
from post_office.models import EmailTemplate
from post_office import mail

from hitcount.models import HitCount
from hitcount.views import HitCountMixin


import logging
@@ -61,7 +63,9 @@ def post_list(request, slug=None):
@login_required
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'post_detail.html', {'post': post})
hit_count = HitCount.objects.get_for_object(post)
hit_count_response = HitCountMixin.hit_count(request, hit_count)
return render(request, 'post_detail.html', locals())


@login_required
@@ -182,17 +186,10 @@ def blog_search_list_view(request):
def tag_cloud(request):
return render(request, 'tag_cloud.html', {})


EmailTemplate.objects.create(
name='weekly-update',
subject='Hi' + User.objects.get(username=request.user) + '!',
content='How are you feeling today?' + Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date'),
html_content='Hi <strong>{{ name }}</strong>, how are you feeling today?',
)

mail.send(
'kleinhenz.e@gmail.com', # List of email addresses also accepted
'esther.kleinhenz@web.de',
template='weekly-update',
context={'name': 'alice'},
subject='My email',
message='Hi there!',
html_message='Hi <strong>there</strong>!',
)

+ 1
- 0
mysite/settings.py View File

@@ -48,6 +48,7 @@ INSTALLED_APPS = [
'taggit_templatetags2',
'kombu.transport.django',
'post_office',
'hitcount',
]

MIDDLEWARE = [

+ 38
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/DESCRIPTION.rst View File

@@ -0,0 +1,38 @@
django-hitcount
===============

.. image:: https://travis-ci.org/thornomad/django-hitcount.svg?branch=master
:target: https://travis-ci.org/thornomad/django-hitcount
.. image:: https://coveralls.io/repos/thornomad/django-hitcount/badge.svg?branch=master
:target: https://coveralls.io/r/thornomad/django-hitcount?branch=master
.. image:: https://badge.fury.io/py/django-hitcount.svg
:target: http://badge.fury.io/py/django-hitcount
.. image:: https://requires.io/github/thornomad/django-hitcount/requirements.svg?branch=develop
:target: https://requires.io/github/thornomad/django-hitcount/requirements/?branch=develop
:alt: Requirements Status

Basic app that allows you to track the number of hits/views for a particular object.

Documentation:
--------------

`<http://django-hitcount.rtfd.org>`_

Source Code:
------------

`<https://github.com/thornomad/django-hitcount>`_

Issues
------

Use the GitHub `issue tracker`_ for django-hitcount to submit bugs, issues, and feature requests.

Changelog
---------

`<http://django-hitcount.readthedocs.org/en/latest/changelog.html>`_

.. _issue tracker: https://github.com/thornomad/django-hitcount/issues



+ 1
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/INSTALLER View File

@@ -0,0 +1 @@
pip

+ 61
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/METADATA View File

@@ -0,0 +1,61 @@
Metadata-Version: 2.0
Name: django-hitcount
Version: 1.3.0
Summary: Hit counting application for Django.
Home-page: http://github.com/thornomad/django-hitcount
Author: Damon Timm
Author-email: damontimm@gmail.com
License: BSD
Description-Content-Type: UNKNOWN
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Plugins
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4

django-hitcount
===============

.. image:: https://travis-ci.org/thornomad/django-hitcount.svg?branch=master
:target: https://travis-ci.org/thornomad/django-hitcount
.. image:: https://coveralls.io/repos/thornomad/django-hitcount/badge.svg?branch=master
:target: https://coveralls.io/r/thornomad/django-hitcount?branch=master
.. image:: https://badge.fury.io/py/django-hitcount.svg
:target: http://badge.fury.io/py/django-hitcount
.. image:: https://requires.io/github/thornomad/django-hitcount/requirements.svg?branch=develop
:target: https://requires.io/github/thornomad/django-hitcount/requirements/?branch=develop
:alt: Requirements Status

Basic app that allows you to track the number of hits/views for a particular object.

Documentation:
--------------

`<http://django-hitcount.rtfd.org>`_

Source Code:
------------

`<https://github.com/thornomad/django-hitcount>`_

Issues
------

Use the GitHub `issue tracker`_ for django-hitcount to submit bugs, issues, and feature requests.

Changelog
---------

`<http://django-hitcount.readthedocs.org/en/latest/changelog.html>`_

.. _issue tracker: https://github.com/thornomad/django-hitcount/issues



+ 73
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/RECORD View File

@@ -0,0 +1,73 @@
django_hitcount-1.3.0.dist-info/DESCRIPTION.rst,sha256=qYNOokuJW5VaV917eTzyWlAD7GUFNfdb1Yuy39nxZnc,1179
django_hitcount-1.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
django_hitcount-1.3.0.dist-info/METADATA,sha256=ZfeWwRDcS89ApGq5n4Z0RkxfmHpNW7KbPI_qOVaVYGc,2029
django_hitcount-1.3.0.dist-info/RECORD,,
django_hitcount-1.3.0.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
django_hitcount-1.3.0.dist-info/metadata.json,sha256=nX26lMZ4TA-8fsrnzAHYW8HW6tJeIZiRkj-e1_qXHbM,950
django_hitcount-1.3.0.dist-info/top_level.txt,sha256=ciQsjrRx8k2LuKG2acan8SxCWV8ktTSJOo2s0eCnxOs,9
hitcount/__init__.py,sha256=l4S7L5a9RF9ngsnCumXsQgnhLbUDrLv75rVI1f37428,110
hitcount/__pycache__/__init__.cpython-36.pyc,,
hitcount/__pycache__/admin.cpython-36.pyc,,
hitcount/__pycache__/managers.cpython-36.pyc,,
hitcount/__pycache__/models.cpython-36.pyc,,
hitcount/__pycache__/signals.cpython-36.pyc,,
hitcount/__pycache__/urls.cpython-36.pyc,,
hitcount/__pycache__/utils.cpython-36.pyc,,
hitcount/__pycache__/views.cpython-36.pyc,,
hitcount/admin.py,sha256=YXinJSVGFwqDuVk0NSwu_PR4JNAkPpZrVvPqvp8Vir8,3517
hitcount/locale/ru/LC_MESSAGES/django.mo,sha256=4P3YSrZbBqm5vKywHU8OP1Yqi_uOr4_0k3ms9hNob3s,922
hitcount/locale/ru/LC_MESSAGES/django.po,sha256=fwKKdutWtaNjshRBuBHgQZS1jfPJ0wLlZPiIQ6l4BoU,1232
hitcount/management/.DS_Store,sha256=eJxOsIPnGpj5wPicqQp2GJotnEXWORC08JwewB8Z7zo,6148
hitcount/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
hitcount/management/__init__.pyc,sha256=ci0qG4gwAh6InRZr7SC0Atn4Ncy4s69lxGTJ24xWL7Q,140
hitcount/management/__pycache__/__init__.cpython-34.pyc,sha256=xt5VdojOPPpEYLlZgrcNAUHpN-0LC2CVx6rqLxd14eo,153
hitcount/management/__pycache__/__init__.cpython-35.pyc,sha256=cJguE581coUsB_2F2LpCEr9P_UXhXydL56y-bKKXNhA,153
hitcount/management/__pycache__/__init__.cpython-36.pyc,sha256=yDorYsjqscWXfGjrO4Z0PBqSzyLzAaHhmc8CN4is9kM,153
hitcount/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
hitcount/management/commands/__init__.pyc,sha256=bAJKmaMoifVBrisekW5ftymMLNpJugWfEkJ2668SIi0,166
hitcount/management/commands/__pycache__/__init__.cpython-34.pyc,sha256=mV0vGG856aEl-nuEL04INYA7iaRteb86yEjsilvwpSA,162
hitcount/management/commands/__pycache__/__init__.cpython-35.pyc,sha256=r4FrwdSELdLgB6nORVThqfr3ZyLXy0azSF5vbW_z6y4,162
hitcount/management/commands/__pycache__/__init__.cpython-36.pyc,sha256=M3hF87z_jcSVV9RHgAjVu977KGlnbtn8kS5o-SFySJ8,162
hitcount/management/commands/__pycache__/hitcount_cleanup.cpython-34-PYTEST.pyc,sha256=CVoedgQixzMUBq0ITb2Y977_rQhfyUuRn8ROB10UwTA,1717
hitcount/management/commands/__pycache__/hitcount_cleanup.cpython-34.pyc,sha256=hdrg5KZxFSk9OQ2XU97TIhoInj_IK5Fo_D4FLVM9Ct0,1600
hitcount/management/commands/__pycache__/hitcount_cleanup.cpython-35.pyc,sha256=ijfeqfAu_bTSjocAkhZiDU53zeVqtPa9U2A2-nGYXh0,1601
hitcount/management/commands/__pycache__/hitcount_cleanup.cpython-36.pyc,sha256=9DALGofFvZXxuReAdWHq-sCFmMrlvFfhGjECRnXl89k,1531
hitcount/management/commands/hitcount_cleanup.py,sha256=W8ubDD60KW-gDRDCqTiErhLB-VmvEIY69JlvZA60sCs,1004
hitcount/management/commands/hitcount_cleanup.pyc,sha256=kJ-jE3silVaQ5ThKJXq_8IH9cCXHV7wtvNgWEpFJd88,1458
hitcount/managers.py,sha256=sO7pNHNm3vbHmev7abW1ZHH8LG4swVXCWJhWRCD40S8,1609
hitcount/migrations/.DS_Store,sha256=knz5wSK6xyaXxFPjuov5ofdbkY45OUT1i5qHp4Hpb_0,6148
hitcount/migrations/0001_initial.py,sha256=Wi4WAnkCqoPZNC9K_mIQCBn7CRYUbWxZsCFe1oyl6ds,3808
hitcount/migrations/0001_initial.pyc,sha256=zUnc6TwI8t3lCBpYSS7PAqdGAEvIA4YlXu8X3tJIq8I,2886
hitcount/migrations/0002_index_ip_and_session.py,sha256=SD4cHYoj-kcOjfR8bOpzq6ZGGn75IAK-KFHNHHsUqNQ,778
hitcount/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
hitcount/migrations/__init__.pyc,sha256=dYHNxrJCOUO4t3iyb8-1dHX51UIqEaEs5F1i6Xqk2eo,157
hitcount/migrations/__pycache__/0001_initial.cpython-34-PYTEST.pyc,sha256=pJYZWfZn294xkgGXKzynGghNM89LiAxsrBitOb8n_zU,2610
hitcount/migrations/__pycache__/0001_initial.cpython-34.pyc,sha256=pod5I9AJU1n_uiCo3af_g1Cr98ymvXWdQwdhee1WxzE,2504
hitcount/migrations/__pycache__/0001_initial.cpython-35.pyc,sha256=zz6vGM0RLLKWeE84UPIE4yGbZqdomjjl3ndZ8usRaH8,2525
hitcount/migrations/__pycache__/0001_initial.cpython-36.pyc,sha256=iyHZAAMqXfJ_bkROAoxUVmrWHMSdGSZ97qfc52OxE9I,2195
hitcount/migrations/__pycache__/0002_auto_20151002_0107.cpython-34.pyc,sha256=d9InaWAdfgzbNAmmehZuyjaUxewfXtmAiVJmzB15EE4,700
hitcount/migrations/__pycache__/0002_index_ip_and_session.cpython-35.pyc,sha256=GEFNCYxH7l2IJbq6aaFfpdXnmfZzymOLau-smEIP38E,888
hitcount/migrations/__pycache__/0002_index_ip_and_session.cpython-36.pyc,sha256=XJ0N1dFeDVmwR8CpSCAMOXtbQgGbSDypaV_cblq_9QM,787
hitcount/migrations/__pycache__/__init__.cpython-34.pyc,sha256=QnlwKINXZCxvEcxGbo6uz9hyeOP7_Sf22MWffdlz7zQ,153
hitcount/migrations/__pycache__/__init__.cpython-35.pyc,sha256=w-PkM7qV8_k4woZyC6AFVVHH0138x3RI5x73MgJ5QgY,153
hitcount/migrations/__pycache__/__init__.cpython-36.pyc,sha256=YZT2H038AusB1l8488oQ_iNqxUjPqBGetXivid8wTAA,153
hitcount/models.py,sha256=TCVZH8n2_7wV6rhJU0w4pj9xEGkqjl-Ho1bRW9feU1g,6452
hitcount/signals.py,sha256=dSAeXBqrfS3alXUyKaT_fTnLKwgpEYo8kSDrUitDYQY,160
hitcount/static/.DS_Store,sha256=rF6vsjXKJhA9tkZOK4dp1e3yiHhIpGTqeG_rErx9KOw,6148
hitcount/static/hitcount/hitcount-jquery.js,sha256=iuT92zgd8cZf3JPIOceugKRw7w8BVvMAb_o3896gnHY,1849
hitcount/static/hitcount/jquery.postcsrf.js,sha256=M7VJf_EO8XXUd9U07gUDndMcOL0a1ZL4DZdukdpTX6c,1696
hitcount/templatetags/.DS_Store,sha256=_bLXwPKHomfjzOqZXKLcl0Df7OVkpZwGLJEuHu46UVg,6148
hitcount/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
hitcount/templatetags/__init__.pyc,sha256=lAeXmAsrGHVMuUKhPyjK_8583A_YDfDD-ameEtV0v4k,142
hitcount/templatetags/__pycache__/__init__.cpython-34.pyc,sha256=L-M9CXK_mJU_claLKAoKdLoyjl_9eykxvX29vZ1xjtU,138
hitcount/templatetags/__pycache__/__init__.cpython-35.pyc,sha256=RsqgNxtyYvbxiFcoZHhuMFBSRLcwud4a7D2xpk7n4rI,155
hitcount/templatetags/__pycache__/__init__.cpython-36.pyc,sha256=DhVijvXN2ym14mkK5ZquL_9FdSfKZA2Yr4z-30aT4_E,155
hitcount/templatetags/__pycache__/hitcount_tags.cpython-34-PYTEST.pyc,sha256=cGwj0P3A_4jOigoaG0BQPAYA8JXTOsaWG-jTzJHuJSg,10515
hitcount/templatetags/__pycache__/hitcount_tags.cpython-34.pyc,sha256=9PnFQoTv1PCwBBcqFaEOarhI62WNke2rFiEsYsUjCHo,10394
hitcount/templatetags/__pycache__/hitcount_tags.cpython-35.pyc,sha256=P3AcxUap_1zp_7I4plKRtth07vyiB3k5gQuIRSc4FW0,10448
hitcount/templatetags/__pycache__/hitcount_tags.cpython-36.pyc,sha256=JaI5iyvmXClChPx2pYnxXxvP-QoOB9ri6UekyZvOiqI,9869
hitcount/templatetags/hitcount_tags.py,sha256=ZhkDhw8ZNvXeRQ8oTb17C0-MTvxJISQ_k3jPup3zLlI,10322
hitcount/templatetags/hitcount_tags.pyc,sha256=lr1N24UpaEcgrOcrmU3RSL9sly4r7nkX8zlpmNY-nn4,9619
hitcount/urls.py,sha256=xBMnJP03XR9bEwhRB6PQmqhSV4rkoiqLs6wG6MIWhJA,255
hitcount/utils.py,sha256=6J-uHYKc7Orweo2ESVSoZqxy7Kvoc2Cr5ZjFTFWzGQs,1610
hitcount/views.py,sha256=cG9Tc7xUQT_PHKHi--wM9zQqdne9HmEl9Tq8uBNuAvE,7288

+ 6
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/WHEEL View File

@@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.30.0)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any


+ 1
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/metadata.json View File

@@ -0,0 +1 @@
{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Plugins", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4"], "description_content_type": "UNKNOWN", "extensions": {"python.details": {"contacts": [{"email": "damontimm@gmail.com", "name": "Damon Timm", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/thornomad/django-hitcount"}}}, "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "django-hitcount", "summary": "Hit counting application for Django.", "version": "1.3.0"}

+ 1
- 0
thesisenv/lib/python3.6/site-packages/django_hitcount-1.3.0.dist-info/top_level.txt View File

@@ -0,0 +1 @@
hitcount

+ 5
- 0
thesisenv/lib/python3.6/site-packages/hitcount/__init__.py View File

@@ -0,0 +1,5 @@
from __future__ import unicode_literals

VERSION = (1, 3, 0)

__version__ = '.'.join(str(i) for i in VERSION)

+ 102
- 0
thesisenv/lib/python3.6/site-packages/hitcount/admin.py View File

@@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext_lazy as _

from .models import Hit, HitCount, BlacklistIP, BlacklistUserAgent


class HitAdmin(admin.ModelAdmin):
list_display = ('created', 'user', 'ip', 'user_agent', 'hitcount')
search_fields = ('ip', 'user_agent')
date_hierarchy = 'created'
actions = ['blacklist_ips',
'blacklist_user_agents',
'blacklist_delete_ips',
'blacklist_delete_user_agents',
'delete_queryset',
]

def __init__(self, *args, **kwargs):
super(HitAdmin, self).__init__(*args, **kwargs)
self.list_display_links = None

def has_add_permission(self, request):
return False

def get_actions(self, request):
actions = super(HitAdmin, self).get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
return actions

def blacklist_ips(self, request, queryset):
for obj in queryset:
ip, created = BlacklistIP.objects.get_or_create(ip=obj.ip)
if created:
ip.save()
msg = _("Successfully blacklisted %d IPs") % queryset.count()
self.message_user(request, msg)
blacklist_ips.short_description = _("Blacklist selected IP addresses")

def blacklist_user_agents(self, request, queryset):
for obj in queryset:
ua, created = BlacklistUserAgent.objects.get_or_create(
user_agent=obj.user_agent)
if created:
ua.save()
msg = _("Successfully blacklisted %d User Agents") % queryset.count()
self.message_user(request, msg)
blacklist_user_agents.short_description = _("Blacklist selected User Agents")

def blacklist_delete_ips(self, request, queryset):
self.blacklist_ips(request, queryset)
self.delete_queryset(request, queryset)
blacklist_delete_ips.short_description = _(
"Delete selected hits and blacklist related IP addresses")

def blacklist_delete_user_agents(self, request, queryset):
self.blacklist_user_agents(request, queryset)
self.delete_queryset(request, queryset)
blacklist_delete_user_agents.short_description = _(
"Delete selected hits and blacklist related User Agents")

def delete_queryset(self, request, queryset):
if not self.has_delete_permission(request):
raise PermissionDenied
else:
if queryset.count() == 1:
msg = "1 hit was"
else:
msg = "%s hits were" % queryset.count()

for obj in queryset.iterator():
obj.delete() # calling it this way to get custom delete() method

self.message_user(request, "%s successfully deleted." % msg)
delete_queryset.short_description = _("Delete selected hits")

admin.site.register(Hit, HitAdmin)


class HitCountAdmin(admin.ModelAdmin):
list_display = ('content_object', 'hits', 'modified')
fields = ('hits',)

def has_add_permission(self, request):
return False

admin.site.register(HitCount, HitCountAdmin)


class BlacklistIPAdmin(admin.ModelAdmin):
pass

admin.site.register(BlacklistIP, BlacklistIPAdmin)


class BlacklistUserAgentAdmin(admin.ModelAdmin):
pass

admin.site.register(BlacklistUserAgent, BlacklistUserAgentAdmin)

BIN
thesisenv/lib/python3.6/site-packages/hitcount/locale/ru/LC_MESSAGES/django.mo View File


+ 51
- 0
thesisenv/lib/python3.6/site-packages/hitcount/locale/ru/LC_MESSAGES/django.po View File

@@ -0,0 +1,51 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-07-08 12:52+0700\n"
"PO-Revision-Date: 2013-07-08 13:31+0700\n"
"Last-Translator: Basil Shubin <basil.shubin@gmail.com>\n"
"Language-Team: <RU@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 1.5.4\n"

#: models.py:87
msgid "hit count"
msgstr "счетчик просмотров"

#: models.py:88
msgid "hit counts"
msgstr "просмотры"

#: models.py:144
msgid "hit"
msgstr "хит"

#: models.py:145
msgid "hits"
msgstr "хиты"

#: models.py:183
msgid "Blacklisted IP"
msgstr "бан"

#: models.py:184
msgid "Blacklisted IPs"
msgstr "черный список (IP)"

#: models.py:195
msgid "Blacklisted User Agent"
msgstr "бан"

#: models.py:196
msgid "Blacklisted User Agents"
msgstr "черный список (UA)"

+ 0
- 0
thesisenv/lib/python3.6/site-packages/hitcount/management/__init__.py View File


+ 0
- 0
thesisenv/lib/python3.6/site-packages/hitcount/management/commands/__init__.py View File


+ 32
- 0
thesisenv/lib/python3.6/site-packages/hitcount/management/commands/hitcount_cleanup.py View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from datetime import timedelta

from django.conf import settings
from django.utils import timezone

try:
from django.core.management.base import BaseCommand
except ImportError:
from django.core.management.base import NoArgsCommand as BaseCommand

from hitcount.models import Hit


class Command(BaseCommand):
help = "Can be run as a cronjob or directly to clean out old Hits objects from the database."

def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)

def handle(self, *args, **kwargs):
self.handle_noargs()

def handle_noargs(self, **options):
grace = getattr(settings, 'HITCOUNT_KEEP_HIT_IN_DATABASE', {'days': 30})
period = timezone.now() - timedelta(**grace)
qs = Hit.objects.filter(created__lt=period)
number_removed = qs.count()
qs.delete()
self.stdout.write('Successfully removed %s Hits' % number_removed)

+ 45
- 0
thesisenv/lib/python3.6/site-packages/hitcount/managers.py View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from datetime import timedelta

from django.db import models
from django.conf import settings
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType


class HitCountManager(models.Manager):

def get_for_object(self, obj):
ctype = ContentType.objects.get_for_model(obj)
hit_count, created = self.get_or_create(
content_type=ctype, object_pk=obj.pk)
return hit_count


class HitManager(models.Manager):

def filter_active(self, *args, **kwargs):
"""
Return only the 'active' hits.

How you count a hit/view will depend on personal choice: Should the
same user/visitor *ever* be counted twice? After a week, or a month,
or a year, should their view be counted again?

The defaulf is to consider a visitor's hit still 'active' if they
return within a the last seven days.. After that the hit
will be counted again. So if one person visits once a week for a year,
they will add 52 hits to a given object.

Change how long the expiration is by adding to settings.py:

HITCOUNT_KEEP_HIT_ACTIVE = {'days' : 30, 'minutes' : 30}

Accepts days, seconds, microseconds, milliseconds, minutes,
hours, and weeks. It's creating a datetime.timedelta object.

"""
grace = getattr(settings, 'HITCOUNT_KEEP_HIT_ACTIVE', {'days': 7})
period = timezone.now() - timedelta(**grace)
return self.filter(created__gte=period).filter(*args, **kwargs)

+ 94
- 0
thesisenv/lib/python3.6/site-packages/hitcount/migrations/0001_initial.py View File

@@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='BlacklistIP',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('ip', models.CharField(unique=True, max_length=40)),
],
options={
'db_table': 'hitcount_blacklist_ip',
'verbose_name': 'Blacklisted IP',
'verbose_name_plural': 'Blacklisted IPs',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='BlacklistUserAgent',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('user_agent', models.CharField(unique=True, max_length=255)),
],
options={
'db_table': 'hitcount_blacklist_user_agent',
'verbose_name': 'Blacklisted User Agent',
'verbose_name_plural': 'Blacklisted User Agents',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Hit',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created', models.DateTimeField(auto_now_add=True, db_index=True)),
('ip', models.CharField(max_length=40, editable=False)),
('session', models.CharField(max_length=40, editable=False)),
('user_agent', models.CharField(max_length=255, editable=False)),
],
options={
'ordering': ('-created',),
'get_latest_by': 'created',
'verbose_name': 'hit',
'verbose_name_plural': 'hits',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='HitCount',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('hits', models.PositiveIntegerField(default=0)),
('modified', models.DateTimeField(auto_now=True)),
('object_pk', models.PositiveIntegerField(verbose_name='object ID')),
('content_type', models.ForeignKey(related_name='content_type_set_for_hitcount',
to='contenttypes.ContentType', on_delete=models.CASCADE)),
],
options={
'get_latest_by': 'modified',
'ordering': ('-hits',),
'verbose_name_plural': 'hit counts',
'db_table': 'hitcount_hit_count',
'verbose_name': 'hit count',
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='hitcount',
unique_together=set([('content_type', 'object_pk')]),
),
migrations.AddField(
model_name='hit',
name='hitcount',
field=models.ForeignKey(editable=False, to='hitcount.HitCount', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AddField(
model_name='hit',
name='user',
field=models.ForeignKey(editable=False, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE),
preserve_default=True,
),
]

+ 29
- 0
thesisenv/lib/python3.6/site-packages/hitcount/migrations/0002_index_ip_and_session.py View File

@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('hitcount', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='hit',
name='ip',
field=models.CharField(max_length=40, db_index=True, editable=False),
),
migrations.AlterField(
model_name='hit',
name='session',
field=models.CharField(max_length=40, db_index=True, editable=False),
),
migrations.AlterField(
model_name='hitcount',
name='object_pk',
field=models.PositiveIntegerField(verbose_name='object ID'),
),
]

+ 0
- 0
thesisenv/lib/python3.6/site-packages/hitcount/migrations/__init__.py View File


+ 197
- 0
thesisenv/lib/python3.6/site-packages/hitcount/models.py View File

@@ -0,0 +1,197 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from datetime import timedelta

from django.db import models
from django.conf import settings
from django.db.models import F
from django.utils import timezone
from django.dispatch import receiver
from django.utils.encoding import python_2_unicode_compatible
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _

AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')

from .managers import HitCountManager, HitManager
from .signals import delete_hit_count


@receiver(delete_hit_count)
def delete_hit_count_handler(sender, instance, save_hitcount=False, **kwargs):
"""
Custom callback for the Hit.delete() method.

Hit.delete(): removes the hit from the associated HitCount object.
Hit.delete(save_hitcount=True): preserves the hit for the associated
HitCount object.

"""
if not save_hitcount:
instance.hitcount.decrease()


@python_2_unicode_compatible
class HitCount(models.Model):
"""
Model that stores the hit totals for any content object.

"""
hits = models.PositiveIntegerField(default=0)
modified = models.DateTimeField(auto_now=True)
content_type = models.ForeignKey(
ContentType, related_name="content_type_set_for_%(class)s", on_delete=models.CASCADE)
object_pk = models.PositiveIntegerField('object ID')
content_object = GenericForeignKey('content_type', 'object_pk')

objects = HitCountManager()

class Meta:
ordering = ('-hits',)
get_latest_by = "modified"
verbose_name = _("hit count")
verbose_name_plural = _("hit counts")
unique_together = ("content_type", "object_pk")
db_table = "hitcount_hit_count"

def __str__(self):
return '%s' % self.content_object

def increase(self):
self.hits = F('hits') + 1
self.save()

def decrease(self):
self.hits = F('hits') - 1
self.save()

def hits_in_last(self, **kwargs):
"""
Returns hit count for an object during a given time period.

This will only work for as long as hits are saved in the Hit database.
If you are purging your database after 45 days, for example, that means
that asking for hits in the last 60 days will return an incorrect
number as that the longest period it can search will be 45 days.

For example: hits_in_last(days=7).

Accepts days, seconds, microseconds, milliseconds, minutes,
hours, and weeks. It's creating a datetime.timedelta object.

"""
assert kwargs, "Must provide at least one timedelta arg (eg, days=1)"

period = timezone.now() - timedelta(**kwargs)
return self.hit_set.filter(created__gte=period).count()

# def get_content_object_url(self):
# """
# Django has this in its contrib.comments.model file -- seems worth
# implementing though it may take a couple steps.
#
# """
# pass


@python_2_unicode_compatible
class Hit(models.Model):
"""
Model captures a single Hit by a visitor.

None of the fields are editable because they are all dynamically created.
Browsing the Hit list in the Admin will allow one to blacklist both
IP addresses as well as User Agents. Blacklisting simply causes those
hits to not be counted or recorded.

Depending on how long you set the HITCOUNT_KEEP_HIT_ACTIVE, and how long
you want to be able to use `HitCount.hits_in_last(days=30)` you can choose
to clean up your Hit table by using the management `hitcount_cleanup`
management command.

"""
created = models.DateTimeField(editable=False, auto_now_add=True, db_index=True)
ip = models.CharField(max_length=40, editable=False, db_index=True)
session = models.CharField(max_length=40, editable=False, db_index=True)
user_agent = models.CharField(max_length=255, editable=False)
user = models.ForeignKey(AUTH_USER_MODEL, null=True, editable=False, on_delete=models.CASCADE)
hitcount = models.ForeignKey(HitCount, editable=False, on_delete=models.CASCADE)

objects = HitManager()

class Meta:
ordering = ('-created',)
get_latest_by = 'created'
verbose_name = _("hit")
verbose_name_plural = _("hits")

def __str__(self):
return 'Hit: %s' % self.pk

def save(self, *args, **kwargs):
"""
The first time the object is created and saved, we increment
the associated HitCount object by one. The opposite applies
if the Hit is deleted.

"""
if self.pk is None:
self.hitcount.increase()

super(Hit, self).save(*args, **kwargs)

def delete(self, save_hitcount=False):
"""
If a Hit is deleted and save_hitcount=True, it will preserve the
HitCount object's total. However, under normal circumstances, a
delete() will trigger a subtraction from the HitCount object's total.

NOTE: This doesn't work at all during a queryset.delete().

"""
delete_hit_count.send(
sender=self, instance=self, save_hitcount=save_hitcount)
super(Hit, self).delete()


@python_2_unicode_compatible
class BlacklistIP(models.Model):

ip = models.CharField(max_length=40, unique=True)

class Meta:
db_table = "hitcount_blacklist_ip"
verbose_name = _("Blacklisted IP")
verbose_name_plural = _("Blacklisted IPs")

def __str__(self):
return '%s' % self.ip


@python_2_unicode_compatible
class BlacklistUserAgent(models.Model):

user_agent = models.CharField(max_length=255, unique=True)

class Meta:
db_table = "hitcount_blacklist_user_agent"
verbose_name = _("Blacklisted User Agent")
verbose_name_plural = _("Blacklisted User Agents")

def __str__(self):
return '%s' % self.user_agent


class HitCountMixin(object):
"""
HitCountMixin provides an easy way to add a `hit_count` property to your
model that will return the related HitCount object.
"""

@property
def hit_count(self):
ctype = ContentType.objects.get_for_model(self.__class__)
hit_count, created = HitCount.objects.get_or_create(
content_type=ctype, object_pk=self.pk)
return hit_count

+ 5
- 0
thesisenv/lib/python3.6/site-packages/hitcount/signals.py View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.dispatch import Signal

delete_hit_count = Signal(providing_args=['save_hitcount'])

+ 60
- 0
thesisenv/lib/python3.6/site-packages/hitcount/static/hitcount/hitcount-jquery.js View File

@@ -0,0 +1,60 @@
$(document).ready(function() {
/**
* https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
*
* Remember you will need to ensure csrf tokens by adding:
* @ensure_csrf_cookie to your views that require this javascript
*
* Also, you will probably want to include this with your other sitewide
* javascript files ... this is just an example.
*/

if ( typeof hitcountJS === 'undefined' ) {
// since this is loaded on every page only do something
// if a hit is going to be counted
return;
}

var hitcountPK = hitcountJS['hitcountPK'];
var hitcountURL = hitcountJS['hitcountURL'];
var csrftoken = getCookie('csrftoken');

$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});

$.post( hitcountURL, { "hitcountPK" : hitcountPK },
function(data, status) {

console.log(data); // just so you can see the response

if (data.status == 'error') {
// do something for error?
}
}, 'json');
});

function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

+ 59
- 0
thesisenv/lib/python3.6/site-packages/hitcount/static/hitcount/jquery.postcsrf.js View File

@@ -0,0 +1,59 @@
/**
* Wrapper for jQuery's $.post() that retrieves the CSRF token from the browser
* cookie and sets then sets "X-CSRFToken" header in one fell swoop.
*
* Based on the example code given at the Django docs:
* https://docs.djangoproject.com/en/1.9/ref/csrf/#ajax
*
* Use as you would $.post().
*/

(function($) {

$.postCSRF = function(url, data, callback, type) {

function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}

var csrftoken = getCookie('csrftoken');

// shift arguments if data argument was omitted
if ($.isFunction(data)) {
type = type || callback;
callback = data;
data = undefined;
}

return $.ajax(jQuery.extend({
url: url,
type: "POST",
dataType: type,
data: data,
success: callback,
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
}, jQuery.isPlainObject(url) && url));
};

}(jQuery));

+ 0
- 0
thesisenv/lib/python3.6/site-packages/hitcount/templatetags/__init__.py View File


+ 319
- 0
thesisenv/lib/python3.6/site-packages/hitcount/templatetags/hitcount_tags.py View File

@@ -0,0 +1,319 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from collections import namedtuple

from django import template
from django.contrib.contenttypes.models import ContentType
try:
from django.core.urlresolvers import reverse
except ImportError:
from django.urls import reverse

from hitcount.models import HitCount

register = template.Library()


def get_hit_count_from_obj_variable(context, obj_variable, tag_name):
"""
Helper function to return a HitCount for a given template object variable.

Raises TemplateSyntaxError if the passed object variable cannot be parsed.
"""
error_to_raise = template.TemplateSyntaxError(
"'%(a)s' requires a valid individual model variable "
"in the form of '%(a)s for [model_obj]'.\n"
"Got: %(b)s" % {'a': tag_name, 'b': obj_variable}
)

try:
obj = obj_variable.resolve(context)
except template.VariableDoesNotExist:
raise error_to_raise

try:
ctype = ContentType.objects.get_for_model(obj)
except AttributeError:
raise error_to_raise

hit_count, created = HitCount.objects.get_or_create(
content_type=ctype, object_pk=obj.pk)

return hit_count


def return_period_from_string(arg):
"""
Takes a string such as "days=1,seconds=30" and strips the quotes
and returns a dictionary with the key/value pairs

"""
period = {}

if arg[0] == '"' and arg[-1] == '"':
opt = arg[1:-1] # remove quotes
else:
opt = arg

for o in opt.split(","):
key, value = o.split("=")
period[str(key)] = int(value)

return period


class GetHitCount(template.Node):

def handle_token(cls, parser, token):
args = token.contents.split()

# {% get_hit_count for [obj] %}
if len(args) == 3 and args[1] == 'for':
return cls(obj_as_str=args[2])

# {% get_hit_count for [obj] as [var] %}
elif len(args) == 5 and args[1] == 'for' and args[3] == 'as':
return cls(obj_as_str=args[2],
as_varname=args[4],)

# {% get_hit_count for [obj] within ["days=1,minutes=30"] %}
elif len(args) == 5 and args[1] == 'for' and args[3] == 'within':
return cls(obj_as_str=args[2],
period=return_period_from_string(args[4]))

# {% get_hit_count for [obj] within ["days=1,minutes=30"] as [var] %}
elif len(args) == 7 and args[1] == 'for' and \
args[3] == 'within' and args[5] == 'as':
return cls(obj_as_str=args[2],
as_varname=args[6],
period=return_period_from_string(args[4]))

else: # TODO - should there be more troubleshooting prior to bailing?
raise template.TemplateSyntaxError(
"'get_hit_count' requires "
"'for [object] in [period] as [var]' (got %r)" % args
)

handle_token = classmethod(handle_token)

def __init__(self, obj_as_str, as_varname=None, period=None):
self.obj_variable = template.Variable(obj_as_str)
self.as_varname = as_varname
self.period = period

def render(self, context):
hit_count = get_hit_count_from_obj_variable(context, self.obj_variable, 'get_hit_count')

if self.period: # if user sets a time period, use it
try:
hits = hit_count.hits_in_last(**self.period)
except TypeError:
raise template.TemplateSyntaxError(
"'get_hit_count for [obj] within [timedelta]' requires "
"a valid comma separated list of timedelta arguments. "
"For example, ['days=5,hours=6']. "
"Got these instead: %s" % self.period
)
else:
hits = hit_count.hits

if self.as_varname: # if user gives us a variable to return
context[self.as_varname] = str(hits)
return ''
else:
return str(hits)


def get_hit_count(parser, token):
"""
Returns hit counts for an object.

- Return total hits for an object:
{% get_hit_count for [object] %}

- Get total hits for an object as a specified variable:
{% get_hit_count for [object] as [var] %}

- Get total hits for an object over a certain time period:
{% get_hit_count for [object] within ["days=1,minutes=30"] %}

- Get total hits for an object over a certain time period as a variable:
{% get_hit_count for [object] within ["days=1,minutes=30"] as [var] %}

The time arguments need to follow datetime.timedelta's limitations:
Accepts days, seconds, microseconds, milliseconds, minutes,
hours, and weeks.

"""
return GetHitCount.handle_token(parser, token)

register.tag('get_hit_count', get_hit_count)


class WriteHitCountJavascriptVariables(template.Node):

def handle_token(cls, parser, token):
args = token.contents.split()

if len(args) == 3 and args[1] == 'for':
return cls(obj_variable=args[2])

else:
raise template.TemplateSyntaxError(
'insert_hit_count_js_variables requires this syntax: '
'"insert_hit_count_js_variables for [object]"\n'
'Got: %s' % ' '.join(str(i) for i in args)
)

handle_token = classmethod(handle_token)

def __init__(self, obj_variable):
self.obj_variable = template.Variable(obj_variable)

def render(self, context):
hit_count = get_hit_count_from_obj_variable(context, self.obj_variable, 'insert_hit_count_js_variables')

js = '<script type="text/javascript">\n' + \
"var hitcountJS = {" + \
"hitcountPK : '" + str(hit_count.pk) + "'," + \
"hitcountURL : '" + str(reverse('hitcount:hit_ajax')) + "'};" + \
"\n</script>"

return js


def insert_hit_count_js_variables(parser, token):
"""
Injects JavaScript global variables into your template. These variables
can be used in your JavaScript files to send the correctly mapped HitCount
ID to the server (see: hitcount-jquery.js for an example).

{% insert_hit_count_js_variables for [object] %}
"""
return WriteHitCountJavascriptVariables.handle_token(parser, token)

register.tag('insert_hit_count_js_variables', insert_hit_count_js_variables)


class GetHitCountJavascriptVariables(template.Node):

def handle_token(cls, parser, token):
args = token.contents.split()

if len(args) == 5 and args[1] == 'for' and args[3] == 'as':
return cls(obj_variable=args[2], as_varname=args[4])

else:
raise template.TemplateSyntaxError(
'get_hit_count_js_variables requires this syntax: '
'"get_hit_count_js_variables for [object] as [var_name]."\n'
'Got: %s' % ' '.join(str(i) for i in args)
)

handle_token = classmethod(handle_token)

def __init__(self, obj_variable, as_varname):
self.obj_variable = template.Variable(obj_variable)
self.as_varname = as_varname

def render(self, context):
HitcountVariables = namedtuple('HitcountVariables', 'pk ajax_url hits')

hit_count = get_hit_count_from_obj_variable(context, self.obj_variable, 'get_hit_count_js_variables')

context[self.as_varname] = HitcountVariables(
hit_count.pk, str(reverse('hitcount:hit_ajax')), str(hit_count.hits))

return ''


def get_hit_count_js_variables(parser, token):
"""
Injects JavaScript global variables into your template. These variables
can be used in your JavaScript files to send the correctly mapped HitCount
ID to the server (see: hitcount-jquery.js for an example).

{% get_hit_count_js_variables for [object] as [var_name] %}

Will provide two variables:
[var_name].pk = the hitcount pk to be sent via JavaScript
[var_name].ajax_url = the relative url to post the ajax request to
"""
return GetHitCountJavascriptVariables.handle_token(parser, token)

register.tag('get_hit_count_js_variables', get_hit_count_js_variables)


class WriteHitCountJavascript(template.Node):

JS_TEMPLATE = """
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function($) {
$.postCSRF("%s", {
hitcountPK: "%s"
});
});
//]]>
</script>
"""

JS_TEMPLATE_DEBUG = """
<script type="text/javascript">
//<![CDATA[
jQuery(document).ready(function($) {
$.postCSRF("%s", {
hitcountPK: "%s"
}).done(function(data) {
console.log('django-hitcount: AJAX POST succeeded.');
console.log(data);
}).fail(function(data) {
console.log('django-hitcount: AJAX POST failed.');
console.log(data);
});
});
//]]>
</script>
"""

def handle_token(cls, parser, token):
args = token.contents.split()

if len(args) == 3 and args[1] == 'for':
return cls(obj_variable=args[2], debug=False)
elif len(args) == 4 and args[1] == 'for' and args[3] == 'debug':
return cls(obj_variable=args[2], debug=True)
else:
raise template.TemplateSyntaxError(
'insert_hit_count_js requires this syntax: '
'"insert_hit_count_js for [object]"\n'
'"insert_hit_count_js for [object] debug"'
'Got: %s' % ' '.join(str(i) for i in args)
)

handle_token = classmethod(handle_token)

def __init__(self, obj_variable, debug):
self.obj_variable = template.Variable(obj_variable)
self.debug = debug

def render(self, context):
hit_count = get_hit_count_from_obj_variable(
context,
self.obj_variable,
'insert_hit_count_js'
)
template = self.JS_TEMPLATE_DEBUG if self.debug else self.JS_TEMPLATE
return template % (str(reverse('hitcount:hit_ajax')), str(hit_count.pk))


def insert_hit_count_js(parser, token):
"""
Injects the JavaScript into your template that works with jquery.postcsrf.js.

{% insert_hit_count_js_variables for [object] %}
"""
return WriteHitCountJavascript.handle_token(parser, token)


register.tag('insert_hit_count_js', insert_hit_count_js)

+ 12
- 0
thesisenv/lib/python3.6/site-packages/hitcount/urls.py View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.conf.urls import url

from hitcount.views import HitCountJSONView

app_name = 'hitcount'

urlpatterns = [
url(r'^hit/ajax/$', HitCountJSONView.as_view(), name='hit_ajax'),
]

+ 47
- 0
thesisenv/lib/python3.6/site-packages/hitcount/utils.py View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
import warnings


# this is not intended to be an all-knowing IP address regex
IP_RE = re.compile('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')


def get_ip(request):
"""
Retrieves the remote IP address from the request data. If the user is
behind a proxy, they may have a comma-separated list of IP addresses, so
we need to account for that. In such a case, only the first IP in the
list will be retrieved. Also, some hosts that use a proxy will put the
REMOTE_ADDR into HTTP_X_FORWARDED_FOR. This will handle pulling back the
IP from the proper place.

**NOTE** This function was taken from django-tracking (MIT LICENSE)
http://code.google.com/p/django-tracking/
"""

# if neither header contain a value, just use local loopback
ip_address = request.META.get('HTTP_X_FORWARDED_FOR',
request.META.get('REMOTE_ADDR', '127.0.0.1'))
if ip_address:
# make sure we have one and only one IP
try:
ip_address = IP_RE.match(ip_address)
if ip_address:
ip_address = ip_address.group(0)
else:
# no IP, probably from some dirty proxy or other device
# throw in some bogus IP
ip_address = '10.0.0.1'
except IndexError:
pass

return ip_address


class RemovedInHitCount13Warning(DeprecationWarning):
pass

# enable warnings by default for our deprecated
warnings.simplefilter("default", RemovedInHitCount13Warning)

+ 189
- 0
thesisenv/lib/python3.6/site-packages/hitcount/views.py View File

@@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-

import warnings
from collections import namedtuple

from django.http import Http404, JsonResponse, HttpResponseBadRequest
from django.conf import settings
from django.views.generic import View, DetailView

from hitcount.utils import get_ip
from hitcount.models import Hit, HitCount, BlacklistIP, BlacklistUserAgent
from hitcount.utils import RemovedInHitCount13Warning


class HitCountMixin(object):
"""
Mixin to evaluate a HttpRequest and a HitCount and determine whether or not
the HitCount should be incremented and the Hit recorded.
"""

@classmethod
def hit_count(self, request, hitcount):
"""
Called with a HttpRequest and HitCount object it will return a
namedtuple:

UpdateHitCountResponse(hit_counted=Boolean, hit_message='Message').

`hit_counted` will be True if the hit was counted and False if it was
not. `'hit_message` will indicate by what means the Hit was either
counted or ignored.
"""
UpdateHitCountResponse = namedtuple(
'UpdateHitCountResponse', 'hit_counted hit_message')

# as of Django 1.8.4 empty sessions are not being saved
# https://code.djangoproject.com/ticket/25489
if request.session.session_key is None:
request.session.save()

user = request.user
try:
is_authenticated_user = user.is_authenticated()
except:
is_authenticated_user = user.is_authenticated
session_key = request.session.session_key
ip = get_ip(request)
user_agent = request.META.get('HTTP_USER_AGENT', '')[:255]
hits_per_ip_limit = getattr(settings, 'HITCOUNT_HITS_PER_IP_LIMIT', 0)
exclude_user_group = getattr(settings, 'HITCOUNT_EXCLUDE_USER_GROUP', None)

# first, check our request against the IP blacklist
if BlacklistIP.objects.filter(ip__exact=ip):
return UpdateHitCountResponse(
False, 'Not counted: user IP has been blacklisted')

# second, check our request against the user agent blacklist
if BlacklistUserAgent.objects.filter(user_agent__exact=user_agent):
return UpdateHitCountResponse(
False, 'Not counted: user agent has been blacklisted')

# third, see if we are excluding a specific user group or not
if exclude_user_group and is_authenticated_user:
if user.groups.filter(name__in=exclude_user_group):
return UpdateHitCountResponse(
False, 'Not counted: user excluded by group')

# eliminated first three possible exclusions, now on to checking our database of
# active hits to see if we should count another one

# start with a fresh active query set (HITCOUNT_KEEP_HIT_ACTIVE)
qs = Hit.objects.filter_active()

# check limit on hits from a unique ip address (HITCOUNT_HITS_PER_IP_LIMIT)
if hits_per_ip_limit:
if qs.filter(ip__exact=ip).count() >= hits_per_ip_limit:
return UpdateHitCountResponse(
False, 'Not counted: hits per IP address limit reached')

# create a generic Hit object with request data
hit = Hit(session=session_key, hitcount=hitcount, ip=get_ip(request),
user_agent=request.META.get('HTTP_USER_AGENT', '')[:255],)

# first, use a user's authentication to see if they made an earlier hit
if is_authenticated_user:
if not qs.filter(user=user, hitcount=hitcount):
hit.user = user # associate this hit with a user
hit.save()

response = UpdateHitCountResponse(
True, 'Hit counted: user authentication')
else:
response = UpdateHitCountResponse(
False, 'Not counted: authenticated user has active hit')

# if not authenticated, see if we have a repeat session
else:
if not qs.filter(session=session_key, hitcount=hitcount):
hit.save()
response = UpdateHitCountResponse(
True, 'Hit counted: session key')
else:
response = UpdateHitCountResponse(
False, 'Not counted: session key has active hit')

return response


class HitCountJSONView(View, HitCountMixin):
"""
JSON response view to handle HitCount POST.
"""

def dispatch(self, request, *args, **kwargs):
if not request.is_ajax():
raise Http404()
return super(HitCountJSONView, self).dispatch(request, *args, **kwargs)

def get(self, request, *args, **kwargs):
msg = "Hits counted via POST only."
return JsonResponse({'success': False, 'error_message': msg})

def post(self, request, *args, **kwargs):
hitcount_pk = request.POST.get('hitcountPK')

try:
hitcount = HitCount.objects.get(pk=hitcount_pk)
except:
return HttpResponseBadRequest("HitCount object_pk not working")

hit_count_response = self.hit_count(request, hitcount)
return JsonResponse(hit_count_response._asdict())


class HitCountDetailView(DetailView, HitCountMixin):
"""
HitCountDetailView provides an inherited DetailView that will inject the
template context with a `hitcount` variable giving you the number of
Hits for an object without using a template tag.

Optionally, by setting `count_hit = True` you can also do the business of
counting the Hit for this object (in lieu of using JavaScript). It will
then further inject the response from the attempt to count the Hit into
the template context.
"""
count_hit = False

def get_context_data(self, **kwargs):
context = super(HitCountDetailView, self).get_context_data(**kwargs)
if self.object:
hit_count = HitCount.objects.get_for_object(self.object)
hits = hit_count.hits
context['hitcount'] = {'pk': hit_count.pk}

if self.count_hit:
hit_count_response = self.hit_count(self.request, hit_count)
if hit_count_response.hit_counted:
hits = hits + 1
context['hitcount']['hit_counted'] = hit_count_response.hit_counted
context['hitcount']['hit_message'] = hit_count_response.hit_message

context['hitcount']['total_hits'] = hits

return context


def _update_hit_count(request, hitcount):
"""
Deprecated in 1.2. Use hitcount.views.Hit CountMixin.hit_count() instead.
"""
warnings.warn(
"hitcount.views._update_hit_count is deprecated. "
"Use hitcount.views.HitCountMixin.hit_count() instead.",
RemovedInHitCount13Warning
)
return HitCountMixin.hit_count(request, hitcount)


def update_hit_count_ajax(request, *args, **kwargs):
"""
Deprecated in 1.2. Use hitcount.views.HitCountJSONView instead.
"""
warnings.warn(
"hitcount.views.update_hit_count_ajax is deprecated. "
"Use hitcount.views.HitCountJSONView instead.",
RemovedInHitCount13Warning
)
view = HitCountJSONView.as_view()
return view(request, *args, **kwargs)

Loading…
Cancel
Save