Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
06394dc3e3 | ||
![]() |
70b103ebd8 | ||
![]() |
b96c828fa3 | ||
![]() |
1fb3ee10d0 | ||
![]() |
95f8ae318a | ||
![]() |
f0dc65ee08 | ||
![]() |
ab12cba1d5 | ||
![]() |
1c14ad72cd | ||
![]() |
4c9c321608 | ||
![]() |
4588fb8179 | ||
![]() |
43a6c56844 | ||
![]() |
3af49e2e62 | ||
![]() |
95e89a6081 | ||
![]() |
088fd4a6a5 | ||
![]() |
2adb18534c | ||
![]() |
1660774ad2 | ||
![]() |
6285ba3cc8 | ||
![]() |
5fb47aa4c0 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
|||
*~
|
||||
*.pyc
|
||||
dist/
|
||||
rattail_tutorial.egg-info/
|
||||
docs/_build/
|
||||
.tox/
|
||||
|
|
18
CHANGELOG.md
18
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.
|
||||
|
|
|
@ -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.
|
3
Vagrantfile
vendored
3
Vagrantfile
vendored
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
89
docs/customize/disable-web-view.rst
Normal file
89
docs/customize/disable-web-view.rst
Normal file
|
@ -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.
|
22
docs/customize/index.rst
Normal file
22
docs/customize/index.rst
Normal file
|
@ -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
|
|
@ -55,6 +55,10 @@ Table of Contents
|
|||
create-project
|
||||
start-docs
|
||||
configure
|
||||
pkg-release
|
||||
make-db
|
||||
run-webapp
|
||||
customize/index
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
|
87
docs/make-db.rst
Normal file
87
docs/make-db.rst
Normal file
|
@ -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.
|
118
docs/pkg-release.rst
Normal file
118
docs/pkg-release.rst
Normal file
|
@ -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.
|
77
docs/run-webapp.rst
Normal file
77
docs/run-webapp.rst
Normal file
|
@ -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.
|
|
@ -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.
|
||||
|
|
62
pyproject.toml
Normal file
62
pyproject.toml
Normal file
|
@ -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
|
|
@ -1,3 +1,6 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
__version__ = '0.1.0'
|
||||
from importlib.metadata import version
|
||||
|
||||
|
||||
__version__ = version('rattail-tutorial')
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
## -*- coding: utf-8; mode: html; -*-
|
||||
<%inherit file="tailbone:templates/home.mako" />
|
||||
|
||||
<%def name="title()">Home</%def>
|
||||
|
||||
<div class="logo">
|
||||
|
||||
## ${h.image(request.static_url('rattail_tutorial.web:static/img/rattail_tutorial.jpg'), "Rattail Tutorial Logo", id='logo', width=500)}
|
||||
${h.image(request.static_url('tailbone:static/img/home_logo.png'), "Rattail Logo")}
|
||||
|
||||
</div>
|
||||
|
||||
<h1 style="text-align: center;">Welcome to Rattail Tutorial</h1>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
## -*- coding: utf-8; mode: html; -*-
|
||||
<%inherit file="tailbone:templates/login.mako" />
|
||||
|
||||
<%def name="extra_styles()">
|
||||
${parent.extra_styles()}
|
||||
<style type="text/css">
|
||||
#logo {
|
||||
margin: 40px auto;
|
||||
}
|
||||
</style>
|
||||
</%def>
|
||||
|
||||
<%def name="logo()">
|
||||
## ${h.image(request.static_url('ratbob.web:static/img/ratbob.jpg'), "Ratbob Logo", id='logo', width=500)}
|
||||
${h.image(request.static_url('tailbone:static/img/home_logo.png'), "Rattail Logo", id='logo')}
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
|
@ -48,7 +48,6 @@ def includeme(config):
|
|||
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')
|
||||
|
|
118
setup.py
118
setup.py
|
@ -1,118 +0,0 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 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 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 General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Rattail Tutorial setup script
|
||||
"""
|
||||
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
exec(open(os.path.join(here, 'rattail_tutorial', '_version.py')).read())
|
||||
README = open(os.path.join(here, 'README.rst')).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
|
||||
|
||||
'psycopg2', # 2.6.2
|
||||
'rattail[auth,db,bouncer]', # 0.7.25
|
||||
'Tailbone', # 0.5.29
|
||||
'tox', # 3.13.2
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name = "rattail-tutorial",
|
||||
version = __version__,
|
||||
author = "Lance Edgar",
|
||||
author_email = "lance@edbob.org",
|
||||
url = "https://rattailproject.org",
|
||||
license = "GNU GPL v3",
|
||||
description = "Rattail Development Tutorial",
|
||||
long_description = README,
|
||||
|
||||
classifiers = [
|
||||
'Private :: Do Not Upload',
|
||||
'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 :: 2.7',
|
||||
'Topic :: Office/Business',
|
||||
],
|
||||
|
||||
install_requires = requires,
|
||||
packages = find_packages(),
|
||||
include_package_data = True,
|
||||
|
||||
entry_points = {
|
||||
|
||||
'rattail.config.extensions': [
|
||||
'rattail_tutorial = rattail_tutorial.config:Rattail_tutorialConfig',
|
||||
],
|
||||
|
||||
'console_scripts': [
|
||||
'rattail_tutorial = rattail_tutorial.commands:main',
|
||||
],
|
||||
|
||||
'rattail_tutorial.commands': [
|
||||
'hello = rattail_tutorial.commands:HelloWorld',
|
||||
],
|
||||
|
||||
'paste.app_factory': [
|
||||
'main = rattail_tutorial.web.app:main',
|
||||
],
|
||||
},
|
||||
)
|
38
tasks.py
Normal file
38
tasks.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2024 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 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 General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Tasks for 'rattail-tutorial' package
|
||||
"""
|
||||
|
||||
from invoke import task
|
||||
|
||||
|
||||
@task
|
||||
def release(c):
|
||||
"""
|
||||
Release a new version of `rattail-tutorial`.
|
||||
"""
|
||||
c.run('rm -rf rattail_tutorial.egg-info')
|
||||
c.run('rm -rf dist')
|
||||
c.run('python -m build --sdist')
|
||||
c.run('twine upload dist/*')
|
Loading…
Reference in a new issue