Compare commits
	
		
			No commits in common. "master" and "v0.1.0" have entirely different histories.
		
	
	
		
	
		
					 24 changed files with 286 additions and 621 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -1,6 +1,3 @@
 | 
				
			||||||
*~
 | 
					 | 
				
			||||||
*.pyc
 | 
					 | 
				
			||||||
dist/
 | 
					 | 
				
			||||||
rattail_tutorial.egg-info/
 | 
					rattail_tutorial.egg-info/
 | 
				
			||||||
docs/_build/
 | 
					docs/_build/
 | 
				
			||||||
.tox/
 | 
					.tox/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								CHANGELOG.md
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								CHANGELOG.md
									
										
									
									
									
								
							| 
						 | 
					@ -5,33 +5,6 @@ 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/)
 | 
					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).
 | 
					and this project (probably doesn't) adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## v0.3.2 (2025-09-20)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### Fix
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- fix config extension entry point
 | 
					 | 
				
			||||||
- avoid deprecated base class for config extension
 | 
					 | 
				
			||||||
- update config for custom emails
 | 
					 | 
				
			||||||
- refactor custom menu builder, to inherit some menus
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## 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
 | 
					## [0.1.0] - 2019-08-15
 | 
				
			||||||
### Changed
 | 
					### Changed
 | 
				
			||||||
- Initial version, with very basic (mostly generated) app.
 | 
					- Initial version, with very basic (mostly generated) app.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,13 @@
 | 
				
			||||||
 | 
					.. -*- mode: rst -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# rattail-tutorial
 | 
					rattail-tutorial
 | 
				
			||||||
 | 
					================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
This project is intended for use as a "tutorial" for Rattail app development.
 | 
					This project is intended for use as a "tutorial" for Rattail app development.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It contains documentation for the tutorial itself, but also contains
 | 
					It contains documentation for the tutorial itself, but also contains
 | 
				
			||||||
code for the tutorial app, which users may run locally for testing.
 | 
					code for the tutorial app, which users may run locally for testing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
See the [Rattail website](https://rattailproject.org/) for more info.
 | 
					See the `Rattail website`_ for more info.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _`Rattail website`: https://rattailproject.org/
 | 
				
			||||||
							
								
								
									
										3
									
								
								Vagrantfile
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								Vagrantfile
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -5,7 +5,4 @@ Vagrant.configure("2") do |config|
 | 
				
			||||||
  # target machine runs Debian 10 "buster"
 | 
					  # target machine runs Debian 10 "buster"
 | 
				
			||||||
  config.vm.box = "debian/buster64"
 | 
					  config.vm.box = "debian/buster64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # rattail-tutorial web app
 | 
					 | 
				
			||||||
  config.vm.network "forwarded_port", guest: 9080, host: 9080
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,14 +17,12 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -- Project information -----------------------------------------------------
 | 
					# -- Project information -----------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib.metadata import version as get_version
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
project = 'rattail-tutorial'
 | 
					project = 'rattail-tutorial'
 | 
				
			||||||
copyright = '2019-2024, Lance Edgar'
 | 
					copyright = '2019, Lance Edgar'
 | 
				
			||||||
author = 'Lance Edgar'
 | 
					author = 'Lance Edgar'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# The full version, including alpha/beta/rc tags
 | 
					# The full version, including alpha/beta/rc tags
 | 
				
			||||||
release = get_version('rattail-tutorial')
 | 
					release = '0.1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -- General configuration ---------------------------------------------------
 | 
					# -- General configuration ---------------------------------------------------
 | 
				
			||||||
| 
						 | 
					@ -53,7 +51,7 @@ todo_include_todos = True
 | 
				
			||||||
# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
					# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
				
			||||||
# a list of builtin themes.
 | 
					# a list of builtin themes.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
html_theme = 'furo'
 | 
					html_theme = 'alabaster'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Add any paths that contain custom static files (such as style sheets) here,
 | 
					# 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,
 | 
					# 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
 | 
					   mkdir -p ~/src
 | 
				
			||||||
   cd ~/src
 | 
					   cd ~/src
 | 
				
			||||||
   git clone https://forgejo.wuttaproject.org/rattail/rattail-tutorial.git
 | 
					   git clone https://rattailproject.org/git/rattail-tutorial.git
 | 
				
			||||||
   pip install -e rattail-tutorial
 | 
					   pip install -e rattail-tutorial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Creating the Project
 | 
					Creating the Project
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,89 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
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.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,22 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
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,10 +55,6 @@ Table of Contents
 | 
				
			||||||
   create-project
 | 
					   create-project
 | 
				
			||||||
   start-docs
 | 
					   start-docs
 | 
				
			||||||
   configure
 | 
					   configure
 | 
				
			||||||
   pkg-release
 | 
					 | 
				
			||||||
   make-db
 | 
					 | 
				
			||||||
   run-webapp
 | 
					 | 
				
			||||||
   customize/index
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Indices and tables
 | 
					Indices and tables
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,87 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
.. 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.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,118 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
.. 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.
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,77 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
.. 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
 | 
					.. highlight:: sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Document Your Project
 | 
					Documenting Your Project
 | 
				
			||||||
=====================
 | 
					========================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
At this point we assume you already have created a new project, and established
 | 
					At this point we assume you already have created a new project, and established
 | 
				
			||||||
the Git repo for it etc.
 | 
					the Git repo for it etc.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,62 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
[build-system]
 | 
					 | 
				
			||||||
requires = ["hatchling"]
 | 
					 | 
				
			||||||
build-backend = "hatchling.build"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[project]
 | 
					 | 
				
			||||||
name = "rattail-tutorial"
 | 
					 | 
				
			||||||
version = "0.3.2"
 | 
					 | 
				
			||||||
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."wutta.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,6 +1,3 @@
 | 
				
			||||||
# -*- coding: utf-8; -*-
 | 
					# -*- coding: utf-8; -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from importlib.metadata import version
 | 
					__version__ = '0.1.0'
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
__version__ = version('rattail-tutorial')
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  Rattail -- Retail Software Framework
 | 
					#  Rattail -- Retail Software Framework
 | 
				
			||||||
#  Copyright © 2010-2024 Lance Edgar
 | 
					#  Copyright © 2010-2019 Lance Edgar
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  This file is part of Rattail.
 | 
					#  This file is part of Rattail.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
| 
						 | 
					@ -26,22 +26,36 @@ Rattail Tutorial commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import typer
 | 
					from rattail import commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rattail.commands.typer import make_typer
 | 
					from rattail_tutorial import __version__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rattail_tutorial_typer = make_typer(
 | 
					def main(*args):
 | 
				
			||||||
    name='rattail_tutorial',
 | 
					    """
 | 
				
			||||||
    help="Rattail Tutorial (custom Rattail system)"
 | 
					    Main entry point for Rattail Tutorial command system
 | 
				
			||||||
)
 | 
					    """
 | 
				
			||||||
 | 
					    args = list(args or sys.argv[1:])
 | 
				
			||||||
 | 
					    cmd = Command()
 | 
				
			||||||
 | 
					    cmd.run(*args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@rattail_tutorial_typer.command()
 | 
					class Command(commands.Command):
 | 
				
			||||||
def hello(
 | 
					    """
 | 
				
			||||||
        ctx: typer.Context,
 | 
					    Main command for Rattail Tutorial
 | 
				
			||||||
):
 | 
					    """
 | 
				
			||||||
 | 
					    name = 'rattail_tutorial'
 | 
				
			||||||
 | 
					    version = __version__
 | 
				
			||||||
 | 
					    description = "Rattail Tutorial (custom Rattail system)"
 | 
				
			||||||
 | 
					    long_description = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HelloWorld(commands.Subcommand):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    The requisite 'hello world' example
 | 
					    The requisite 'hello world' example
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    sys.stdout.write("hello world!\n")
 | 
					    name = 'hello'
 | 
				
			||||||
 | 
					    description = __doc__.strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self, args):
 | 
				
			||||||
 | 
					        self.stdout.write("hello world!\n")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  Rattail -- Retail Software Framework
 | 
					#  Rattail -- Retail Software Framework
 | 
				
			||||||
#  Copyright © 2010-2024 Lance Edgar
 | 
					#  Copyright © 2010-2019 Lance Edgar
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  This file is part of Rattail.
 | 
					#  This file is part of Rattail.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
| 
						 | 
					@ -24,10 +24,10 @@
 | 
				
			||||||
Custom config
 | 
					Custom config
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from wuttjamaican.conf import WuttaConfigExtension
 | 
					from rattail.config import ConfigExtension
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RattailTutorialConfig(WuttaConfigExtension):
 | 
					class Rattail_tutorialConfig(ConfigExtension):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Rattail config extension for Rattail Tutorial
 | 
					    Rattail config extension for Rattail Tutorial
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -36,4 +36,5 @@ class RattailTutorialConfig(WuttaConfigExtension):
 | 
				
			||||||
    def configure(self, config):
 | 
					    def configure(self, config):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # set some default config values
 | 
					        # set some default config values
 | 
				
			||||||
        config.setdefault('tailbone.menus.handler', 'rattail_tutorial.web.menus:TutorialMenuHandler')
 | 
					        config.setdefault('rattail.mail', 'emails', 'rattail_tutorial.emails')
 | 
				
			||||||
 | 
					        config.setdefault('tailbone', 'menus', 'rattail_tutorial.web.menus')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  Rattail -- Retail Software Framework
 | 
					#  Rattail -- Retail Software Framework
 | 
				
			||||||
#  Copyright © 2010-2024 Lance Edgar
 | 
					#  Copyright © 2010-2019 Lance Edgar
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  This file is part of Rattail.
 | 
					#  This file is part of Rattail.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,9 @@ Custom email profiles
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rattail.mail import Email
 | 
					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):
 | 
					class rattail_import_sample_updates(Email):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
################################################################################
 | 
					################################################################################
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  Rattail -- Retail Software Framework
 | 
					#  Rattail -- Retail Software Framework
 | 
				
			||||||
#  Copyright © 2010-2024 Lance Edgar
 | 
					#  Copyright © 2010-2019 Lance Edgar
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
#  This file is part of Rattail.
 | 
					#  This file is part of Rattail.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
| 
						 | 
					@ -24,139 +24,166 @@
 | 
				
			||||||
Web Menus
 | 
					Web Menus
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tailbone import menus as base
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def simple_menus(request):
 | 
				
			||||||
 | 
					    url = request.route_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TutorialMenuHandler(base.MenuHandler):
 | 
					    menus = [
 | 
				
			||||||
    """
 | 
					        {
 | 
				
			||||||
    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",
 | 
					            'title': "Products",
 | 
				
			||||||
            'type': 'menu',
 | 
					            'type': 'menu',
 | 
				
			||||||
            'items': [
 | 
					            'items': [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Products",
 | 
					                    'title': "Products",
 | 
				
			||||||
                    'route': 'products',
 | 
					                    'url': url('products'),
 | 
				
			||||||
                    'perm': 'products.list',
 | 
					                    'perm': 'products.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Brands",
 | 
					                    'title': "Brands",
 | 
				
			||||||
                    'route': 'brands',
 | 
					                    'url': url('brands'),
 | 
				
			||||||
                    'perm': 'brands.list',
 | 
					                    'perm': 'brands.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Report Codes",
 | 
					                    'title': "Report Codes",
 | 
				
			||||||
                    'route': 'reportcodes',
 | 
					                    'url': url('reportcodes'),
 | 
				
			||||||
                    'perm': 'reportcodes.list',
 | 
					                    'perm': 'reportcodes.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
    def make_vendors_menu(self, request, **kwargs):
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'title': "Vendors",
 | 
					            'title': "Vendors",
 | 
				
			||||||
            'type': 'menu',
 | 
					            'type': 'menu',
 | 
				
			||||||
            'items': [
 | 
					            'items': [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Vendors",
 | 
					                    'title': "Vendors",
 | 
				
			||||||
                    'route': 'vendors',
 | 
					                    'url': url('vendors'),
 | 
				
			||||||
                    'perm': 'vendors.list',
 | 
					                    'perm': 'vendors.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {'type': 'sep'},
 | 
					                {'type': 'sep'},
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Catalogs",
 | 
					                    'title': "Catalogs",
 | 
				
			||||||
                    'route': 'vendorcatalogs',
 | 
					                    'url': url('vendorcatalogs'),
 | 
				
			||||||
                    'perm': 'vendorcatalogs.list',
 | 
					                    'perm': 'vendorcatalogs.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Upload New Catalog",
 | 
					                    'title': "Upload New Catalog",
 | 
				
			||||||
                    'route': 'vendorcatalogs.create',
 | 
					                    'url': url('vendorcatalogs.create'),
 | 
				
			||||||
                    'perm': 'vendorcatalogs.create',
 | 
					                    'perm': 'vendorcatalogs.create',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
    def make_company_menu(self, request, **kwargs):
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'title': "Company",
 | 
					            'title': "Company",
 | 
				
			||||||
            'type': 'menu',
 | 
					            'type': 'menu',
 | 
				
			||||||
            'items': [
 | 
					            'items': [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Stores",
 | 
					                    'title': "Stores",
 | 
				
			||||||
                    'route': 'stores',
 | 
					                    'url': url('stores'),
 | 
				
			||||||
                    'perm': 'stores.list',
 | 
					                    'perm': 'stores.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Departments",
 | 
					                    'title': "Departments",
 | 
				
			||||||
                    'route': 'departments',
 | 
					                    'url': url('departments'),
 | 
				
			||||||
                    'perm': 'departments.list',
 | 
					                    'perm': 'departments.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Subdepartments",
 | 
					                    'title': "Subdepartments",
 | 
				
			||||||
                    'route': 'subdepartments',
 | 
					                    'url': url('subdepartments'),
 | 
				
			||||||
                    'perm': 'subdepartments.list',
 | 
					                    'perm': 'subdepartments.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {'type': 'sep'},
 | 
					                {'type': 'sep'},
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Employees",
 | 
					                    'title': "Employees",
 | 
				
			||||||
                    'route': 'employees',
 | 
					                    'url': url('employees'),
 | 
				
			||||||
                    'perm': 'employees.list',
 | 
					                    'perm': 'employees.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {'type': 'sep'},
 | 
					                {'type': 'sep'},
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Customers",
 | 
					                    'title': "Customers",
 | 
				
			||||||
                    'route': 'customers',
 | 
					                    'url': url('customers'),
 | 
				
			||||||
                    'perm': 'customers.list',
 | 
					                    'perm': 'customers.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Customer Groups",
 | 
					                    'title': "Customer Groups",
 | 
				
			||||||
                    'route': 'customergroups',
 | 
					                    'url': url('customergroups'),
 | 
				
			||||||
                    'perm': 'customergroups.list',
 | 
					                    'perm': 'customergroups.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        }
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
    def make_batches_menu(self, request, **kwargs):
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'title': "Batches",
 | 
					            'title': "Batches",
 | 
				
			||||||
            'type': 'menu',
 | 
					            'type': 'menu',
 | 
				
			||||||
            'items': [
 | 
					            'items': [
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Handheld",
 | 
					                    'title': "Handheld",
 | 
				
			||||||
                    'route': 'batch.handheld',
 | 
					                    'url': url('batch.handheld'),
 | 
				
			||||||
                    'perm': 'batch.handheld.list',
 | 
					                    'perm': 'batch.handheld.list',
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'title': "Inventory",
 | 
					                    'title': "Inventory",
 | 
				
			||||||
                    'route': 'batch.inventory',
 | 
					                    'url': url('batch.inventory'),
 | 
				
			||||||
                    'perm': 'batch.inventory.list',
 | 
					                    '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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								rattail_tutorial/web/templates/home.mako
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								rattail_tutorial/web/templates/home.mako
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					## -*- 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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								rattail_tutorial/web/templates/login.mako
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								rattail_tutorial/web/templates/login.mako
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					## -*- 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,6 +48,7 @@ def includeme(config):
 | 
				
			||||||
    config.include('tailbone.views.reportcodes')
 | 
					    config.include('tailbone.views.reportcodes')
 | 
				
			||||||
    config.include('tailbone.views.roles')
 | 
					    config.include('tailbone.views.roles')
 | 
				
			||||||
    config.include('tailbone.views.settings')
 | 
					    config.include('tailbone.views.settings')
 | 
				
			||||||
 | 
					    config.include('tailbone.views.shifts')
 | 
				
			||||||
    config.include('tailbone.views.stores')
 | 
					    config.include('tailbone.views.stores')
 | 
				
			||||||
    config.include('tailbone.views.subdepartments')
 | 
					    config.include('tailbone.views.subdepartments')
 | 
				
			||||||
    config.include('tailbone.views.users')
 | 
					    config.include('tailbone.views.users')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										118
									
								
								setup.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								setup.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,118 @@
 | 
				
			||||||
 | 
					# -*- 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
									
										
									
									
									
								
							
							
						
						
									
										38
									
								
								tasks.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,38 +0,0 @@
 | 
				
			||||||
# -*- 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue