initial commit (save point)
This commit is contained in:
commit
81f6b57a63
5
CHANGES.txt
Normal file
5
CHANGES.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
0.3a1
|
||||||
|
-----
|
||||||
|
|
||||||
|
- Initial port to Rattail v0.3.
|
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
include *.txt *.ini *.cfg *.rst
|
||||||
|
recursive-include rattail/pyramid *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
|
11
README.txt
Normal file
11
README.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
rattail.pyramid
|
||||||
|
===============
|
||||||
|
|
||||||
|
Rattail is a retail software framework based on `edbob <http://edbob.org/>`_,
|
||||||
|
and released under the GNU Affero General Public License.
|
||||||
|
|
||||||
|
This package contains Pyramid views, etc., for managing a Rattail system.
|
||||||
|
|
||||||
|
Please see Rattail's `home page <http://rattail.edbob.org/>`_ for more
|
||||||
|
information.
|
2
rattail/__init__.py
Normal file
2
rattail/__init__.py
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
from pkgutil import extend_path
|
||||||
|
__path__ = extend_path(__path__, __name__)
|
78
rattail/pyramid/__init__.py
Normal file
78
rattail/pyramid/__init__.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``rattail.pyramid`` -- Rattail's Pyramid Framework
|
||||||
|
"""
|
||||||
|
|
||||||
|
# import os.path
|
||||||
|
|
||||||
|
# import pyramid_beaker
|
||||||
|
# from pyramid.config import Configurator
|
||||||
|
|
||||||
|
# import edbob
|
||||||
|
|
||||||
|
from rattail.pyramid._version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
# def main(global_config, **settings):
|
||||||
|
# """
|
||||||
|
# This function returns a Pyramid WSGI application.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# # Here you can insert any code to modify the ``settings`` dict.
|
||||||
|
# # You can:
|
||||||
|
# # * Add additional keys to serve as constants or "global variables" in the
|
||||||
|
# # application.
|
||||||
|
# # * Set default values for settings that may have been omitted.
|
||||||
|
# # * Override settings that you don't want the user to change.
|
||||||
|
# # * Raise an exception if a setting is missing or invalid.
|
||||||
|
# # * Convert values from strings to their intended type.
|
||||||
|
|
||||||
|
# settings['mako.directories'] = [
|
||||||
|
# 'something:templates',
|
||||||
|
# 'edbob.pyramid:templates',
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# # Configure Pyramid
|
||||||
|
# config = Configurator(settings=settings)
|
||||||
|
# config.include('edbob.pyramid')
|
||||||
|
# config.include('something.subscribers')
|
||||||
|
# config.scan()
|
||||||
|
|
||||||
|
# # Configure Beaker
|
||||||
|
# session_factory = pyramid_beaker.session_factory_from_settings(settings)
|
||||||
|
# config.set_session_factory(session_factory)
|
||||||
|
# pyramid_beaker.set_cache_regions_from_settings(settings)
|
||||||
|
|
||||||
|
# # Configure edbob
|
||||||
|
# edbob.basic_logging()
|
||||||
|
# edbob.init('something', os.path.abspath(settings['edbob.config']))
|
||||||
|
|
||||||
|
# # Add static views
|
||||||
|
# # config.add_static_view('css', 'static/css', cache_max_age=3600)
|
||||||
|
# # config.add_static_view('img', 'static/img', cache_max_age=3600)
|
||||||
|
# # config.add_static_view('js', 'static/js', cache_max_age=3600)
|
||||||
|
|
||||||
|
# return config.make_wsgi_app()
|
1
rattail/pyramid/_version.py
Normal file
1
rattail/pyramid/_version.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
__version__ = '0.3a1'
|
98
rattail/pyramid/forms.py
Normal file
98
rattail/pyramid/forms.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``rattail.pyramid.forms`` -- Rattail Forms
|
||||||
|
"""
|
||||||
|
|
||||||
|
import formalchemy
|
||||||
|
# from formalchemy.fields import SelectFieldRenderer
|
||||||
|
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
|
||||||
|
import rattail
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['UpcFieldRenderer']
|
||||||
|
|
||||||
|
|
||||||
|
class BatchIdFieldRenderer(formalchemy.FieldRenderer):
|
||||||
|
"""
|
||||||
|
Renders the :attr:`rattail.Batch.batch_id` field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def render_readonly(self, **kwargs):
|
||||||
|
value = self.raw_value
|
||||||
|
if value is None:
|
||||||
|
return ''
|
||||||
|
return '%08u' % int(value)
|
||||||
|
|
||||||
|
|
||||||
|
# class BatchTerminalFieldRenderer(SelectFieldRenderer):
|
||||||
|
# """
|
||||||
|
# Renders a field whose value is a relationship to a
|
||||||
|
# :class:`rattail.BatchTerminal` instance.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# def render(self, options, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UpcFieldRenderer(formalchemy.TextFieldRenderer):
|
||||||
|
"""
|
||||||
|
Handles rendering for the product UPC field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def render_readonly(self, **kwargs):
|
||||||
|
value = self.raw_value
|
||||||
|
if not value:
|
||||||
|
return ''
|
||||||
|
if isinstance(value, basestring):
|
||||||
|
if value.isdigit():
|
||||||
|
value = int(value)
|
||||||
|
if isinstance(value, int):
|
||||||
|
return '%013u' % value
|
||||||
|
return self.stringify_value(value, as_html=True)
|
||||||
|
|
||||||
|
|
||||||
|
def unique_batch_terminal_id(value, field=None):
|
||||||
|
"""
|
||||||
|
.. highlight:: python
|
||||||
|
|
||||||
|
Validator for the :class:`rattail.BatchTerminal` class to ensure that SIL
|
||||||
|
IDs are not duplicated. For example::
|
||||||
|
|
||||||
|
from rattail.pyramid.forms import unique_batch_terminal_id
|
||||||
|
|
||||||
|
# fieldset = some_batch_terminal_fieldset_factory()
|
||||||
|
fieldset.sil_id.set(validate=unique_batch_terminal_id)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if value:
|
||||||
|
q = Session.query(rattail.BatchTerminal)
|
||||||
|
q = q.filter(rattail.BatchTerminal.sil_id == value)
|
||||||
|
if field.parent.edit:
|
||||||
|
q = q.filter(rattail.BatchTerminal.uuid != field.parent.model.uuid)
|
||||||
|
if q.count():
|
||||||
|
raise formalchemy.ValidationError("SIL ID value must be unique within the system")
|
49
rattail/pyramid/subscribers.py
Normal file
49
rattail/pyramid/subscribers.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``rattail.pyramid.subscribers`` -- Event Subscribers
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyramid import threadlocal
|
||||||
|
|
||||||
|
import rattail
|
||||||
|
|
||||||
|
|
||||||
|
def before_render(event):
|
||||||
|
"""
|
||||||
|
Adds goodies to the global template renderer context:
|
||||||
|
|
||||||
|
* ``rattail``
|
||||||
|
"""
|
||||||
|
|
||||||
|
request = event.get('request') or threadlocal.get_current_request()
|
||||||
|
|
||||||
|
renderer_globals = event
|
||||||
|
renderer_globals['rattail'] = rattail
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
config.add_subscriber('rattail.pyramid.subscribers:before_render',
|
||||||
|
'pyramid.events.BeforeRender')
|
2
rattail/pyramid/templates/batches/base.mako
Normal file
2
rattail/pyramid/templates/batches/base.mako
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<%inherit file="/base.mako" />
|
||||||
|
${parent.body()}
|
13
rattail/pyramid/templates/batches/batch.mako
Normal file
13
rattail/pyramid/templates/batches/batch.mako
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/crud.mako" />
|
||||||
|
|
||||||
|
<%def name="crud_name()">Batch</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
% if fieldset.edit and fieldset.model.rowcount:
|
||||||
|
<p>${h.link_to("View Batch Details", url('batch.details', uuid=fieldset.model.uuid))}</p>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
9
rattail/pyramid/templates/batches/columns.mako
Normal file
9
rattail/pyramid/templates/batches/columns.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
<table class="fieldset">
|
||||||
|
<tr>
|
||||||
|
<td class="label">Columns</td>
|
||||||
|
<td>
|
||||||
|
${columns|n}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
12
rattail/pyramid/templates/batches/details.mako
Normal file
12
rattail/pyramid/templates/batches/details.mako
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Batch : ${batch.name}</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
<p>${h.link_to("View Batch Properties", url('batch') + '?uuid=' + batch.uuid)}</p>
|
||||||
|
<p>${h.link_to("Execute this Batch", url('batch.execute', uuid=batch.uuid))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
11
rattail/pyramid/templates/batches/dictionaries.mako
Normal file
11
rattail/pyramid/templates/batches/dictionaries.mako
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Batch Dictionaries</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
## <p>${h.link_to("Create a New Dictionary", url('batch_dictionary'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
11
rattail/pyramid/templates/batches/dictionary.mako
Normal file
11
rattail/pyramid/templates/batches/dictionary.mako
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/crud.mako" />
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
<p>${h.link_to("Back to Dictionaries", url('batch_dictionaries'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
||||||
|
${columns|n}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<table class="fieldset">
|
||||||
|
<tr class="columns">
|
||||||
|
<td class="label">Supported Columns</td>
|
||||||
|
<td>
|
||||||
|
${grid|n}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
13
rattail/pyramid/templates/batches/index.mako
Normal file
13
rattail/pyramid/templates/batches/index.mako
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Batches</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Create a New Batch", url('batch'))}</p>
|
||||||
|
<p>${h.link_to("Manage Terminals", url('batch_terminals'))}</p>
|
||||||
|
<p>${h.link_to("View Dictionaries", url('batch_dictionaries'))}</p>
|
||||||
|
<p>${h.link_to("SIL Columns", url('sil_columns'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
9
rattail/pyramid/templates/batches/sil_column.mako
Normal file
9
rattail/pyramid/templates/batches/sil_column.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/crud.mako" />
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
<p>${h.link_to("Back to SIL Columns", url('sil_columns'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
10
rattail/pyramid/templates/batches/sil_columns.mako
Normal file
10
rattail/pyramid/templates/batches/sil_columns.mako
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">SIL Columns</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
24
rattail/pyramid/templates/batches/supported_fields.mako
Normal file
24
rattail/pyramid/templates/batches/supported_fields.mako
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
<table class="wrapper">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td><h2>Supported Fields</h2></td>
|
||||||
|
## <td class="right">${h.link_to("Update Field List", '#', id='update-fields')}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
${grid|n}
|
||||||
|
|
||||||
|
##<script language="javascript" type="text/javascript">
|
||||||
|
##
|
||||||
|
##$(function() {
|
||||||
|
##
|
||||||
|
## $('#update-fields').click(function() {
|
||||||
|
## $('div.grid').load('${url('batches', action='update_terminal')}?uuid=${fieldset.model.uuid}');
|
||||||
|
## return false;
|
||||||
|
## });
|
||||||
|
##
|
||||||
|
##});
|
||||||
|
##
|
||||||
|
##</script>
|
13
rattail/pyramid/templates/batches/terminal.mako
Normal file
13
rattail/pyramid/templates/batches/terminal.mako
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/crud.mako" />
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
<p>${h.link_to("Back to Batch Terminals", url('batch_terminals'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
||||||
|
% if fieldset.edit:
|
||||||
|
${terminal_columns|n}
|
||||||
|
% endif
|
17
rattail/pyramid/templates/batches/terminal_columns.mako
Normal file
17
rattail/pyramid/templates/batches/terminal_columns.mako
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
<h2>Supported Columns</h2>
|
||||||
|
|
||||||
|
<table class="fieldset">
|
||||||
|
<tr class="source_columns">
|
||||||
|
<td class="label">Source</td>
|
||||||
|
<td>
|
||||||
|
${source|n}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="target_columns">
|
||||||
|
<td class="label">Target</td>
|
||||||
|
<td>
|
||||||
|
${target|n}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
11
rattail/pyramid/templates/batches/terminals.mako
Normal file
11
rattail/pyramid/templates/batches/terminals.mako
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<%inherit file="/batches/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Batch Terminals</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
<p>${h.link_to("Back to Batches", url('batches'))}</p>
|
||||||
|
<p>${h.link_to("Create a New Terminal", url('batch_terminal'))}</p>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
2
rattail/pyramid/templates/products/base.mako
Normal file
2
rattail/pyramid/templates/products/base.mako
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<%inherit file="/base.mako" />
|
||||||
|
${parent.body()}
|
9
rattail/pyramid/templates/products/index.mako
Normal file
9
rattail/pyramid/templates/products/index.mako
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<%inherit file="/products/base.mako" />
|
||||||
|
<%inherit file="/index.mako" />
|
||||||
|
|
||||||
|
<%def name="title()">Products</%def>
|
||||||
|
|
||||||
|
<%def name="menu()">
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
${parent.body()}
|
0
rattail/pyramid/views/__init__.py
Normal file
0
rattail/pyramid/views/__init__.py
Normal file
858
rattail/pyramid/views/batches.py
Normal file
858
rattail/pyramid/views/batches.py
Normal file
|
@ -0,0 +1,858 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``rattail.pyramid.views.batches`` -- Batch (et al.) Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
import formalchemy
|
||||||
|
from sqlalchemy import and_, or_
|
||||||
|
|
||||||
|
from pyramid.httpexceptions import HTTPFound
|
||||||
|
from pyramid.renderers import render
|
||||||
|
from pyramid.response import Response
|
||||||
|
from pyramid.view import view_config
|
||||||
|
|
||||||
|
from edbob.exceptions import LoadSpecError
|
||||||
|
from edbob.pyramid import filters
|
||||||
|
from edbob.pyramid import forms
|
||||||
|
from edbob.pyramid import grids
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
from edbob.pyramid.views.crud import crud
|
||||||
|
from edbob.util import prettify
|
||||||
|
|
||||||
|
import rattail
|
||||||
|
from rattail import sil
|
||||||
|
from rattail.batches import next_batch_id
|
||||||
|
from rattail.pyramid.forms import (
|
||||||
|
BatchIdFieldRenderer,
|
||||||
|
unique_batch_terminal_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='sil_columns', renderer='/batches/sil_columns.mako')
|
||||||
|
def sil_columns(context, request):
|
||||||
|
"""
|
||||||
|
Index page for SIL columns (:class:`rattail.SilColumn`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.SilColumn,
|
||||||
|
exact=['sil_name'],
|
||||||
|
ilike=['display'])
|
||||||
|
|
||||||
|
config = filters.get_search_config(
|
||||||
|
'sil_columns', request, fmap,
|
||||||
|
include_filter_sil_name=True,
|
||||||
|
filter_type_sil_name='eq',
|
||||||
|
include_filter_display=True,
|
||||||
|
filter_type_display='lk')
|
||||||
|
|
||||||
|
search = filters.get_search_form(
|
||||||
|
config, sil_name="SIL Name")
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'sil_columns', request, search,
|
||||||
|
filter_map=fmap, sort='sil_name')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.SilColumn,
|
||||||
|
['sil_name', 'display', 'data_type'])
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.SilColumn)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
columns = grids.get_pager(query, config)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.SilColumn, columns, config,
|
||||||
|
request.route_url('sil_columns'),
|
||||||
|
url_object=request.route_url('sil_column'),
|
||||||
|
url_delete=request.route_url('sil_column'))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.sil_name.label("SIL Name"),
|
||||||
|
g.display,
|
||||||
|
g.data_type,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
grid = g.render(class_='clickable batch-columns')
|
||||||
|
return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='sil_column', renderer='/batches/sil_column.mako')
|
||||||
|
def sil_column(context, request):
|
||||||
|
|
||||||
|
def fieldset(column):
|
||||||
|
fs = forms.make_fieldset(
|
||||||
|
column, crud_title="Batch Column",
|
||||||
|
url_action=request.route_url('sil_column'),
|
||||||
|
url_cancel=request.route_url('sil_columns'))
|
||||||
|
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.sil_name.label("SIL Name"),
|
||||||
|
fs.display,
|
||||||
|
fs.data_type,
|
||||||
|
])
|
||||||
|
|
||||||
|
if fs.edit:
|
||||||
|
fs.sil_name.set(readonly=True)
|
||||||
|
return fs
|
||||||
|
|
||||||
|
|
||||||
|
return crud(
|
||||||
|
request, rattail.SilColumn, fieldset,
|
||||||
|
home=request.route_url('sil_columns'),
|
||||||
|
delete=request.route_url('sil_columns'))
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_dictionaries', renderer='/batches/dictionaries.mako')
|
||||||
|
def batch_dictionaries(context, request):
|
||||||
|
"""
|
||||||
|
Index page for batch dictionaries (:class:`rattail.BatchDictionary`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.BatchDictionary,
|
||||||
|
exact=['name'],
|
||||||
|
ilike=['description'])
|
||||||
|
|
||||||
|
config = filters.get_search_config(
|
||||||
|
'batch_dictionaries', request, fmap,
|
||||||
|
include_filter_name=True,
|
||||||
|
filter_type_name='eq',
|
||||||
|
include_filter_description=True,
|
||||||
|
filter_type_description='lk')
|
||||||
|
|
||||||
|
search = filters.get_search_form(config)
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch_dictionaries', request, search,
|
||||||
|
filter_map=fmap, sort='description')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.BatchDictionary,
|
||||||
|
['name', 'description'])
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.BatchDictionary)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
dictionaries = grids.get_pager(query, config)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.BatchDictionary, dictionaries, config,
|
||||||
|
request.route_url('batch_dictionaries'),
|
||||||
|
url_object=request.route_url('batch_dictionary'),
|
||||||
|
url_delete=request.route_url('batch_dictionary'))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.name,
|
||||||
|
g.description,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
grid = g.render(class_='clickable batch-dictionaries')
|
||||||
|
return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
|
def _dictionary_columns(request, uuid=None):
|
||||||
|
"""
|
||||||
|
Returns a rendered grid of columns for a batch dictionary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not uuid:
|
||||||
|
uuid = request.params.get('uuid')
|
||||||
|
dictionary = Session.query(rattail.BatchDictionary).get(uuid) if uuid else None
|
||||||
|
assert dictionary
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.BatchDictionaryColumn,
|
||||||
|
exact=['key'])
|
||||||
|
fmap['column'] = filters.filter_exact(rattail.SilColumn.sil_name)
|
||||||
|
fmap['display'] = filters.filter_ilike(rattail.SilColumn.display)
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch_dictionary.columns', request,
|
||||||
|
filter_map=fmap, sort='column')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.BatchDictionaryColumn,
|
||||||
|
['key'])
|
||||||
|
smap['column'] = grids.sorter(rattail.SilColumn.sil_name)
|
||||||
|
smap['display'] = grids.sorter(rattail.SilColumn.display)
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.BatchDictionaryColumn)
|
||||||
|
q = q.join(rattail.SilColumn)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
columns = query(config)
|
||||||
|
url = (request.route_url('batch_dictionary.columns')
|
||||||
|
+ '?uuid=' + dictionary.uuid)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.BatchDictionaryColumn, columns, config, url)
|
||||||
|
|
||||||
|
g.append(
|
||||||
|
formalchemy.Field(
|
||||||
|
'display',
|
||||||
|
value=lambda x: x.column.display))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.column,
|
||||||
|
g.display,
|
||||||
|
g.key,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
return g.render(class_='hoverable batch-dictionary-columns')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_dictionary.columns')
|
||||||
|
def batch_dictionary_columns(context, request):
|
||||||
|
return Response(body=_dictionary_columns(request),
|
||||||
|
content_type='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_dictionary', renderer='/batches/dictionary.mako')
|
||||||
|
def batch_dictionary(context, request):
|
||||||
|
|
||||||
|
def fieldset(dictionary):
|
||||||
|
fs = forms.make_fieldset(
|
||||||
|
dictionary, crud_title="Batch Dictionary",
|
||||||
|
url_action=request.route_url('batch_dictionary'),
|
||||||
|
url_cancel=request.route_url('batch_dictionaries'))
|
||||||
|
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.name,
|
||||||
|
fs.description,
|
||||||
|
])
|
||||||
|
|
||||||
|
fs.readonly = True
|
||||||
|
return fs
|
||||||
|
|
||||||
|
|
||||||
|
def pre_render(fs):
|
||||||
|
if fs.edit:
|
||||||
|
g = _dictionary_columns(request, fs.model.uuid)
|
||||||
|
return {'columns':
|
||||||
|
render('/batches/dictionary_columns.mako', {'grid':g})}
|
||||||
|
|
||||||
|
return crud(
|
||||||
|
request, rattail.BatchDictionary, fieldset,
|
||||||
|
home=request.route_url('batch_dictionaries'),
|
||||||
|
delete=request.route_url('batch_dictionaries'),
|
||||||
|
pre_render=pre_render)
|
||||||
|
|
||||||
|
|
||||||
|
def _terminal_columns(request, terminal, column_type):
|
||||||
|
"""
|
||||||
|
Returns a rendered grid of either
|
||||||
|
:class:`rattail.BatchTerminalSourceColumn` or
|
||||||
|
:class:`rattail.BatchTerminalTargetColumn` instances, depending on
|
||||||
|
``column_type`` (and of cource ``terminal``).
|
||||||
|
"""
|
||||||
|
|
||||||
|
cls = getattr(rattail, 'BatchTerminal%sColumn' % column_type.title())
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch_terminal.columns', request, sort='dictionary')
|
||||||
|
|
||||||
|
smap = {
|
||||||
|
'dictionary': grids.sorter(rattail.BatchDictionary.name),
|
||||||
|
'column': grids.sorter(rattail.SilColumn.sil_name),
|
||||||
|
'display': grids.sorter(rattail.SilColumn.display),
|
||||||
|
}
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(cls)
|
||||||
|
q = q.join(rattail.BatchDictionary)
|
||||||
|
q = q.join(rattail.SilColumn)
|
||||||
|
q = q.filter(cls.terminal == terminal)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
url = (request.route_url('batch_terminal.columns')
|
||||||
|
+ '?uuid=' + terminal.uuid + '&type=' + column_type)
|
||||||
|
|
||||||
|
columns = query(config)
|
||||||
|
g = forms.AlchemyGrid(cls, columns, config, url)
|
||||||
|
|
||||||
|
g.append(
|
||||||
|
formalchemy.Field(
|
||||||
|
'display',
|
||||||
|
value=lambda x: x.column.display))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.dictionary,
|
||||||
|
g.column,
|
||||||
|
g.display,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
return g.render(class_='hoverable batch-terminal-columns')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_terminal.columns')
|
||||||
|
def batch_terminal_columns(context, request):
|
||||||
|
uuid = request.params.get('uuid')
|
||||||
|
terminal = Session.query(rattail.BatchTerminal).get(uuid) if uuid else None
|
||||||
|
assert terminal
|
||||||
|
return Response(
|
||||||
|
body=_terminal_columns(request, terminal, request.params.get('type')),
|
||||||
|
content_type='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
# @view_config(route_name='batch_terminal.source_columns')
|
||||||
|
# def batch_terminal_source_columns(context, request):
|
||||||
|
# return Response(body=_terminal_columns(request, 'source'),
|
||||||
|
# content_type='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
# @view_config(route_name='batch_terminal.target_columns')
|
||||||
|
# def batch_terminal_target_columns(context, request):
|
||||||
|
# return Response(body=_terminal_columns(request, 'target'),
|
||||||
|
# content_type='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
def update_terminal_info(terminal):
|
||||||
|
"""
|
||||||
|
Updates the terminal's list of supported source and target columns, based
|
||||||
|
on feedback from the actual :class:`rattail.batch.BatchTerminal` instance.
|
||||||
|
Also sets the terminal's ``functional`` flag (et al.) to reflect reality.
|
||||||
|
"""
|
||||||
|
|
||||||
|
terminal.functional = True
|
||||||
|
try:
|
||||||
|
true_terminal = terminal.get_terminal()
|
||||||
|
except LoadSpecError, err:
|
||||||
|
terminal.functional = False
|
||||||
|
return err
|
||||||
|
else:
|
||||||
|
if true_terminal:
|
||||||
|
for column_type in ('source', 'target'):
|
||||||
|
declared_columns = getattr(true_terminal, '%s_columns' % column_type)
|
||||||
|
stored_columns = getattr(terminal, '%s_columns' % column_type)
|
||||||
|
cls = getattr(rattail, 'BatchTerminal%sColumn' % column_type.title())
|
||||||
|
for dictionary, columns in declared_columns.iteritems():
|
||||||
|
q = Session.query(rattail.BatchDictionary)
|
||||||
|
q = q.filter_by(name=dictionary)
|
||||||
|
dictionary = q.one()
|
||||||
|
for col in columns:
|
||||||
|
q = Session.query(rattail.SilColumn)
|
||||||
|
q = q.filter_by(sil_name=col)
|
||||||
|
col = q.one()
|
||||||
|
stored_columns.append(cls(
|
||||||
|
dictionary=dictionary,
|
||||||
|
column=col))
|
||||||
|
setattr(terminal, column_type, bool(declared_columns))
|
||||||
|
return true_terminal
|
||||||
|
else:
|
||||||
|
terminal.functional = False
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_terminal', renderer='/batches/terminal.mako')
|
||||||
|
def batch_terminal(context, request):
|
||||||
|
|
||||||
|
def fieldset(terminal):
|
||||||
|
fs = forms.make_fieldset(
|
||||||
|
terminal, crud_title="Batch Terminal",
|
||||||
|
url_action=request.route_url('batch_terminal'),
|
||||||
|
url_cancel=request.route_url('batch_terminals'))
|
||||||
|
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.sil_id.label("SIL ID").validate(unique_batch_terminal_id),
|
||||||
|
fs.description,
|
||||||
|
fs.class_spec.validate(forms.required),
|
||||||
|
fs.functional.readonly(),
|
||||||
|
fs.source_kwargs,
|
||||||
|
fs.target_kwargs,
|
||||||
|
])
|
||||||
|
|
||||||
|
if fs.edit:
|
||||||
|
fs.sil_id.set(readonly=True)
|
||||||
|
else:
|
||||||
|
del fs.source_kwargs
|
||||||
|
del fs.target_kwargs
|
||||||
|
return fs
|
||||||
|
|
||||||
|
def pre_render(fs):
|
||||||
|
if fs.edit:
|
||||||
|
data = {
|
||||||
|
'source': _terminal_columns(request, fs.model, 'source'),
|
||||||
|
'target': _terminal_columns(request, fs.model, 'target'),
|
||||||
|
}
|
||||||
|
return {'terminal_columns':
|
||||||
|
render('/batches/terminal_columns.mako', data)}
|
||||||
|
|
||||||
|
def post_sync(fs):
|
||||||
|
terminal = update_terminal_info(fs.model)
|
||||||
|
if isinstance(terminal, LoadSpecError):
|
||||||
|
request.session.flash(str(terminal))
|
||||||
|
elif terminal:
|
||||||
|
if not fs.model.sil_id:
|
||||||
|
fs.model.sil_id = terminal.name
|
||||||
|
if not fs.model.description:
|
||||||
|
fs.model.description = terminal.description
|
||||||
|
|
||||||
|
return crud(
|
||||||
|
request, rattail.BatchTerminal, fieldset,
|
||||||
|
delete=request.route_url('batch_terminals'),
|
||||||
|
pre_render=pre_render, post_sync=post_sync)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch_terminals', renderer='/batches/terminals.mako')
|
||||||
|
def batch_terminals(context, request):
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.BatchTerminal,
|
||||||
|
exact=['sil_id'],
|
||||||
|
ilike=['description'])
|
||||||
|
|
||||||
|
config = filters.get_search_config(
|
||||||
|
'batch_terminals', request, fmap,
|
||||||
|
include_filter_description=True,
|
||||||
|
filter_type_description='lk')
|
||||||
|
|
||||||
|
search = filters.get_search_form(
|
||||||
|
config, sil_id="SIL ID")
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch_terminals', request, search,
|
||||||
|
filter_map=fmap, sort='description', deletable=True)
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.BatchTerminal,
|
||||||
|
['sil_id', 'description'])
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.BatchTerminal)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
terminals = grids.get_pager(query, config)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.BatchTerminal, terminals, config,
|
||||||
|
request.route_url('batch_terminals'),
|
||||||
|
url_object=request.route_url('batch_terminal'),
|
||||||
|
url_delete=request.route_url('batch_terminal'))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.sil_id.label("SIL ID"),
|
||||||
|
g.description,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
grid = g.render(class_='clickable batch-terminals')
|
||||||
|
return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batches', renderer='/batches/index.mako')
|
||||||
|
def batches(context, request):
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.Batch,
|
||||||
|
ilike=['description', 'source_description'],
|
||||||
|
target=filters.filter_ilike(rattail.BatchTerminal.description))
|
||||||
|
|
||||||
|
config = filters.get_search_config(
|
||||||
|
'batches', request, fmap,
|
||||||
|
include_filter_description=True,
|
||||||
|
filter_type_description='lk',
|
||||||
|
include_filter_source_description=True,
|
||||||
|
filter_type_source_description='lk',
|
||||||
|
include_filter_target=True,
|
||||||
|
filter_type_target='lk')
|
||||||
|
|
||||||
|
search = filters.get_search_form(
|
||||||
|
config, source_description="Source")
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batches', request, search,
|
||||||
|
filter_map=fmap, deletable=True, sort='target')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.Batch,
|
||||||
|
['source_description', 'batch_id', 'action_type',
|
||||||
|
'description', 'rowcount', 'effective'],
|
||||||
|
target=grids.sorter(rattail.BatchTerminal.description))
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.Batch)
|
||||||
|
q = q.outerjoin((rattail.BatchTerminal,
|
||||||
|
rattail.BatchTerminal.uuid == rattail.Batch.target_uuid))
|
||||||
|
q = q.filter(or_(
|
||||||
|
rattail.Batch.deleted == None,
|
||||||
|
and_(
|
||||||
|
rattail.Batch.deleted != None,
|
||||||
|
rattail.Batch.deleted != True,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
batches = grids.get_pager(query, config)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.Batch, batches, config,
|
||||||
|
request.route_url('batches'),
|
||||||
|
url_object=request.route_url('batch'),
|
||||||
|
url_delete=request.route_url('batch'))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.source_description.label("Source"),
|
||||||
|
g.batch_id.with_renderer(BatchIdFieldRenderer).label("Batch ID"),
|
||||||
|
g.target,
|
||||||
|
g.action_type.with_renderer(forms.EnumFieldRenderer(rattail.BATCH_ACTION_TYPE)).label("Action"),
|
||||||
|
g.description,
|
||||||
|
g.rowcount.label("Rows"),
|
||||||
|
g.effective.with_renderer(forms.PrettyDateTimeFieldRenderer),
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
grid = g.render(class_='clickable batches')
|
||||||
|
return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
|
def _batch_columns(request, batch):
|
||||||
|
"""
|
||||||
|
Returns a rendered grid of :class:`rattail.BatchColumn` instances for a
|
||||||
|
given :class:`rattail.Batch` instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch.columns', request, sort='ordinal')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.BatchColumn,
|
||||||
|
['ordinal', 'targeted'])
|
||||||
|
smap['column'] = grids.sorter(rattail.SilColumn.sil_name)
|
||||||
|
smap['display'] = grids.sorter(rattail.SilColumn.display)
|
||||||
|
smap['source'] = grids.sorter(rattail.BatchTerminal.description)
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.BatchColumn)
|
||||||
|
q = q.join(rattail.SilColumn)
|
||||||
|
q = q.join(rattail.BatchTerminal)
|
||||||
|
q = q.filter(rattail.BatchColumn.batch == batch)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
url = request.route_url('batch.columns', uuid=batch.uuid)
|
||||||
|
|
||||||
|
columns = query(config)
|
||||||
|
g = forms.AlchemyGrid(rattail.BatchColumn, columns, config, url)
|
||||||
|
|
||||||
|
g.append(formalchemy.Field('display', value=lambda x: x.column.display))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.ordinal,
|
||||||
|
g.column.label("SIL Name"),
|
||||||
|
g.display,
|
||||||
|
g.source,
|
||||||
|
g.targeted,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
return g.render(class_='hoverable batch-columns')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch.columns')
|
||||||
|
def batch_columns(context, request):
|
||||||
|
uuid = request.matchdict['uuid']
|
||||||
|
batch = Session.query(rattail.Batch).get(uuid) if uuid else None
|
||||||
|
assert batch
|
||||||
|
return Response(
|
||||||
|
body=_batch_columns(request, batch),
|
||||||
|
content_type='text/html')
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='batch', renderer='/batches/batch.mako')
|
||||||
|
def batch(context, request):
|
||||||
|
|
||||||
|
def fieldset(batch):
|
||||||
|
fs = forms.make_fieldset(batch, url=request.route_url,
|
||||||
|
url_action=request.route_url('batch'),
|
||||||
|
route_name='batches')
|
||||||
|
|
||||||
|
# Remove unsupported action types...for now.
|
||||||
|
enum = rattail.BATCH_ACTION_TYPE.copy()
|
||||||
|
del enum[rattail.BATCH_ADD_REPLACE]
|
||||||
|
del enum[rattail.BATCH_CHANGE]
|
||||||
|
del enum[rattail.BATCH_LOAD]
|
||||||
|
del enum[rattail.BATCH_REMOVE]
|
||||||
|
|
||||||
|
fs.configure(
|
||||||
|
include=[
|
||||||
|
fs.source.validate(forms.required),
|
||||||
|
fs.batch_id.with_renderer(BatchIdFieldRenderer).label("Batch ID").readonly(),
|
||||||
|
fs.target.validate(forms.required),
|
||||||
|
fs.dictionary.validate(forms.required),
|
||||||
|
fs.action_type.with_renderer(forms.EnumFieldRenderer(enum)).validate(forms.required).label("Action"),
|
||||||
|
fs.description,
|
||||||
|
fs.effective.with_renderer(forms.PrettyDateTimeFieldRenderer),
|
||||||
|
fs.rowcount.label("Rows").readonly(),
|
||||||
|
])
|
||||||
|
if fs.edit:
|
||||||
|
fs.source.set(readonly=True)
|
||||||
|
fs.dictionary.set(readonly=True)
|
||||||
|
if fs.model.target:
|
||||||
|
fs.target.set(readonly=True)
|
||||||
|
fs.action_type.set(readonly=True)
|
||||||
|
fs.effective.set(readonly=True)
|
||||||
|
fs.append(forms.ChildGridField('columns', _batch_columns(request, fs.model)))
|
||||||
|
else:
|
||||||
|
del fs.batch_id
|
||||||
|
del fs.effective
|
||||||
|
del fs.rowcount
|
||||||
|
return fs
|
||||||
|
|
||||||
|
def post_sync(fs):
|
||||||
|
if not fs.edit:
|
||||||
|
source = Session.query(rattail.BatchTerminal).get(fs.model.source_uuid)
|
||||||
|
target = Session.query(rattail.BatchTerminal).get(fs.model.target_uuid)
|
||||||
|
assert source and target
|
||||||
|
batch = fs.model
|
||||||
|
batch.batch_id = next_batch_id(source.sil_id, consume=True,
|
||||||
|
session=Session())
|
||||||
|
batch.name = '%s.%08u' % (source.sil_id, batch.batch_id)
|
||||||
|
batch.source_description = source.description
|
||||||
|
|
||||||
|
for i, col in enumerate(source.source_columns, 1):
|
||||||
|
batch.columns.append(rattail.BatchColumn(
|
||||||
|
ordinal=i,
|
||||||
|
column=col.column,
|
||||||
|
source=source,
|
||||||
|
targeted=True,
|
||||||
|
))
|
||||||
|
|
||||||
|
def pre_render(fs):
|
||||||
|
if not fs.edit:
|
||||||
|
|
||||||
|
for field in ('source', 'target'):
|
||||||
|
if not fs.render_fields[field].value:
|
||||||
|
q = Session.query(rattail.BatchTerminal)
|
||||||
|
q = q.filter(getattr(rattail.BatchTerminal, field) == True)
|
||||||
|
count = q.count()
|
||||||
|
if count == 1:
|
||||||
|
setattr(fs.model, field, q.first())
|
||||||
|
fs.render_fields[field].set(required=True) # hack to avoid null option
|
||||||
|
elif not count:
|
||||||
|
request.session.flash("You must create at least one %s Batch Terminal "
|
||||||
|
"before you may create a Batch." % prettify(field))
|
||||||
|
return HTTPFound(location=request.route_url('batches'))
|
||||||
|
|
||||||
|
# else:
|
||||||
|
# data = {'columns': _batch_columns(request, fs.model)}
|
||||||
|
# return {'columns': render('/batches/columns.mako', data)}
|
||||||
|
|
||||||
|
# def delete(batch):
|
||||||
|
# # jct = batch.target_junction.display if batch.target_junction else batch.target
|
||||||
|
# # batch.table.drop()
|
||||||
|
# # self.request.session.flash("Batch was deleted: %s" % self.batch_display(batch))
|
||||||
|
# self.Session.delete(batch)
|
||||||
|
# # self.Session.flush()
|
||||||
|
|
||||||
|
return crud(request, rattail.Batch, fieldset,
|
||||||
|
delete=request.route_url('batches'),
|
||||||
|
post_sync=post_sync, pre_render=pre_render)
|
||||||
|
|
||||||
|
@view_config(route_name='batch.details', renderer='/batches/details.mako')
|
||||||
|
def batch_details(context, request):
|
||||||
|
uuid = request.matchdict['uuid']
|
||||||
|
batch = Session.query(rattail.Batch).get(uuid) if uuid else None
|
||||||
|
assert batch
|
||||||
|
request.session['batches.uuid'] = batch.uuid
|
||||||
|
name = 'batches.%s' % batch.name
|
||||||
|
|
||||||
|
# def batch_search_form(batch, name):
|
||||||
|
# """
|
||||||
|
# Returns a dynamically-assembled search form, specific to the current
|
||||||
|
# batch.
|
||||||
|
# """
|
||||||
|
|
||||||
|
# config = filters.get_search_config(name, request, batch_filter_map(batch))
|
||||||
|
# labels = {}
|
||||||
|
# form = filters.get_search_form(config, **labels)
|
||||||
|
# return form
|
||||||
|
|
||||||
|
# def batch_query(batch):
|
||||||
|
|
||||||
|
# def query(config):
|
||||||
|
# q = Session.query(batch.rowclass)
|
||||||
|
# q = filters.filter_query(q, config, batch_filter_map(batch))
|
||||||
|
# q = grids.sort_query(q, config, batch_sort_map(batch))
|
||||||
|
# return q
|
||||||
|
|
||||||
|
# return query
|
||||||
|
|
||||||
|
# def batch_grid(batch):
|
||||||
|
|
||||||
|
# cls = batch.rowclass
|
||||||
|
|
||||||
|
# def grid(query, config):
|
||||||
|
# rows = grids.get_pager(query, config)
|
||||||
|
# g = grids.get_grid(cls, rows, config)
|
||||||
|
# include = [getattr(g, x.column.sil_name) for x in batch.columns]
|
||||||
|
# # if batch.target_junction:
|
||||||
|
# # fnmap = batch.target_junction.fieldname_map_ui
|
||||||
|
# # for name in fnmap:
|
||||||
|
# # getattr(g, name).set(label=fnmap[name])
|
||||||
|
# g.configure(include=include)
|
||||||
|
# return g
|
||||||
|
|
||||||
|
# return grid
|
||||||
|
|
||||||
|
cls = batch.rowclass
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(cls, exact=['F01'])
|
||||||
|
for col in batch.columns:
|
||||||
|
fmap.setdefault(col.column.sil_name,
|
||||||
|
filters.filter_ilike(getattr(cls, col.column.sil_name)))
|
||||||
|
|
||||||
|
config = filters.get_search_config('batch.%s' % batch.name, request, fmap)
|
||||||
|
|
||||||
|
search = filters.get_search_form(config)
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'batch.%s' % batch.name, request, search, filter_map=fmap,
|
||||||
|
url=request.route_url('batch.details', uuid=batch.uuid),
|
||||||
|
sort=batch.columns[0].column.sil_name)
|
||||||
|
|
||||||
|
# url = request.route_url('batch.details')
|
||||||
|
# grid = forms.AlchemyGrid(name, batch_grid(batch), batch_query(batch),
|
||||||
|
# search, url=url, sort='F01', per_page=20)
|
||||||
|
# query = batch_query(batch)
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(cls)
|
||||||
|
# cls = batch.rowclass
|
||||||
|
# # return {
|
||||||
|
# # 'F01': util.sorter(cls.F01),
|
||||||
|
# # }
|
||||||
|
# sort_map = {}
|
||||||
|
# for name in batch.elements.split(','):
|
||||||
|
# sort_map[name] = util.sorter(getattr(cls, name))
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(batch.rowclass)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
rows = grids.get_pager(query, config)
|
||||||
|
grid = forms.AlchemyGrid(
|
||||||
|
cls, rows, config,
|
||||||
|
request.route_url('batch.details', uuid=batch.uuid))
|
||||||
|
grid.readonly = True
|
||||||
|
grid = grid.render(class_='clickable batch-details')
|
||||||
|
return grids.render_grid(request, grid, search, batch=batch,
|
||||||
|
pretty_datetime=forms.pretty_datetime)
|
||||||
|
|
||||||
|
@view_config(route_name='batch.execute')
|
||||||
|
def execute(context, request):
|
||||||
|
"""
|
||||||
|
Executes a batch.
|
||||||
|
"""
|
||||||
|
|
||||||
|
home = HTTPFound(location=request.route_url('batches'))
|
||||||
|
|
||||||
|
# batch = self.request.params.get('uuid')
|
||||||
|
# batch = self.Session.query(rattail.Batch).get(batch) if batch else None
|
||||||
|
# if not batch:
|
||||||
|
# return home
|
||||||
|
# print 'got a batch'
|
||||||
|
|
||||||
|
uuid = request.matchdict['uuid']
|
||||||
|
batch = Session.query(rattail.Batch).get(uuid) if uuid else None
|
||||||
|
if not batch:
|
||||||
|
return home
|
||||||
|
# print 'got a batch'
|
||||||
|
|
||||||
|
# jct = batch.target_junction
|
||||||
|
# if not jct:
|
||||||
|
# self.request.session.flash("Batch does not have a valid target")
|
||||||
|
# return home
|
||||||
|
# print 'got a target'
|
||||||
|
|
||||||
|
# if not batch.target:
|
||||||
|
# request.session.flash("Batch does not have a valid target")
|
||||||
|
# return home
|
||||||
|
|
||||||
|
# jct = jct()
|
||||||
|
# if batch.dictionary == rattail.BATCH_MAIN_ITEM:
|
||||||
|
# print 'exporting main item'
|
||||||
|
# jct.export_main_item(batch, self.Session)
|
||||||
|
# print 'exported main item'
|
||||||
|
|
||||||
|
batch.execute()
|
||||||
|
|
||||||
|
# table = batch.table
|
||||||
|
# rowclass = batch.rowclass
|
||||||
|
# # self.Session.delete(batch)
|
||||||
|
# batch.deleted = True
|
||||||
|
# self.Session.flush()
|
||||||
|
# print 'deleted batch'
|
||||||
|
# q = self.Session.query(rowclass)
|
||||||
|
# q.delete(synchronize_session=False)
|
||||||
|
# print 'deleted rows'
|
||||||
|
# # table.drop(bind=self.Session().bind, checkfirst=False)
|
||||||
|
# # print 'dropped table'
|
||||||
|
|
||||||
|
request.session.flash("The batch was executed: %s, %s" % (
|
||||||
|
batch.target, rattail.BATCH_ACTION_TYPE[batch.action_type]))
|
||||||
|
return home
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
config.add_route('sil_columns', '/batches/sil_columns')
|
||||||
|
config.add_route('sil_column', '/batches/sil_column')
|
||||||
|
config.add_route('batch_dictionaries', '/batches/dictionaries')
|
||||||
|
config.add_route('batch_dictionary', '/batches/dictionary')
|
||||||
|
config.add_route('batch_dictionary.columns', '/batches/dictionary-columns')
|
||||||
|
config.add_route('batch_terminals', '/batches/terminals')
|
||||||
|
config.add_route('batch_terminal', '/batches/terminal')
|
||||||
|
config.add_route('batch_terminal.columns', '/batches/terminal-columns')
|
||||||
|
config.add_route('batches', '/batches')
|
||||||
|
config.add_route('batch', '/batch')
|
||||||
|
config.add_route('batch.columns', '/batch/{uuid}/columns')
|
||||||
|
config.add_route('batch.details', '/batch/{uuid}/details')
|
||||||
|
config.add_route('batch.execute', '/batch/{uuid}/execute')
|
||||||
|
config.scan(__name__)
|
88
rattail/pyramid/views/products.py
Normal file
88
rattail/pyramid/views/products.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
``rattail.pyramid.views.products`` -- Product Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyramid.view import view_config
|
||||||
|
|
||||||
|
from edbob.pyramid import filters
|
||||||
|
from edbob.pyramid import forms
|
||||||
|
from edbob.pyramid import grids
|
||||||
|
from edbob.pyramid import Session
|
||||||
|
|
||||||
|
import rattail
|
||||||
|
from rattail.pyramid.forms import UpcFieldRenderer
|
||||||
|
|
||||||
|
|
||||||
|
@view_config(route_name='products', renderer='/products/index.mako')
|
||||||
|
def products(context, request):
|
||||||
|
|
||||||
|
fmap = filters.get_filter_map(
|
||||||
|
rattail.Product,
|
||||||
|
exact=['upc'],
|
||||||
|
ilike=['description'])
|
||||||
|
|
||||||
|
config = filters.get_search_config(
|
||||||
|
'products', request, fmap,
|
||||||
|
include_filter_description=True,
|
||||||
|
filter_type_description='lk')
|
||||||
|
|
||||||
|
search = filters.get_search_form(
|
||||||
|
config, upc="UPC")
|
||||||
|
|
||||||
|
config = grids.get_grid_config(
|
||||||
|
'products', request, search,
|
||||||
|
filter_map=fmap, sort='description')
|
||||||
|
|
||||||
|
smap = grids.get_sort_map(
|
||||||
|
rattail.Product,
|
||||||
|
['upc', 'description'])
|
||||||
|
|
||||||
|
def query(config):
|
||||||
|
q = Session.query(rattail.Product)
|
||||||
|
q = filters.filter_query(q, config)
|
||||||
|
q = grids.sort_query(q, config, smap)
|
||||||
|
return q
|
||||||
|
|
||||||
|
products = grids.get_pager(query, config)
|
||||||
|
g = forms.AlchemyGrid(
|
||||||
|
rattail.Product, products, config,
|
||||||
|
request.route_url('products'))
|
||||||
|
|
||||||
|
g.configure(
|
||||||
|
include=[
|
||||||
|
g.upc.with_renderer(UpcFieldRenderer).label("UPC"),
|
||||||
|
g.description,
|
||||||
|
])
|
||||||
|
|
||||||
|
g.readonly = True
|
||||||
|
grid = g.render(class_='clickable products')
|
||||||
|
return grids.render_grid(request, grid, search)
|
||||||
|
|
||||||
|
|
||||||
|
def includeme(config):
|
||||||
|
config.add_route('products', '/products')
|
||||||
|
config.scan(__name__)
|
120
setup.py
Normal file
120
setup.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2012 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Affero General Public License as published by the Free
|
||||||
|
# Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
execfile(os.path.join(here, 'rattail', 'pyramid', '_version.py'))
|
||||||
|
README = open(os.path.join(here, 'README.txt')).read()
|
||||||
|
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
|
||||||
|
|
||||||
|
|
||||||
|
requires = [
|
||||||
|
#
|
||||||
|
# Version numbers within comments below have specific meanings.
|
||||||
|
# Basically the 'low' value is a "soft low," and 'high' a "soft high."
|
||||||
|
# In other words:
|
||||||
|
#
|
||||||
|
# If either a 'low' or 'high' value exists, the primary point to be
|
||||||
|
# made about the value is that it represents the most current (stable)
|
||||||
|
# version available for the package (assuming typical public access
|
||||||
|
# methods) whenever this project was started and/or documented.
|
||||||
|
# Therefore:
|
||||||
|
#
|
||||||
|
# If a 'low' version is present, you should know that attempts to use
|
||||||
|
# versions of the package significantly older than the 'low' version
|
||||||
|
# may not yield happy results. (A "hard" high limit may or may not be
|
||||||
|
# indicated by a true version requirement.)
|
||||||
|
#
|
||||||
|
# Similarly, if a 'high' version is present, and especially if this
|
||||||
|
# project has laid dormant for a while, you may need to refactor a bit
|
||||||
|
# when attempting to support a more recent version of the package. (A
|
||||||
|
# "hard" low limit should be indicated by a true version requirement
|
||||||
|
# when a 'high' version is present.)
|
||||||
|
#
|
||||||
|
# In any case, developers and other users are encouraged to play
|
||||||
|
# outside the lines with regard to these soft limits. If bugs are
|
||||||
|
# encountered then they should be filed as such.
|
||||||
|
#
|
||||||
|
# package # low high
|
||||||
|
|
||||||
|
# Beaker dependency included here because 'pyramid_beaker' uses incorrect
|
||||||
|
# case in its requirement declaration.
|
||||||
|
'Beaker', # 1.6.3
|
||||||
|
|
||||||
|
# Pyramid 1.3 introduced 'pcreate' command (and friends) to replace
|
||||||
|
# deprecated 'paster create' (and friends).
|
||||||
|
'pyramid>=1.3a1', # 1.3b2
|
||||||
|
|
||||||
|
'decorator', # 3.3.2
|
||||||
|
'Mako', # 0.6.2
|
||||||
|
'pyramid_beaker', # 0.6.1
|
||||||
|
'pyramid_debugtoolbar', # 1.0
|
||||||
|
'pyramid_tm', # 0.3
|
||||||
|
'rattail>=0.3a1.dev', # 0.3a1.dev
|
||||||
|
'SQLAlchemy', # 0.7.6
|
||||||
|
'Tempita', # 0.5.1
|
||||||
|
'transaction', # 1.2.0
|
||||||
|
'waitress', # 0.8.1
|
||||||
|
'WebHelpers', # 1.3
|
||||||
|
'zope.sqlalchemy', # 0.7
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name = "rattail.pyramid",
|
||||||
|
version = __version__,
|
||||||
|
author = "Lance Edgar",
|
||||||
|
author_email = "lance@edbob.org",
|
||||||
|
url = "http://rattail.edbob.org/",
|
||||||
|
license = "GNU Affero GPL v3",
|
||||||
|
description = "Rattail Pyramid Framework",
|
||||||
|
long_description = README + '\n\n' + CHANGES,
|
||||||
|
|
||||||
|
classifiers = [
|
||||||
|
'Development Status :: 3 - Alpha',
|
||||||
|
'Environment :: Web Environment',
|
||||||
|
'Framework :: Pylons',
|
||||||
|
'Intended Audience :: Developers',
|
||||||
|
'License :: OSI Approved :: GNU Affero General Public License v3',
|
||||||
|
'Natural Language :: English',
|
||||||
|
'Operating System :: OS Independent',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Topic :: Internet :: WWW/HTTP',
|
||||||
|
'Topic :: Office/Business',
|
||||||
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
|
],
|
||||||
|
|
||||||
|
install_requires = requires,
|
||||||
|
|
||||||
|
namespace_packages = ['rattail'],
|
||||||
|
packages = find_packages(),
|
||||||
|
include_package_data = True,
|
||||||
|
zip_safe = False,
|
||||||
|
)
|
Loading…
Reference in a new issue