diff --git a/.gitignore b/.gitignore index da46463..2593080 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +*~ +*.pyc +dist/ rattail_tutorial.egg-info/ docs/_build/ .tox/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f908fda..8b57c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ All notable changes to rattail-tutorial will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project (probably doesn't) adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## v0.3.1 (2024-07-01) + +### Fix + +- remove legacy command definitions + +## v0.3.0 (2024-07-01) + +### Feat + +- migrate all commands to use `typer` framework + +## v0.2.0 (2024-07-01) + +### Feat + +- switch from setup.cfg to pyproject.toml + hatchling + ## [0.1.0] - 2019-08-15 ### Changed - Initial version, with very basic (mostly generated) app. diff --git a/README.rst b/README.md similarity index 59% rename from README.rst rename to README.md index 1d15628..d8b38c1 100644 --- a/README.rst +++ b/README.md @@ -1,13 +1,9 @@ -.. -*- mode: rst -*- -rattail-tutorial -================ +# rattail-tutorial This project is intended for use as a "tutorial" for Rattail app development. It contains documentation for the tutorial itself, but also contains code for the tutorial app, which users may run locally for testing. -See the `Rattail website`_ for more info. - -.. _`Rattail website`: https://rattailproject.org/ +See the [Rattail website](https://rattailproject.org/) for more info. diff --git a/Vagrantfile b/Vagrantfile index 0c359c8..5da7689 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -5,4 +5,7 @@ Vagrant.configure("2") do |config| # target machine runs Debian 10 "buster" config.vm.box = "debian/buster64" + # rattail-tutorial web app + config.vm.network "forwarded_port", guest: 9080, host: 9080 + end diff --git a/docs/conf.py b/docs/conf.py index 50aaca2..eb80bf5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,12 +17,14 @@ # -- Project information ----------------------------------------------------- +from importlib.metadata import version as get_version + project = 'rattail-tutorial' -copyright = '2019, Lance Edgar' +copyright = '2019-2024, Lance Edgar' author = 'Lance Edgar' # The full version, including alpha/beta/rc tags -release = '0.1' +release = get_version('rattail-tutorial') # -- General configuration --------------------------------------------------- @@ -51,7 +53,7 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = 'furo' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/create-project.rst b/docs/create-project.rst index 4c6932a..9cb84ec 100644 --- a/docs/create-project.rst +++ b/docs/create-project.rst @@ -125,7 +125,7 @@ rattail-tutorial app instead, you should do this:: mkdir -p ~/src cd ~/src - git clone https://rattailproject.org/git/rattail-tutorial.git + git clone https://forgejo.wuttaproject.org/rattail/rattail-tutorial.git pip install -e rattail-tutorial Creating the Project diff --git a/docs/customize/disable-web-view.rst b/docs/customize/disable-web-view.rst new file mode 100644 index 0000000..8e1cc0e --- /dev/null +++ b/docs/customize/disable-web-view.rst @@ -0,0 +1,89 @@ + +Hide / Disable Unwanted Web Views +================================= + +The first thing we'll want to do is take stock of the views currently exposed +by the web app, and either hide or outright "remove" any we don't want (yet). + +There are sort of 3 different aspects to whether or not a particular web view +is "available" for a given user: + +* is the view even defined? +* does user have permission to access the view? +* is there a menu (or other) link to the view? + +Removing a (Master) View +------------------------ + +There are a few "core" web views which will "always" be defined, but the vast +majority are really optional. The so-called "master" web views, each of which +basically corresponds to a particular table in the DB, are (almost?) entirely +optional. For instance if your organization needs to track customers but not +products, within your Poser app, then you might go so far as to "remove" the +product views from your app. + +If you do this, then e.g. navigating to http://localhost:9080/products/ (or +whatever your URL is) would result in a 404 not found error regardless of user +permissions, i.e. even if you "become root". However by default (using code +generated via scaffold) the product views *are* enabled, so this URL *would* +work. + +Whether or not a given view(s) is "defined" will depend on whether or not the +module containing this view(s) has been "included" by the Pyramid (web app) +Configurator object. In other words we're leveraging this "include" concept +from `Pyramid`_ in order to control which views are brought into the running +app. + +.. _Pyramid: https://trypyramid.com/ + +In practice what that means is usually just that you must curate the list of +views which are included, within your own project. This config thing works +recursively, but we try to keep the primary list within a conventional place. +In our (tutorial's) case this file is at +``~/src/rattail-tutorial/rattail_tutorial/web/views/__init__.py`` and by +default (freshly generated via scaffold) it looks something like this:: + + def includeme(config): + + # core views + config.include('rattail_tutorial.web.views.common') + config.include('tailbone.views.auth') + config.include('tailbone.views.tables') + config.include('tailbone.views.upgrades') + config.include('tailbone.views.progress') + + # main table views + config.include('tailbone.views.brands') + config.include('tailbone.views.customers') + config.include('tailbone.views.customergroups') + config.include('tailbone.views.datasync') + config.include('tailbone.views.departments') + config.include('tailbone.views.email') + config.include('tailbone.views.employees') + config.include('tailbone.views.messages') + config.include('tailbone.views.people') + config.include('tailbone.views.products') + config.include('tailbone.views.reportcodes') + config.include('tailbone.views.roles') + config.include('tailbone.views.settings') + config.include('tailbone.views.shifts') + config.include('tailbone.views.stores') + config.include('tailbone.views.subdepartments') + config.include('tailbone.views.users') + config.include('tailbone.views.vendors') + + # batch views + config.include('tailbone.views.handheld') + config.include('tailbone.views.inventory') + +In our case the only thing we'll remove for now is the "shifts" entry, i.e. we +wish to remove the line that says:: + + config.include('tailbone.views.shifts') + +That's because these views have to do with staff scheduling and time clock +stuff, which (at least for now) we won't concern ourselves with. + +Note that the underlying *tables* which might contain such data, are left in +place within our database. We're just declaring that we do not need our web +app to support master views for interacting with those tables. diff --git a/docs/customize/index.rst b/docs/customize/index.rst new file mode 100644 index 0000000..8986d22 --- /dev/null +++ b/docs/customize/index.rst @@ -0,0 +1,22 @@ + +Customizing the App! +==================== + +Now that you've made it through the "setup" gauntlet, it's finally time for +some fun stuff. + +As we're building this tutorial, at this point our project is *basically* the +same as any other "Poser" project, as generated via scaffold. But as we keep +going, our project will become more and more customized for the purposes of the +tutorial. + +So if you have just created a new project, some of this might make more sense +to you, vs. if you're running the rattail-tutorial project itself, many things +described in this section will have already been done (and then some) to the +code base, so you should keep that in mind when reading. + + +.. toctree:: + :maxdepth: 2 + + disable-web-view diff --git a/docs/index.rst b/docs/index.rst index 687f895..a434455 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,6 +55,10 @@ Table of Contents create-project start-docs configure + pkg-release + make-db + run-webapp + customize/index Indices and tables diff --git a/docs/make-db.rst b/docs/make-db.rst new file mode 100644 index 0000000..b4d437e --- /dev/null +++ b/docs/make-db.rst @@ -0,0 +1,87 @@ + +.. highlight:: sh + +Establish Main App Database +=========================== + +Now that you have a hang of how to use the Rattail-style command line +(somewhat), let's move on to the database. + +The main reason to wait until now to add a DB to the mix, was simply to show +that the "core" of Rattail does not need a DB. However in practice there *are* +definitely some commands which Rattail comes with out of the box, and which +also would require one or even multiple databases to be present. + + +Create User for PostgreSQL +-------------------------- + +Before we make our database, let's first establish a user account within +Postgres, which we will designate as the "owner" of our database(s). + +It is convention within Rattail, to create a PG user named "rattail" for this +purpose. You are free to use another name if you prefer:: + + sudo -u postgres createuser --no-createdb --no-createrole --no-superuser rattail + +You also should declare a password for the user:: + + sudo -u postgres psql -c "alter user rattail password 'newpassword'" + + +Create Database +--------------- + +Now that we know who to use as the "owner" we will create a new Postgres +database:: + + sudo -u postgres createdb --owner rattail rattut + +Of course we named our database "rattut" here only because we're assuming this +tutorial project is the app, but your name may be different. + +At this point you should update your ``app/rattail.conf`` file to reflect your +chosen database name and user credentials: + +.. code-block:: ini + + [rattail.db] + default.url = postgresql://rattail:newpassword@localhost/rattut + + +Install DB Schema +----------------- + +So you have a DB but it’s empty; you can confirm that with:: + + sudo -u postgres psql -c '\d' rattut + +But we'll fix that now. Schema is managed entirely via Alembic "version" +scripts, so to install the schema we merely run all the scripts:: + + cdvirtualenv + bin/alembic -c app/rattail.conf upgrade heads + +(Note that you must use ``rattail.conf`` for that; ``quiet.conf`` won't work.) + +If you check the DB again you should see a good amount of tables. + + +.. _make-user: + +Create Admin User in DB +----------------------- + +We include this here not so much because you *need* an admin user in your DB at +this point (although you will for the web app), but rather just to confirm that +everything is setup correctly thus far. + +You currently should have no users in your DB:: + + sudo -u postgres psql -c 'select * from "user"' rattut + +Okay then let's make an admin user for you:: + + bin/rattail -c app/quiet.conf make-user --admin myusername + +Now if you query the ``user`` table again you should see your new account. diff --git a/docs/pkg-release.rst b/docs/pkg-release.rst new file mode 100644 index 0000000..b65a054 --- /dev/null +++ b/docs/pkg-release.rst @@ -0,0 +1,118 @@ + +.. highlight:: sh + +Build a Release for the Project +=============================== + +Even though our app does very little at this stage, we wish to go ahead and +"release" our first version for it. + +.. note:: + Whether or not you actually need to build releases for your project, may + depend on your use case. For instance if you have reason to run the app(s) + directly from source (i.e. git HEAD) then you may have no use for a built + package. + + +Project Versioning +------------------ + +The project's current version "number" is kept in only one place really, in our +case ``~/src/rattail-tutorial/rattail_tutorial/_version.py``. Other files are +configured to read the current project version from there. + +The initial version for a new project will generally be '0.1.0' and it's +assumed that subsequent versions will be '0.1.1' then '0.1.2' etc. until you've +decided that it's time to do a '0.2.0' release, and the cycle begins again. + +You can be as aggressive or conservative as you like when it comes to +incrementing the more "major" parts of the version number, e.g. you can +increment conservatively to where you've just released say, '0.1.427' before you +finally go to '0.2.0'. The only real "requirement" (assumption) here is that +you will build a new version release *every time* you update the production +environment(s). Sometimes that may mean multiple releases in a given day, +e.g. if the first one ships with a bug and you have to push a release to fix. + + +Install Invoke +-------------- + +While you can most certainly go about the build/release task in various ways, +the convention within Rattail-land is to use `Invoke`_. + +.. _Invoke: https://www.pyinvoke.org/ + +So next we'll install that to your virtualenv:: + + pip install invoke + +You may also want to declare this within your project's dependencies (in +``setup.py``), but that's up to you. + + +Create Tasks File +----------------- + +The ``invoke`` command will invoke tasks which we have defined in a tasks file. +(Duh!) + +We will now create a file at ``~/src/rattail-tutorial/tasks.py`` and in it +place some minimal contents: + +.. code-block:: python3 + + # -*- coding: utf-8; -*- + """ + Tasks for 'rattail-tutorial' project + """ + + from invoke import task + + # this is needed to read current `__version__` value + #import os + #here = os.path.abspath(os.path.dirname(__file__)) + #exec(open(os.path.join(here, 'rattail_tutorial', '_version.py')).read()) + + + @task + def release(c): + """ + Release a new version of `rattail-tutorial`. + """ + # clear out previous package info + c.run('rm -rf rattail_tutorial.egg-info') + + # build fresh package! + c.run('python setup.py sdist --formats=gztar') + + # enable this if you intend to publish package to PyPI + #c.run('twine upload dist/rattail-tutorial-{}.tar.gz'.format(__version__)) + +If you're creating your own project then you can use the above as a starting +point for your own file. Instead of using ``twine`` to upload the package to +`PyPI`_, you may need to push to some private package repository which you +control. + +.. _PyPI: https://pypi.org/ + + +Run Release Task +---------------- + +As you can see above, ``release`` is the one and only task we have defined so +far. In most cases that will be the only task you ever define for the project, +but YMMV. + +At any rate it's all we need for now, so let's run it:: + + cd ~/src/rattail-tutorial + invoke release + +If you're feeling lazy you can even shorten that second one to:: + + inv release + +This will build a new "release" which may then be found within e.g. the +``~/src/rattail-tutorial/dist/`` folder. Depending on the specifics of your +tasks file, this release may also be uploaded to some (public or private) +package index. diff --git a/docs/run-webapp.rst b/docs/run-webapp.rst new file mode 100644 index 0000000..42c0f9b --- /dev/null +++ b/docs/run-webapp.rst @@ -0,0 +1,77 @@ + +.. highlight:: sh + +Run the Web App +=============== + +At this point we assume you already have a project installed to your +virtualenv, and have done basic configuration as well as established your app +database. + + +Make/Edit Config +---------------- + +If you've been following along with this tutorial you may have already done +this step, but in any case we'll revisit now. + +If you do *not* yet have a file at e.g. ``/srv/envs/rattut/app/web.conf`` then +you should now run:: + + cdvirtualenv app + rattail make-config -T web + +Then you must edit the generated file, looking for TODO notes and such, and +generally tweaking things to your liking. + + +Start the Web App +----------------- + +The web app is effectively a daemon, in that it's meant to be a long-running +process which continues to listen for and respond to incoming requests. + +In production, this may be wired up in various ways, but for now we're only +concerned with development, where we'll be starting the web app "server" from +command line:: + + cdvirtualenv + bin/pserve --reload file+ini:app/web.conf + +Note that this command will "block" - meaning control will not immediately fall +back to your shell prompt. You may use Ctrl+C whenever you like, to kill the +web app. + + +Browse the Web App +------------------ + +This will only work when the above ``pserve`` command is running, but assuming +it is currently, you can access the web app at http://localhost:9080/ + +Note that the default ``web.conf`` specifies 9080 as the port on which the web +app will listen. You can modify this as needed, but if you do, and are also +using Vagrant, you may also need to modify your ``Vagrantfile`` (and do a +``vagrant reload``). + + +Login to Web App +---------------- + +If you've been following along with the tutorial then you probably have already +created an admin account for yourself. But in case you haven't, please see +:ref:`make-user`. + +Once that's set then you should be able to login to the web app with those same +credentials. + +The very first thing you see is likely "not much" - most of the menu will be +hidden to you, since by default you do not have sufficient permissions to +access the features they represent. + +However you are an "admin" user - which really just means your user account +belongs to the special "Administrators" role. This role is special in that +anyone who belongs to it, is given an extra "Become Root" option in the menu. +This works similarly to the Linux "root" concept, in that if you become root, +you will be implicitly granted *all* permissions and nothing will be hidden +from you. This lasts until you "stop being root" or logout. diff --git a/docs/start-docs.rst b/docs/start-docs.rst index 4b937f7..1878a2a 100644 --- a/docs/start-docs.rst +++ b/docs/start-docs.rst @@ -1,8 +1,8 @@ .. highlight:: sh -Documenting Your Project -======================== +Document Your Project +===================== At this point we assume you already have created a new project, and established the Git repo for it etc. diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f2c9f05 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,62 @@ + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + + +[project] +name = "rattail-tutorial" +version = "0.3.1" +description = "Rattail Development Tutorial" +readme = "README.md" +authors = [{name = "Lance Edgar", email = "lance@edbob.org"}] +license = {text = "GNU GPL v3+"} +classifiers = [ + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Environment :: Web Environment", + "Framework :: Pyramid", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Topic :: Office/Business", +] +dependencies = [ + "furo", + "invoke", + "psycopg2", + "rattail[db,bouncer]", + "Sphinx", + "Tailbone", + "tox", +] + + +[project.scripts] +rattail_tutorial = "rattail_tutorial.commands:rattail_tutorial_typer" + + +[project.entry-points."paste.app_factory"] +main = "rattail_tutorial.web.app:main" + + +[project.entry-points."rattail.config.extensions"] +rattail_tutorial = "rattail_tutorial.config:RattailTutorialConfig" + + +[project.entry-points."rattail.emails"] +rattail_tutorial = "rattail_tutorial.emails" + + +[project.urls] +Homepage = "https://rattailproject.org" +repository = "https://forgejo.wuttaproject.org/rattail/rattail-tutorial" +Changelog = "https://forgejo.wuttaproject.org/rattail/rattail-tutorial/src/branch/master/CHANGELOG.md" + + +[tool.commitizen] +version_provider = "pep621" +tag_format = "v$version" +update_changelog_on_bump = true diff --git a/rattail_tutorial/_version.py b/rattail_tutorial/_version.py index e41b669..fcfc0bd 100644 --- a/rattail_tutorial/_version.py +++ b/rattail_tutorial/_version.py @@ -1,3 +1,6 @@ # -*- coding: utf-8; -*- -__version__ = '0.1.0' +from importlib.metadata import version + + +__version__ = version('rattail-tutorial') diff --git a/rattail_tutorial/commands.py b/rattail_tutorial/commands.py index 78e2dd5..87c50f9 100644 --- a/rattail_tutorial/commands.py +++ b/rattail_tutorial/commands.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2024 Lance Edgar # # This file is part of Rattail. # @@ -26,36 +26,22 @@ Rattail Tutorial commands import sys -from rattail import commands +import typer -from rattail_tutorial import __version__ +from rattail.commands.typer import make_typer -def main(*args): - """ - Main entry point for Rattail Tutorial command system - """ - args = list(args or sys.argv[1:]) - cmd = Command() - cmd.run(*args) +rattail_tutorial_typer = make_typer( + name='rattail_tutorial', + help="Rattail Tutorial (custom Rattail system)" +) -class Command(commands.Command): - """ - Main command for Rattail Tutorial - """ - name = 'rattail_tutorial' - version = __version__ - description = "Rattail Tutorial (custom Rattail system)" - long_description = '' - - -class HelloWorld(commands.Subcommand): +@rattail_tutorial_typer.command() +def hello( + ctx: typer.Context, +): """ The requisite 'hello world' example """ - name = 'hello' - description = __doc__.strip() - - def run(self, args): - self.stdout.write("hello world!\n") + sys.stdout.write("hello world!\n") diff --git a/rattail_tutorial/config.py b/rattail_tutorial/config.py index 1415ea1..5b54e1f 100644 --- a/rattail_tutorial/config.py +++ b/rattail_tutorial/config.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2024 Lance Edgar # # This file is part of Rattail. # @@ -24,10 +24,10 @@ Custom config """ -from rattail.config import ConfigExtension +from wuttjamaican.conf import WuttaConfigExtension -class Rattail_tutorialConfig(ConfigExtension): +class RattailTutorialConfig(WuttaConfigExtension): """ Rattail config extension for Rattail Tutorial """ @@ -36,5 +36,4 @@ class Rattail_tutorialConfig(ConfigExtension): def configure(self, config): # set some default config values - config.setdefault('rattail.mail', 'emails', 'rattail_tutorial.emails') - config.setdefault('tailbone', 'menus', 'rattail_tutorial.web.menus') + config.setdefault('tailbone.menus.handler', 'rattail_tutorial.web.menus:TutorialMenuHandler') diff --git a/rattail_tutorial/emails.py b/rattail_tutorial/emails.py index 1e82c6a..4359049 100644 --- a/rattail_tutorial/emails.py +++ b/rattail_tutorial/emails.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2024 Lance Edgar # # This file is part of Rattail. # @@ -26,9 +26,6 @@ Custom email profiles from rattail.mail import Email -# bring in some common ones from rattail -from rattail.emails import datasync_error_watcher_get_changes, filemon_action_error - class rattail_import_sample_updates(Email): """ diff --git a/rattail_tutorial/web/menus.py b/rattail_tutorial/web/menus.py index b9b6d40..402ca8a 100644 --- a/rattail_tutorial/web/menus.py +++ b/rattail_tutorial/web/menus.py @@ -2,7 +2,7 @@ ################################################################################ # # Rattail -- Retail Software Framework -# Copyright © 2010-2019 Lance Edgar +# Copyright © 2010-2024 Lance Edgar # # This file is part of Rattail. # @@ -24,166 +24,139 @@ Web Menus """ +from tailbone import menus as base -def simple_menus(request): - url = request.route_url - menus = [ - { +class TutorialMenuHandler(base.MenuHandler): + """ + Demo menu handler + """ + + def make_menus(self, request, **kwargs): + + products_menu = self.make_products_menu(request) + + vendors_menu = self.make_vendors_menu(request) + + company_menu = self.make_company_menu(request) + + batches_menu = self.make_batches_menu(request) + + admin_menu = self.make_admin_menu(request, + include_stores=False, + include_tenders=False) + + menus = [ + products_menu, + vendors_menu, + company_menu, + batches_menu, + admin_menu, + ] + + return menus + + def make_products_menu(self, request, **kwargs): + return { 'title': "Products", 'type': 'menu', 'items': [ { 'title': "Products", - 'url': url('products'), + 'route': 'products', 'perm': 'products.list', }, { 'title': "Brands", - 'url': url('brands'), + 'route': 'brands', 'perm': 'brands.list', }, { 'title': "Report Codes", - 'url': url('reportcodes'), + 'route': 'reportcodes', 'perm': 'reportcodes.list', }, ], - }, - { + } + + def make_vendors_menu(self, request, **kwargs): + return { 'title': "Vendors", 'type': 'menu', 'items': [ { 'title': "Vendors", - 'url': url('vendors'), + 'route': 'vendors', 'perm': 'vendors.list', }, {'type': 'sep'}, { 'title': "Catalogs", - 'url': url('vendorcatalogs'), + 'route': 'vendorcatalogs', 'perm': 'vendorcatalogs.list', }, { 'title': "Upload New Catalog", - 'url': url('vendorcatalogs.create'), + 'route': 'vendorcatalogs.create', 'perm': 'vendorcatalogs.create', }, ], - }, - { + } + + def make_company_menu(self, request, **kwargs): + return { 'title': "Company", 'type': 'menu', 'items': [ { 'title': "Stores", - 'url': url('stores'), + 'route': 'stores', 'perm': 'stores.list', }, { 'title': "Departments", - 'url': url('departments'), + 'route': 'departments', 'perm': 'departments.list', }, { 'title': "Subdepartments", - 'url': url('subdepartments'), + 'route': 'subdepartments', 'perm': 'subdepartments.list', }, {'type': 'sep'}, { 'title': "Employees", - 'url': url('employees'), + 'route': 'employees', 'perm': 'employees.list', }, {'type': 'sep'}, { 'title': "Customers", - 'url': url('customers'), + 'route': 'customers', 'perm': 'customers.list', }, { 'title': "Customer Groups", - 'url': url('customergroups'), + 'route': 'customergroups', 'perm': 'customergroups.list', }, ], - }, - { + } + + def make_batches_menu(self, request, **kwargs): + return { 'title': "Batches", 'type': 'menu', 'items': [ { 'title': "Handheld", - 'url': url('batch.handheld'), + 'route': 'batch.handheld', 'perm': 'batch.handheld.list', }, { 'title': "Inventory", - 'url': url('batch.inventory'), + 'route': 'batch.inventory', 'perm': 'batch.inventory.list', }, ], - }, - { - 'title': "Admin", - 'type': 'menu', - 'items': [ - { - 'title': "Users", - 'url': url('users'), - 'perm': 'users.list', - }, - { - 'title': "User Events", - 'url': url('userevents'), - 'perm': 'userevents.list', - }, - { - 'title': "Roles", - 'url': url('roles'), - 'perm': 'roles.list', - }, - {'type': 'sep'}, - { - 'title': "App Settings", - 'url': url('appsettings'), - 'perm': 'settings.list', - }, - { - 'title': "Email Settings", - 'url': url('emailprofiles'), - 'perm': 'emailprofiles.list', - }, - { - 'title': "Email Attempts", - 'url': url('email_attempts'), - 'perm': 'email_attempts.list', - }, - { - 'title': "Raw Settings", - 'url': url('settings'), - 'perm': 'settings.list', - }, - {'type': 'sep'}, - { - 'title': "DataSync Changes", - 'url': url('datasyncchanges'), - 'perm': 'datasync.list', - }, - { - 'title': "Tables", - 'url': url('tables'), - 'perm': 'tables.list', - }, - { - 'title': "Rattail Tutorial Upgrades", - 'url': url('upgrades'), - 'perm': 'upgrades.list', - }, - ], - }, - ] - - return menus + } diff --git a/rattail_tutorial/web/templates/home.mako b/rattail_tutorial/web/templates/home.mako deleted file mode 100644 index 51360f4..0000000 --- a/rattail_tutorial/web/templates/home.mako +++ /dev/null @@ -1,14 +0,0 @@ -## -*- coding: utf-8; mode: html; -*- -<%inherit file="tailbone:templates/home.mako" /> - -<%def name="title()">Home%def> - -