Init commit
This commit is contained in:
parent
f05e13ff62
commit
6418a5206e
|
@ -0,0 +1,38 @@
|
||||||
|
sudo: false
|
||||||
|
language: python
|
||||||
|
|
||||||
|
python:
|
||||||
|
- 2.7
|
||||||
|
- 3.2
|
||||||
|
- 3.3
|
||||||
|
- 3.4
|
||||||
|
- 3.5
|
||||||
|
|
||||||
|
|
||||||
|
install:
|
||||||
|
- pip install Django${DJANGO_VERSION}
|
||||||
|
|
||||||
|
env:
|
||||||
|
matrix:
|
||||||
|
- DJANGO_VERSION=">=1.6,<1.7"
|
||||||
|
- DJANGO_VERSION=">=1.7,<1.8"
|
||||||
|
- DJANGO_VERSION=">=1.8,<1.9"
|
||||||
|
- DJANGO_VERSION=">=1.9,<1.10"
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
exclude:
|
||||||
|
- python: 3.5
|
||||||
|
env: DJANGO_VERSION=">=1.6,<1.7"
|
||||||
|
- python: 3.5
|
||||||
|
env: DJANGO_VERSION=">=1.7,<1.8"
|
||||||
|
- python: 3.3
|
||||||
|
env: DJANGO_VERSION=">=1.9,<1.10"
|
||||||
|
- python: 3.2
|
||||||
|
env: DJANGO_VERSION=">=1.9,<1.10"
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
|
script:
|
||||||
|
- python runtests.py
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Dmitriy Sokolov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,5 @@
|
||||||
|
include LICENSE CHANGES README.rst
|
||||||
|
recursive-include rangefilter/static *.js *.css *.png *.eot *.svg *.ttf *.woff
|
||||||
|
recursive-include rangefilter/templates *.html
|
||||||
|
recursive-exclude * __pycache__
|
||||||
|
recursive-exclude * *.py[co]
|
|
@ -0,0 +1,58 @@
|
||||||
|
.. image:: https://travis-ci.org/silentsokolov/django-admin-rangefilter.png?branch=master
|
||||||
|
:target: https://travis-ci.org/silentsokolov/django-admin-rangefilter
|
||||||
|
|
||||||
|
|
||||||
|
django-admin-rangefilter
|
||||||
|
========================
|
||||||
|
|
||||||
|
django-admin-rangefilter app, add the filter by a custom date range on the admin UI.
|
||||||
|
|
||||||
|
.. image:: https://raw.githubusercontent.com/silentsokolov/django-admin-rangefilter/master/docs/images/screenshot.png
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Python 2.7+ or Python 3.2+
|
||||||
|
* Django 1.7+
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
Use your favorite Python package manager to install the app from PyPI, e.g.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``pip install django-admin-rangefilter``
|
||||||
|
|
||||||
|
|
||||||
|
Add ``rangefilter`` to ``INSTALLED_APPS``:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
...
|
||||||
|
'rangefilter',
|
||||||
|
...
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Example usage
|
||||||
|
-------------
|
||||||
|
|
||||||
|
In admin
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from rangefilter.filtres import DateRangeFilter
|
||||||
|
|
||||||
|
@admin.register(Post)
|
||||||
|
class PostAdmin(admin.ModelAdmin):
|
||||||
|
list_filter = (
|
||||||
|
('created_at', DateRangeFilter), ('updated_at', DateRangeFilter),
|
||||||
|
)
|
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
|
@ -0,0 +1,10 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
__author__ = 'Dmitriy Sokolov'
|
||||||
|
__version__ = '0.1.0'
|
||||||
|
|
||||||
|
|
||||||
|
default_app_config = 'rangefilter.apps.RangeFilterConfig'
|
|
@ -0,0 +1,11 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class RangeFilterConfig(AppConfig):
|
||||||
|
name = 'rangefilter'
|
||||||
|
verbose_name = _('Range Filter')
|
|
@ -0,0 +1,127 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import django
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
|
from django.template.defaultfilters import slugify
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pytz
|
||||||
|
except ImportError:
|
||||||
|
pytz = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
from suit.widgets import SuitDateWidget as AdminDateWidget
|
||||||
|
except ImportError:
|
||||||
|
from django.contrib.admin.widgets import AdminDateWidget
|
||||||
|
|
||||||
|
|
||||||
|
def make_dt_aware(dt):
|
||||||
|
if pytz is not None and settings.USE_TZ:
|
||||||
|
timezone = pytz.timezone(settings.TIME_ZONE)
|
||||||
|
if dt.tzinfo is not None:
|
||||||
|
dt = timezone.normalize(dt)
|
||||||
|
else:
|
||||||
|
dt = timezone.localize(dt)
|
||||||
|
return dt
|
||||||
|
|
||||||
|
|
||||||
|
class DateRangeFilter(admin.filters.FieldListFilter):
|
||||||
|
def __init__(self, field, request, params, model, model_admin, field_path):
|
||||||
|
self.lookup_kwarg_gte = '{}__gte'.format(field_path)
|
||||||
|
self.lookup_kwarg_lte = '{}__lte'.format(field_path)
|
||||||
|
|
||||||
|
super(DateRangeFilter, self).__init__(field, request, params, model, model_admin, field_path)
|
||||||
|
|
||||||
|
self.form = self.get_form(request)
|
||||||
|
|
||||||
|
def choices(self, cl):
|
||||||
|
yield {
|
||||||
|
'system_name': slugify(self.title),
|
||||||
|
'query_string': cl.get_query_string(
|
||||||
|
{}, remove=[self.lookup_kwarg_gte, self.lookup_kwarg_lte]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def expected_parameters(self):
|
||||||
|
return [self.lookup_kwarg_gte, self.lookup_kwarg_lte]
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
if self.form.is_valid():
|
||||||
|
filter_params = dict(filter(lambda f: f[1] is not None, self.form.cleaned_data.items()))
|
||||||
|
|
||||||
|
if filter_params:
|
||||||
|
_filter = {
|
||||||
|
'{0}__range'.format(self.field_path): (
|
||||||
|
make_dt_aware(datetime.datetime.combine(
|
||||||
|
filter_params[self.lookup_kwarg_gte], datetime.time.min
|
||||||
|
)),
|
||||||
|
make_dt_aware(datetime.datetime.combine(
|
||||||
|
filter_params[self.lookup_kwarg_lte], datetime.time.max
|
||||||
|
))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryset.filter(**_filter)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def get_template(self):
|
||||||
|
if django.VERSION >= (1, 9):
|
||||||
|
return 'admin/daterange_filter19.html'
|
||||||
|
return 'admin/daterange_filter.html'
|
||||||
|
|
||||||
|
template = property(get_template)
|
||||||
|
|
||||||
|
def get_form(self, request):
|
||||||
|
form_class = self._get_form_class()
|
||||||
|
return form_class(self.used_parameters)
|
||||||
|
|
||||||
|
def _get_form_class(self):
|
||||||
|
fields = self._get_form_fields()
|
||||||
|
|
||||||
|
form_class = type(
|
||||||
|
str('DateRangeForm'),
|
||||||
|
(forms.BaseForm,),
|
||||||
|
{'base_fields': fields}
|
||||||
|
)
|
||||||
|
form_class.media = self._get_media()
|
||||||
|
|
||||||
|
return form_class
|
||||||
|
|
||||||
|
def _get_form_fields(self):
|
||||||
|
return OrderedDict((
|
||||||
|
(self.lookup_kwarg_gte, forms.DateField(
|
||||||
|
label='',
|
||||||
|
widget=AdminDateWidget(attrs={'placeholder': _('From date')}),
|
||||||
|
localize=True,
|
||||||
|
required=False
|
||||||
|
)),
|
||||||
|
(self.lookup_kwarg_lte, forms.DateField(
|
||||||
|
label='',
|
||||||
|
widget=AdminDateWidget(attrs={'placeholder': _('To date')}),
|
||||||
|
localize=True,
|
||||||
|
required=False
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_media():
|
||||||
|
js = [
|
||||||
|
'calendar.js',
|
||||||
|
'admin/DateTimeShortcuts.js',
|
||||||
|
]
|
||||||
|
css = [
|
||||||
|
'widgets.css',
|
||||||
|
]
|
||||||
|
return forms.Media(
|
||||||
|
js=['admin/js/%s' % url for url in js],
|
||||||
|
css={'all': ['admin/css/%s' % path for path in css]}
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
|
@ -0,0 +1,70 @@
|
||||||
|
{% load i18n admin_static %}
|
||||||
|
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
|
||||||
|
<style>
|
||||||
|
.button, input[type=reset] {
|
||||||
|
background: #79aec8;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.admindatefilter {
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
}
|
||||||
|
.admindatefilter p {
|
||||||
|
padding-left: 0px;
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.admindatefilter .timezonewarning {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.admindatefilter .datetimeshortcuts a:first-child {
|
||||||
|
margin-right: 4px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.calendarbox, .clockbox {
|
||||||
|
z-index: 1100;
|
||||||
|
margin-left: -16em !important;
|
||||||
|
margin-top: 9em !important;
|
||||||
|
}
|
||||||
|
.admindatefilter .datetimeshortcuts {
|
||||||
|
font-size: 0;
|
||||||
|
float: right;
|
||||||
|
position: absolute;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
|
.admindatefilter a {
|
||||||
|
color: #999;
|
||||||
|
position: absolute;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function datefilter_apply(event, qs_name, form_name){
|
||||||
|
event.preventDefault();
|
||||||
|
var query_string = django.jQuery('input#'+qs_name).val();
|
||||||
|
var form_data = django.jQuery('#'+form_name).serialize();
|
||||||
|
window.location = window.location.pathname + query_string + '&' + form_data;
|
||||||
|
}
|
||||||
|
function datefilter_reset(qs_name){
|
||||||
|
var query_string = django.jQuery('input#'+qs_name).val();
|
||||||
|
window.location = window.location.pathname + query_string;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<div class="admindatefilter">
|
||||||
|
<form method="GET" action="." id="{{ choices.0.system_name }}-form">
|
||||||
|
{{ spec.form.media }}
|
||||||
|
{{ spec.form.as_p }}
|
||||||
|
{% for choice in choices %}
|
||||||
|
<input type="hidden" id="{{ choice.system_name }}-query-string" value="{{ choice.query_string }}">
|
||||||
|
{% endfor %}
|
||||||
|
<div class="controls">
|
||||||
|
<input type="submit" value="{% trans "Search" %}" onclick="datefilter_apply(event, '{{ choices.0.system_name }}-query-string', '{{ choices.0.system_name }}-form')">
|
||||||
|
<input type="reset" class="button" value="{% trans "Reset" %}" onclick="datefilter_reset('{{ choices.0.system_name }}-query-string')">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -0,0 +1,62 @@
|
||||||
|
{% load i18n admin_static %}
|
||||||
|
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
|
||||||
|
<style>
|
||||||
|
.button, input[type=reset] {
|
||||||
|
padding: 3px 5px;
|
||||||
|
color: black;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-color: #ddd #aaa #aaa #ddd;
|
||||||
|
}
|
||||||
|
.admindatefilter {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
.admindatefilter .timezonewarning {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.admindatefilter .datetimeshortcuts a:first-child {
|
||||||
|
margin-right: 4px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.calendarbox, .clockbox {
|
||||||
|
z-index: 1100;
|
||||||
|
margin-left: -16em !important;
|
||||||
|
margin-top: 9em !important;
|
||||||
|
}
|
||||||
|
.admindatefilter .datetimeshortcuts {
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
.admindatefilter a {
|
||||||
|
color: #999;
|
||||||
|
position: absolute;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-left: 4px;
|
||||||
|
}
|
||||||
|
.admindatefilter br {
|
||||||
|
content: ""
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function datefilter_apply(event, qs_name, form_name){
|
||||||
|
event.preventDefault();
|
||||||
|
var query_string = django.jQuery('input#'+qs_name).val();
|
||||||
|
var form_data = django.jQuery('#'+form_name).serialize();
|
||||||
|
window.location = window.location.pathname + query_string + '&' + form_data;
|
||||||
|
}
|
||||||
|
function datefilter_reset(qs_name){
|
||||||
|
var query_string = django.jQuery('input#'+qs_name).val();
|
||||||
|
window.location = window.location.pathname + query_string;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<div class="admindatefilter">
|
||||||
|
<form method="GET" action="." id="{{ choices.0.system_name }}-form">
|
||||||
|
{{ spec.form.media }}
|
||||||
|
{{ spec.form }}
|
||||||
|
{% for choice in choices %}
|
||||||
|
<input type="hidden" id="{{ choice.system_name }}-query-string" value="{{ choice.query_string }}">
|
||||||
|
{% endfor %}
|
||||||
|
<div class="controls">
|
||||||
|
<input type="submit" value="{% trans "Search" %}" onclick="datefilter_apply(event, '{{ choices.0.system_name }}-query-string', '{{ choices.0.system_name }}-form')">
|
||||||
|
<input type="reset" class="button" value="{% trans "Reset" %}" onclick="datefilter_reset('{{ choices.0.system_name }}-query-string')">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -0,0 +1,105 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.test import RequestFactory, TestCase
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.admin import ModelAdmin, site
|
||||||
|
from django.contrib.admin.views.main import ChangeList
|
||||||
|
from django.utils.encoding import force_text
|
||||||
|
|
||||||
|
from .filter import make_dt_aware, DateRangeFilter
|
||||||
|
|
||||||
|
|
||||||
|
class MyModel(models.Model):
|
||||||
|
created_at = models.DateTimeField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ('created_at',)
|
||||||
|
|
||||||
|
|
||||||
|
class MyModelAdmin(ModelAdmin):
|
||||||
|
list_filter = (('created_at', DateRangeFilter),)
|
||||||
|
ordering = ('-id',)
|
||||||
|
|
||||||
|
|
||||||
|
def select_by(dictlist):
|
||||||
|
return [x for x in dictlist][0]
|
||||||
|
|
||||||
|
|
||||||
|
class DateFuncTestCase(TestCase):
|
||||||
|
def test_make_dt_aware_without_pytz(self):
|
||||||
|
with override_settings(USE_TZ=False):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
date = make_dt_aware(now)
|
||||||
|
|
||||||
|
self.assertEqual(date.tzinfo, None)
|
||||||
|
self.assertTrue(timezone.is_naive(date))
|
||||||
|
|
||||||
|
def test_make_dt_aware_with_pytz(self):
|
||||||
|
local_tz = timezone.get_current_timezone()
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
date = make_dt_aware(now)
|
||||||
|
|
||||||
|
self.assertEqual(date.tzinfo.zone, local_tz.zone)
|
||||||
|
self.assertTrue(timezone.is_aware(date))
|
||||||
|
|
||||||
|
now = timezone.now()
|
||||||
|
date = make_dt_aware(now)
|
||||||
|
|
||||||
|
self.assertEqual(date.tzinfo.zone, local_tz.zone)
|
||||||
|
self.assertTrue(timezone.is_aware(date))
|
||||||
|
|
||||||
|
|
||||||
|
class DateRangeFilterTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.today = datetime.date.today()
|
||||||
|
self.tomorrow = self.today + datetime.timedelta(days=1)
|
||||||
|
self.one_week_ago = self.today - datetime.timedelta(days=7)
|
||||||
|
|
||||||
|
self.django_book = MyModel.objects.create(created_at=self.today)
|
||||||
|
self.djangonaut_book = MyModel.objects.create(created_at=self.one_week_ago)
|
||||||
|
|
||||||
|
def get_changelist(self, request, model, modeladmin):
|
||||||
|
return ChangeList(
|
||||||
|
request, model, modeladmin.list_display,
|
||||||
|
modeladmin.list_display_links, modeladmin.list_filter,
|
||||||
|
modeladmin.date_hierarchy, modeladmin.search_fields,
|
||||||
|
modeladmin.list_select_related, modeladmin.list_per_page,
|
||||||
|
modeladmin.list_max_show_all, modeladmin.list_editable, modeladmin,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_datefilter(self):
|
||||||
|
self.request_factory = RequestFactory()
|
||||||
|
modeladmin = MyModelAdmin(MyModel, site)
|
||||||
|
|
||||||
|
request = self.request_factory.get('/')
|
||||||
|
changelist = self.get_changelist(request, MyModel, modeladmin)
|
||||||
|
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
|
||||||
|
self.assertEqual(list(queryset), [self.djangonaut_book, self.django_book])
|
||||||
|
filterspec = changelist.get_filters(request)[0][0]
|
||||||
|
self.assertEqual(force_text(filterspec.title), 'created at')
|
||||||
|
|
||||||
|
def test_datefilter_filtered(self):
|
||||||
|
self.request_factory = RequestFactory()
|
||||||
|
modeladmin = MyModelAdmin(MyModel, site)
|
||||||
|
|
||||||
|
request = self.request_factory.get('/', {'created_at__gte': self.today,
|
||||||
|
'created_at__lte': self.tomorrow})
|
||||||
|
changelist = self.get_changelist(request, MyModel, modeladmin)
|
||||||
|
|
||||||
|
queryset = changelist.get_queryset(request)
|
||||||
|
|
||||||
|
self.assertEqual(list(queryset), [self.django_book])
|
||||||
|
filterspec = changelist.get_filters(request)[0][0]
|
||||||
|
self.assertEqual(force_text(filterspec.title), 'created at')
|
||||||
|
|
||||||
|
choice = select_by(filterspec.choices(changelist))
|
||||||
|
self.assertEqual(choice['query_string'], '?')
|
||||||
|
self.assertEqual(choice['system_name'], 'created-at')
|
|
@ -0,0 +1,35 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
|
from django.conf import settings, global_settings
|
||||||
|
from django.core.management import call_command
|
||||||
|
|
||||||
|
|
||||||
|
settings.configure(
|
||||||
|
MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES,
|
||||||
|
INSTALLED_APPS=(
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.contenttypes',
|
||||||
|
'django.contrib.sites',
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'rangefilter',
|
||||||
|
),
|
||||||
|
DATABASES={
|
||||||
|
'default': {'ENGINE': 'django.db.backends.sqlite3'}
|
||||||
|
},
|
||||||
|
TEST_RUNNER='django.test.runner.DiscoverRunner',
|
||||||
|
USE_TZ=True
|
||||||
|
)
|
||||||
|
|
||||||
|
from django.test.utils import setup_test_environment
|
||||||
|
setup_test_environment()
|
||||||
|
|
||||||
|
if django.VERSION > (1, 7):
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
call_command('test', 'rangefilter')
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
def get_packages(package):
|
||||||
|
return [dirpath for dirpath, dirnames, filenames in os.walk(package)
|
||||||
|
if os.path.exists(os.path.join(dirpath, '__init__.py'))]
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_data(package):
|
||||||
|
walk = [(dirpath.replace(package + os.sep, '', 1), filenames)
|
||||||
|
for dirpath, dirnames, filenames in os.walk(package)
|
||||||
|
if not os.path.exists(os.path.join(dirpath, '__init__.py'))]
|
||||||
|
|
||||||
|
filepaths = []
|
||||||
|
for base, filenames in walk:
|
||||||
|
filepaths.extend([os.path.join(base, filename)
|
||||||
|
for filename in filenames])
|
||||||
|
return {package: filepaths}
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='django-admin-rangefilter',
|
||||||
|
version='0.1.0',
|
||||||
|
url='https://github.com/silentsokolov/django-admin-rangefilter',
|
||||||
|
license='MIT',
|
||||||
|
author='Dmitriy Sokolov',
|
||||||
|
author_email='silentsokolov@gmail.com',
|
||||||
|
description='django-admin-rangefilter app, add the filter by a custom date range on the admin UI.',
|
||||||
|
zip_safe=False,
|
||||||
|
include_package_data=True,
|
||||||
|
platforms='any',
|
||||||
|
packages=get_packages('rangefilter'),
|
||||||
|
package_data=get_package_data('rangefilter'),
|
||||||
|
install_requires=[],
|
||||||
|
classifiers=[
|
||||||
|
'Development Status :: 4 - Beta',
|
||||||
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'Environment :: Web Environment',
|
||||||
|
'Framework :: Django',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'License :: OSI Approved :: MIT License',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Topic :: Utilities',
|
||||||
|
],
|
||||||
|
)
|
Loading…
Reference in New Issue