Compare commits
136 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
87d84f005a | ||
![]() |
86abdea9d7 | ||
![]() |
8946121e8a | ||
![]() |
59e61357be | ||
![]() |
f18037b2fb | ||
![]() |
beddcf6987 | ||
![]() |
c67494a18c | ||
![]() |
e10b1fa2fd | ||
![]() |
19f4f62229 | ||
![]() |
6756899bbb | ||
![]() |
cbb5174ac8 | ||
![]() |
10e79be01a | ||
![]() |
e071a9ba25 | ||
![]() |
ef696b22b0 | ||
![]() |
c883e79a2a | ||
![]() |
5e466be42e | ||
![]() |
cc9ec47857 | ||
![]() |
5176acaf9e | ||
![]() |
c65cfe5144 | ||
![]() |
6b7c69265f | ||
![]() |
8287f02793 | ||
![]() |
d819d833af | ||
![]() |
ffe97e3a12 | ||
![]() |
ba534fbae1 | ||
![]() |
97db13a19f | ||
![]() |
3fb08869e0 | ||
![]() |
83a810cb0d | ||
![]() |
ecbc9abb10 | ||
![]() |
492cdc0329 | ||
![]() |
1c1611df88 | ||
![]() |
e7248b4589 | ||
![]() |
400133a98d | ||
![]() |
316bdfe1aa | ||
![]() |
6a8096391f | ||
![]() |
34a3ee8f36 | ||
![]() |
5481c115ba | ||
![]() |
16fdfd51ba | ||
![]() |
640ca783c7 | ||
![]() |
e6d4f7e303 | ||
![]() |
5cfe10c2e7 | ||
![]() |
9fc9138bce | ||
![]() |
a6871fac95 | ||
![]() |
a475db3fd5 | ||
![]() |
655dbea531 | ||
![]() |
e23e65155f | ||
![]() |
1f37a4bd37 | ||
![]() |
0c73ee07d6 | ||
![]() |
3384de9b63 | ||
![]() |
be2858084b | ||
![]() |
4288e1ce64 | ||
![]() |
8cb42712fd | ||
![]() |
06f959cd09 | ||
![]() |
2c8cc67043 | ||
![]() |
25bf17715c | ||
![]() |
05ac07d2e6 | ||
![]() |
2a34243e33 | ||
![]() |
1e06066353 | ||
![]() |
2619869ceb | ||
![]() |
34ab56247b | ||
![]() |
b9cf911688 | ||
![]() |
94a311828f | ||
![]() |
6e51686892 | ||
![]() |
2d3c4fd935 | ||
![]() |
852f9d4902 | ||
![]() |
a7656928f5 | ||
![]() |
f74101e7ae | ||
![]() |
4218d466ae | ||
![]() |
ebe0c2b479 | ||
![]() |
c5922c74ea | ||
![]() |
25a7d46588 | ||
![]() |
310baea00f | ||
![]() |
0729a8902f | ||
![]() |
b01f8425e5 | ||
![]() |
b044790d51 | ||
![]() |
099fe14de1 | ||
![]() |
89a4bc50d7 | ||
![]() |
17f443b36a | ||
![]() |
366594c53a | ||
![]() |
f1a01af3c9 | ||
![]() |
69316183f7 | ||
![]() |
92517605d8 | ||
![]() |
0bf3fcfba4 | ||
![]() |
07a36982d7 | ||
![]() |
d7462f415d | ||
![]() |
72a4c347d8 | ||
![]() |
f4a1868aaf | ||
![]() |
ece361a0e6 | ||
![]() |
db70870921 | ||
![]() |
5c3a95915b | ||
![]() |
e4620c8670 | ||
![]() |
a0ba89ecfd | ||
![]() |
d42f6a065b | ||
![]() |
f7eac8d707 | ||
![]() |
e401549e29 | ||
![]() |
5ebe3a001a | ||
![]() |
b0b7fc42b9 | ||
![]() |
b1875f5834 | ||
![]() |
520940bc81 | ||
![]() |
599bf7cc32 | ||
![]() |
15c44196bc | ||
![]() |
71c60c58a1 | ||
![]() |
9294d95ffb | ||
![]() |
b0a52dcec2 | ||
![]() |
46867b1154 | ||
![]() |
e3d9d7d4e5 | ||
![]() |
57c5f8ba5e | ||
![]() |
b7512e0b31 | ||
![]() |
e20a9b8123 | ||
![]() |
452dc934fa | ||
![]() |
e91858b567 | ||
![]() |
12c8b3b8dd | ||
![]() |
ba95d39780 | ||
![]() |
caaf2842dc | ||
![]() |
8ed906020b | ||
![]() |
42e2373b08 | ||
![]() |
d5a9b5c471 | ||
![]() |
82d5bcc0ed | ||
![]() |
8df12a1774 | ||
![]() |
fe19b5ae65 | ||
![]() |
b9021afd96 | ||
![]() |
ed1cc301a3 | ||
![]() |
7947f89b5f | ||
![]() |
9b9bd8ea0f | ||
![]() |
bae33491b6 | ||
![]() |
e9998cb597 | ||
![]() |
db9deffaa6 | ||
![]() |
b5c81244ac | ||
![]() |
9d9bc58a99 | ||
![]() |
fcb21b9671 | ||
![]() |
7a22b11e2e | ||
![]() |
d812e5465a | ||
![]() |
365d5847a4 | ||
![]() |
c61f5c8053 | ||
![]() |
50509f8f52 | ||
![]() |
94993cf553 | ||
![]() |
7f35fd828a |
.gitignoreCHANGELOG.mdCHANGES.rstREADME.mdREADME.rstpyproject.toml
rattail_demo
_version.pycommands.pyconfig.py
setup.pytasks.pydb
importing
shopfoo
web
app.pymenus.py
static
__init__.py
subscribers.pylibcache
templates
views
webapi.py
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
|||
*~
|
||||
*.pyc
|
||||
dist/
|
||||
rattail_demo.egg-info/
|
||||
|
|
46
CHANGELOG.md
Normal file
46
CHANGELOG.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
|
||||
# Changelog
|
||||
All notable changes to rattail will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.2.4 (2024-11-24)
|
||||
|
||||
### Fix
|
||||
|
||||
- update project links, kallithea -> forgejo
|
||||
- avoid deprecated base class for config extension
|
||||
- just use upstream `main()` for webapi
|
||||
- update menu config per wuttaweb
|
||||
- update config for default app model
|
||||
- remove unused alembic script
|
||||
|
||||
## v0.2.3 (2024-07-01)
|
||||
|
||||
### Fix
|
||||
|
||||
- use rattail function to create top-level command
|
||||
|
||||
## v0.2.2 (2024-06-30)
|
||||
|
||||
### Fix
|
||||
|
||||
- declare custom static libcache module for tailbone
|
||||
|
||||
## v0.2.1 (2024-06-30)
|
||||
|
||||
### Fix
|
||||
|
||||
- add butterball libcache via fanstatic
|
||||
- add command to purge shopfoo exports
|
||||
|
||||
## v0.2.0 (2024-06-10)
|
||||
|
||||
### Feat
|
||||
|
||||
- switch from setup.cfg to pyproject.toml + hatchling
|
||||
|
||||
## v0.1.0 (2016-12-08)
|
||||
|
||||
- initial release
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.1.0 (2016-12-08)
|
||||
------------------
|
||||
|
||||
* Initial release
|
9
README.md
Normal file
9
README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
# Rattail Demo
|
||||
|
||||
This project serves as a working demo, to illustrate various concepts
|
||||
of the Rattail software framework. See the [Rattail
|
||||
Wiki](https://rattailproject.org/moin/) for more info.
|
||||
|
||||
Note that it *can be* usable as a starting point for your own project(s),
|
||||
should you need one. But probably the Rattail Tutorial is a better one.
|
11
README.rst
11
README.rst
|
@ -1,11 +0,0 @@
|
|||
|
||||
Rattail Demo
|
||||
============
|
||||
|
||||
This project serves as a working demo, to illustrate various concepts of the
|
||||
Rattail software framework. See the `Rattail Wiki`_ for more info.
|
||||
|
||||
Note that it also aims to be usable as a starting point for your own
|
||||
project(s), should you need one.
|
||||
|
||||
.. _`Rattail Wiki`: https://rattailproject.org/moin/
|
61
pyproject.toml
Normal file
61
pyproject.toml
Normal file
|
@ -0,0 +1,61 @@
|
|||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
|
||||
[project]
|
||||
name = "rattail-demo"
|
||||
version = "0.2.4"
|
||||
description = "Rattail Software Demo"
|
||||
readme = "README.md"
|
||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Office/Business",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dependencies = [
|
||||
"invoke",
|
||||
"psycopg2",
|
||||
"rattail-tempmon",
|
||||
"Tailbone",
|
||||
"tailbone-corepos",
|
||||
"tailbone-woocommerce",
|
||||
"typer",
|
||||
"xlrd",
|
||||
]
|
||||
|
||||
|
||||
[project.scripts]
|
||||
rattail-demo = "rattail_demo.commands:rattail_demo_typer"
|
||||
|
||||
|
||||
[project.entry-points."fanstatic.libraries"]
|
||||
rattail_demo_libcache = "rattail_demo.web.static:libcache"
|
||||
|
||||
|
||||
[project.entry-points."paste.app_factory"]
|
||||
main = "rattail_demo.web.app:main"
|
||||
webapi = "rattail_demo.web.webapi:main"
|
||||
|
||||
|
||||
[project.entry-points."rattail.config.extensions"]
|
||||
rattail-demo = "rattail_demo.config:DemoConfigExtension"
|
||||
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://demo.rattailproject.org"
|
||||
Repository = "https://forgejo.wuttaproject.org/rattail/rattail-demo"
|
||||
Changelog = "https://forgejo.wuttaproject.org/rattail/rattail-demo/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 -*-
|
||||
# -*- coding: utf-8; -*-
|
||||
|
||||
__version__ = u'0.1.0'
|
||||
from importlib.metadata import version
|
||||
|
||||
|
||||
__version__ = version('rattail-demo')
|
||||
|
|
140
rattail_demo/commands.py
Normal file
140
rattail_demo/commands.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo Commands
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
|
||||
import typer
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from rattail.commands.typer import (make_typer, typer_get_runas_user,
|
||||
importer_command, file_exporter_command)
|
||||
from rattail.commands.importing import ImportCommandHandler
|
||||
from rattail.commands.purging import run_purge
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# nb. this is the top-level command
|
||||
rattail_demo_typer = make_typer(
|
||||
name='rattail_demo',
|
||||
help="Rattail Demo (custom Rattail system)"
|
||||
)
|
||||
|
||||
|
||||
@rattail_demo_typer.command()
|
||||
@file_exporter_command
|
||||
def export_shopfoo(
|
||||
ctx: typer.Context,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Export data to the Harvest system
|
||||
"""
|
||||
config = ctx.parent.rattail_config
|
||||
progress = ctx.parent.rattail_progress
|
||||
handler = ImportCommandHandler(
|
||||
config, import_handler_spec='rattail_demo.shopfoo.importing.rattail:FromRattailToShopfoo')
|
||||
kwargs['user'] = typer_get_runas_user(ctx)
|
||||
kwargs['handler_kwargs'] = {'output_dir': kwargs['output_dir']}
|
||||
handler.run(kwargs, progress=progress)
|
||||
|
||||
|
||||
@rattail_demo_typer.command()
|
||||
@importer_command
|
||||
def import_self(
|
||||
ctx: typer.Context,
|
||||
**kwargs
|
||||
):
|
||||
"""
|
||||
Update "cascading" Rattail data based on "core" Rattail data
|
||||
"""
|
||||
config = ctx.parent.rattail_config
|
||||
progress = ctx.parent.rattail_progress
|
||||
handler = ImportCommandHandler(
|
||||
config, import_handler_spec='rattail_demo.importing.local:FromRattailDemoToSelf')
|
||||
kwargs['user'] = typer_get_runas_user(ctx)
|
||||
handler.run(kwargs, progress=progress)
|
||||
|
||||
|
||||
@rattail_demo_typer.command()
|
||||
def purge_shopfoo(
|
||||
ctx: typer.Context,
|
||||
before: Annotated[
|
||||
datetime.datetime,
|
||||
typer.Option(formats=['%Y-%m-%d'],
|
||||
help="Use this date as cutoff, i.e. purge all data "
|
||||
"*before* this date. If not specified, will use "
|
||||
"--before-days to calculate instead.")] = None,
|
||||
before_days: Annotated[
|
||||
int,
|
||||
typer.Option(help="Calculate the cutoff date by subtracting this "
|
||||
"number of days from the current date, i.e. purge all "
|
||||
"data *before* the resulting date. Note that if you "
|
||||
"specify --before then that date will be used instead "
|
||||
"of calculating one from --before-days. If neither is "
|
||||
"specified then --before-days is used, with its default "
|
||||
"value.")] = 90,
|
||||
dry_run: Annotated[
|
||||
bool,
|
||||
typer.Option('--dry-run',
|
||||
help="Go through the full motions and allow logging "
|
||||
"etc. to occur, but rollback (abort) the transaction "
|
||||
"at the end.")] = False,
|
||||
):
|
||||
"""
|
||||
Purge old Shopfoo export data
|
||||
"""
|
||||
config = ctx.parent.rattail_config
|
||||
progress = ctx.parent.rattail_progress
|
||||
app = config.get_app()
|
||||
model = app.model
|
||||
|
||||
def finder(session, cutoff, dry_run=False):
|
||||
return session.query(model.ShopfooProductExport)\
|
||||
.filter(model.ShopfooProductExport.created < app.make_utc(cutoff))\
|
||||
.all()
|
||||
|
||||
def purger(session, export, cutoff, dry_run=False):
|
||||
uuid = export.uuid
|
||||
log.debug("purging export object %s: %s", uuid, export)
|
||||
session.delete(export)
|
||||
|
||||
# maybe delete associated files
|
||||
if not dry_run:
|
||||
session.flush()
|
||||
key = model.ShopfooProductExport.export_key
|
||||
path = config.export_filepath(key, uuid)
|
||||
if os.path.exists(path):
|
||||
shutil.rmtree(path)
|
||||
|
||||
return True
|
||||
|
||||
run_purge(config, "Shopfoo Export", "Shopfoo Exports",
|
||||
finder, purger,
|
||||
before=before.date() if before else None,
|
||||
before_days=before_days,
|
||||
default_before_days=90,
|
||||
dry_run=dry_run, progress=progress)
|
||||
|
||||
|
||||
@rattail_demo_typer.command()
|
||||
def install(
|
||||
ctx: typer.Context,
|
||||
):
|
||||
"""
|
||||
Install the Rattail Demo app
|
||||
"""
|
||||
from rattail.install import InstallHandler
|
||||
|
||||
config = ctx.parent.rattail_config
|
||||
handler = InstallHandler(config,
|
||||
app_title="Rattail Demo",
|
||||
app_package='rattail_demo',
|
||||
app_eggname='rattail_demo',
|
||||
app_pypiname='rattail_demo')
|
||||
handler.run()
|
32
rattail_demo/config.py
Normal file
32
rattail_demo/config.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo config extension
|
||||
"""
|
||||
|
||||
from wuttjamaican.conf import WuttaConfigExtension
|
||||
|
||||
|
||||
class DemoConfigExtension(WuttaConfigExtension):
|
||||
"""
|
||||
Rattail Demo config extension
|
||||
"""
|
||||
key = 'rattail-demo'
|
||||
|
||||
def configure(self, config):
|
||||
|
||||
config.setdefault('rattail', 'app_package', 'rattail_demo')
|
||||
|
||||
# tell rattail where our stuff lives
|
||||
config.setdefault('rattail', 'model_spec', 'rattail_demo.db.model')
|
||||
config.setdefault('rattail.trainwreck', 'model', 'rattail.trainwreck.db.model.defaults')
|
||||
config.setdefault('tailbone.static_libcache.module', 'rattail_demo.web.static')
|
||||
|
||||
# menus
|
||||
config.setdefault('rattail.web.menus.handler_spec', 'rattail_demo.web.menus:DemoMenuHandler')
|
||||
|
||||
# default app handlers
|
||||
config.setdefault('rattail', 'products.handler', 'rattail_corepos.products:CoreProductsHandler')
|
||||
|
||||
# default import handlers
|
||||
config.setdefault('rattail.importing', 'versions.handler', 'rattail_demo.importing.versions:FromRattailDemoToRattailDemoVersions')
|
||||
config.setdefault('rattail.importing', 'corepos_api.handler', 'rattail_demo.importing.corepos_api:FromCOREPOSToRattail')
|
0
rattail_demo/db/__init__.py
Normal file
0
rattail_demo/db/__init__.py
Normal file
28
rattail_demo/db/alembic/script.py.mako
Normal file
28
rattail_demo/db/alembic/script.py.mako
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8; mode: python; -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
branch_labels = ${repr(branch_labels)}
|
||||
depends_on = ${repr(depends_on)}
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import rattail.db.types
|
||||
${imports if imports else ""}
|
||||
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
|
@ -0,0 +1,75 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""initial tables
|
||||
|
||||
Revision ID: 2108f9efa758
|
||||
Revises: efb7cd318947
|
||||
Create Date: 2020-08-19 20:02:15.501843
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2108f9efa758'
|
||||
down_revision = None
|
||||
branch_labels = ('rattail_demo',)
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import rattail.db.types
|
||||
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
# demo_shopfoo_product
|
||||
op.create_table('demo_shopfoo_product',
|
||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||
sa.Column('product_uuid', sa.String(length=32), nullable=True),
|
||||
sa.Column('upc', sa.String(length=14), nullable=True),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.Column('price', sa.Numeric(precision=13, scale=2), nullable=True),
|
||||
sa.Column('enabled', sa.Boolean(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['product_uuid'], ['product.uuid'], name='demo_shopfoo_product_fk_product'),
|
||||
sa.PrimaryKeyConstraint('uuid')
|
||||
)
|
||||
op.create_table('demo_shopfoo_product_version',
|
||||
sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False),
|
||||
sa.Column('product_uuid', sa.String(length=32), autoincrement=False, nullable=True),
|
||||
sa.Column('upc', sa.String(length=14), autoincrement=False, nullable=True),
|
||||
sa.Column('description', sa.String(length=255), autoincrement=False, nullable=True),
|
||||
sa.Column('price', sa.Numeric(precision=13, scale=2), autoincrement=False, nullable=True),
|
||||
sa.Column('enabled', sa.Boolean(), autoincrement=False, nullable=True),
|
||||
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
|
||||
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
|
||||
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('uuid', 'transaction_id')
|
||||
)
|
||||
op.create_index(op.f('ix_demo_shopfoo_product_version_end_transaction_id'), 'demo_shopfoo_product_version', ['end_transaction_id'], unique=False)
|
||||
op.create_index(op.f('ix_demo_shopfoo_product_version_operation_type'), 'demo_shopfoo_product_version', ['operation_type'], unique=False)
|
||||
op.create_index(op.f('ix_demo_shopfoo_product_version_transaction_id'), 'demo_shopfoo_product_version', ['transaction_id'], unique=False)
|
||||
|
||||
# demo_shopfoo_product_export
|
||||
op.create_table('demo_shopfoo_product_export',
|
||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.Column('created_by_uuid', sa.String(length=32), nullable=False),
|
||||
sa.Column('record_count', sa.Integer(), nullable=True),
|
||||
sa.Column('filename', sa.String(length=255), nullable=True),
|
||||
sa.Column('uploaded', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name='demo_shopfoo_product_export_fk_created_by'),
|
||||
sa.PrimaryKeyConstraint('uuid')
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
||||
# demo_shopfoo_product_export
|
||||
op.drop_table('demo_shopfoo_product_export')
|
||||
|
||||
# demo_shopfoo_product
|
||||
op.drop_index(op.f('ix_demo_shopfoo_product_version_transaction_id'), table_name='demo_shopfoo_product_version')
|
||||
op.drop_index(op.f('ix_demo_shopfoo_product_version_operation_type'), table_name='demo_shopfoo_product_version')
|
||||
op.drop_index(op.f('ix_demo_shopfoo_product_version_end_transaction_id'), table_name='demo_shopfoo_product_version')
|
||||
op.drop_table('demo_shopfoo_product_version')
|
||||
op.drop_table('demo_shopfoo_product')
|
16
rattail_demo/db/model/__init__.py
Normal file
16
rattail_demo/db/model/__init__.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo data model
|
||||
"""
|
||||
|
||||
# bring in all the normal stuff from Rattail
|
||||
from rattail.db.model import *
|
||||
|
||||
# also bring in CORE-POS integration models
|
||||
from rattail_corepos.db.model import *
|
||||
|
||||
# also bring in WooCommerce integration models
|
||||
from rattail_woocommerce.db.model import *
|
||||
|
||||
# now bring in Demo-specific models
|
||||
from .shopfoo import ShopfooProduct, ShopfooProductExport
|
37
rattail_demo/db/model/shopfoo.py
Normal file
37
rattail_demo/db/model/shopfoo.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Database schema extensions for Shopfoo integration
|
||||
"""
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.db.model.shopfoo import ShopfooProductBase, ShopfooProductExportBase
|
||||
|
||||
|
||||
class ShopfooProduct(ShopfooProductBase, model.Base):
|
||||
"""
|
||||
Shopfoo-specific product cache table. Each record in this table *should*
|
||||
match exactly, what is in the actual "Shopfoo" system (even though that's
|
||||
made-up in this case).
|
||||
"""
|
||||
__tablename__ = 'demo_shopfoo_product'
|
||||
__versioned__ = {}
|
||||
|
||||
upc = sa.Column(sa.String(length=14), nullable=True)
|
||||
|
||||
description = sa.Column(sa.String(length=255), nullable=True)
|
||||
|
||||
price = sa.Column(sa.Numeric(precision=13, scale=2), nullable=True)
|
||||
|
||||
enabled = sa.Column(sa.Boolean(), nullable=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.description or self.upc or ""
|
||||
|
||||
|
||||
class ShopfooProductExport(ShopfooProductExportBase, model.Base):
|
||||
"""
|
||||
Shopfoo product exports
|
||||
"""
|
||||
__tablename__ = 'demo_shopfoo_product_export'
|
6
rattail_demo/importing/__init__.py
Normal file
6
rattail_demo/importing/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Importing into Rattail Demo
|
||||
"""
|
||||
|
||||
from . import model
|
31
rattail_demo/importing/corepos_api.py
Normal file
31
rattail_demo/importing/corepos_api.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
CORE-POS API -> Rattail Demo importing
|
||||
"""
|
||||
|
||||
from rattail_corepos.importing.corepos import api as base
|
||||
|
||||
|
||||
class FromCOREPOSToRattail(base.FromCOREPOSToRattail):
|
||||
"""
|
||||
Override some parts of CORE-POS API -> Rattail importing.
|
||||
"""
|
||||
|
||||
def get_importers(self):
|
||||
importers = super(FromCOREPOSToRattail, self).get_importers()
|
||||
importers['Store'] = StoreImporter
|
||||
return importers
|
||||
|
||||
|
||||
class StoreImporter(base.StoreImporter):
|
||||
"""
|
||||
Tweak how we import Store data from CORE-POS API.
|
||||
"""
|
||||
|
||||
def cache_query(self):
|
||||
model = self.model
|
||||
# we ignore any Store records which are not associated with CORE, so
|
||||
# the importer will never be tempted to delete them etc.
|
||||
return self.session.query(model.Store)\
|
||||
.join(model.CoreStore)\
|
||||
.filter(model.CoreStore.corepos_id != None)
|
61
rattail_demo/importing/local.py
Normal file
61
rattail_demo/importing/local.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo -> Rattail Demo "self" data import
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.importing.local import FromRattailSelfToRattail, FromRattailSelf
|
||||
from rattail.importing.shopfoo import ShopfooProductImporterMixin
|
||||
from rattail_demo import importing as rattail_demo_importing
|
||||
|
||||
|
||||
class FromRattailDemoToSelf(FromRattailSelfToRattail):
|
||||
"""
|
||||
Handler for Rattail Demo -> Rattail Demo ("self") imports
|
||||
"""
|
||||
|
||||
def get_importers(self):
|
||||
importers = OrderedDict()
|
||||
importers['ShopfooProduct'] = ShopfooProductImporter
|
||||
return importers
|
||||
|
||||
|
||||
class ShopfooProductImporter(ShopfooProductImporterMixin, FromRattailSelf, rattail_demo_importing.model.ShopfooProductImporter):
|
||||
"""
|
||||
Product -> ShopfooProduct
|
||||
"""
|
||||
supported_fields = [
|
||||
'uuid',
|
||||
'product_uuid',
|
||||
'upc',
|
||||
'description',
|
||||
'price',
|
||||
'enabled',
|
||||
]
|
||||
|
||||
def normalize_base_product_data(self, product):
|
||||
|
||||
price = None
|
||||
if product.regular_price:
|
||||
price = product.regular_price.price
|
||||
|
||||
return {
|
||||
'product_uuid': product.uuid,
|
||||
'upc': str(product.upc or '') or None,
|
||||
'description': product.full_description,
|
||||
'price': price,
|
||||
'enabled': True, # will maybe unset this in mark_unwanted()
|
||||
}
|
||||
|
||||
def product_is_unwanted(self, product, data):
|
||||
if super(ShopfooProductImporter, self).product_is_unwanted(product, data):
|
||||
return True
|
||||
if not data['price']: # let's say this is a required field for Shopfoo
|
||||
return True
|
||||
return False
|
||||
|
||||
def mark_unwanted(self, product, data):
|
||||
data = super(ShopfooProductImporter, self).mark_unwanted(product, data)
|
||||
data['enabled'] = False
|
||||
return data
|
18
rattail_demo/importing/model.py
Normal file
18
rattail_demo/importing/model.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo model importers
|
||||
"""
|
||||
|
||||
from rattail.importing.model import ToRattail
|
||||
from rattail_demo.db import model
|
||||
|
||||
|
||||
##############################
|
||||
# custom models
|
||||
##############################
|
||||
|
||||
class ShopfooProductImporter(ToRattail):
|
||||
"""
|
||||
Importer for ShopfooProduct data
|
||||
"""
|
||||
model_class = model.ShopfooProduct
|
28
rattail_demo/importing/versions.py
Normal file
28
rattail_demo/importing/versions.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo -> Rattail Demo "versions" data import
|
||||
"""
|
||||
|
||||
from rattail_demo.db import model
|
||||
from rattail.importing import versions as base
|
||||
from rattail_corepos.importing.versions import CoreposVersionMixin
|
||||
from rattail_woocommerce.importing.versions import WooVersionMixin
|
||||
|
||||
|
||||
class FromRattailDemoToRattailDemoVersions(base.FromRattailToRattailVersions,
|
||||
CoreposVersionMixin,
|
||||
WooVersionMixin):
|
||||
"""
|
||||
Handler for Rattail Demo -> Rattail Demo "versions" data import
|
||||
"""
|
||||
|
||||
def get_importers(self):
|
||||
importers = super(FromRattailDemoToRattailDemoVersions, self).get_importers()
|
||||
importers = self.add_corepos_importers(importers)
|
||||
importers = self.add_woocommerce_importers(importers)
|
||||
importers['ShopfooProduct'] = ShopfooProductImporter
|
||||
return importers
|
||||
|
||||
|
||||
class ShopfooProductImporter(base.VersionImporter):
|
||||
host_model_class = model.ShopfooProduct
|
0
rattail_demo/shopfoo/__init__.py
Normal file
0
rattail_demo/shopfoo/__init__.py
Normal file
6
rattail_demo/shopfoo/importing/__init__.py
Normal file
6
rattail_demo/shopfoo/importing/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Importing into Shopfoo
|
||||
"""
|
||||
|
||||
from . import model
|
28
rattail_demo/shopfoo/importing/model.py
Normal file
28
rattail_demo/shopfoo/importing/model.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Shopfoo model importers
|
||||
"""
|
||||
|
||||
from rattail_demo.db import model
|
||||
from rattail.importing.exporters import ToCSV
|
||||
from rattail.shopfoo.importing.model import ProductImporterMixin
|
||||
|
||||
|
||||
class ToShopfoo(ToCSV):
|
||||
pass
|
||||
|
||||
|
||||
class ProductImporter(ProductImporterMixin, ToShopfoo):
|
||||
"""
|
||||
Shopfoo product data importer
|
||||
"""
|
||||
key = 'uuid'
|
||||
simple_fields = [
|
||||
'uuid',
|
||||
'product_uuid',
|
||||
'upc',
|
||||
'description',
|
||||
'price',
|
||||
'enabled',
|
||||
]
|
||||
export_model_class = model.ShopfooProductExport
|
63
rattail_demo/shopfoo/importing/rattail.py
Normal file
63
rattail_demo/shopfoo/importing/rattail.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail -> Shopfoo importing
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail import importing
|
||||
|
||||
from rattail_demo.db import model
|
||||
from rattail_demo.shopfoo import importing as shopfoo_importing
|
||||
from rattail.shopfoo.importing.rattail import ProductImporterMixin
|
||||
|
||||
|
||||
class FromRattailToShopfoo(importing.FromRattailHandler):
|
||||
"""
|
||||
Rattail -> Shopfoo import handler
|
||||
"""
|
||||
host_title = "Rattail"
|
||||
local_title = "Shopfoo"
|
||||
direction = 'export'
|
||||
|
||||
def get_importers(self):
|
||||
importers = OrderedDict()
|
||||
importers['Product'] = ProductImporter
|
||||
return importers
|
||||
|
||||
|
||||
class FromRattail(importing.FromSQLAlchemy):
|
||||
"""
|
||||
Base class for Shopfoo -> Rattail importers
|
||||
"""
|
||||
|
||||
|
||||
class ProductImporter(ProductImporterMixin, FromRattail, shopfoo_importing.model.ProductImporter):
|
||||
"""
|
||||
Product data importer
|
||||
"""
|
||||
host_model_class = model.ShopfooProduct
|
||||
supported_fields = [
|
||||
'uuid',
|
||||
'product_uuid',
|
||||
'upc',
|
||||
'description',
|
||||
'price',
|
||||
'enabled',
|
||||
]
|
||||
|
||||
def query(self):
|
||||
return self.host_session.query(model.ShopfooProduct)\
|
||||
.order_by(model.ShopfooProduct.upc)
|
||||
|
||||
def normalize_host_object(self, product):
|
||||
|
||||
# copy all values "as-is" from our cache record
|
||||
data = dict([(field, getattr(product, field))
|
||||
for field in self.fields])
|
||||
|
||||
# TODO: is it ever a good idea to set this flag? doing so will mean
|
||||
# the record is *not* included in CSV output file
|
||||
# data['_deleted_'] = product.deleted_from_shopfoo
|
||||
|
||||
return data
|
|
@ -1,35 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Pyramid web application
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from tailbone import app
|
||||
from tailbone_corepos.db import CoreOfficeSession, CoreTransSession
|
||||
|
||||
|
||||
def main(global_config, **settings):
|
||||
"""
|
||||
This function returns a Pyramid WSGI application.
|
||||
"""
|
||||
# set some defaults for PostgreSQL
|
||||
app.provide_postgresql_settings(settings)
|
||||
|
||||
# prefer demo templates over tailbone; use 'better' theme
|
||||
# prefer demo templates over tailbone
|
||||
settings.setdefault('mako.directories', ['rattail_demo.web:templates',
|
||||
'tailbone:templates/themes/better',
|
||||
'tailbone_corepos:templates',
|
||||
'tailbone_woocommerce:templates',
|
||||
'tailbone:templates',])
|
||||
|
||||
# for graceful handling of postgres restart
|
||||
settings.setdefault('retry.attempts', 2)
|
||||
|
||||
# make config objects
|
||||
rattail_config = app.make_rattail_config(settings)
|
||||
pyramid_config = app.make_pyramid_config(settings)
|
||||
|
||||
# bring in rest of rattail-demo etc.
|
||||
pyramid_config.include('tailbone.static')
|
||||
pyramid_config.include('tailbone.subscribers')
|
||||
# configure database sessions
|
||||
CoreOfficeSession.configure(bind=rattail_config.corepos_engine)
|
||||
CoreTransSession.configure(bind=rattail_config.coretrans_engine)
|
||||
|
||||
# bring in rest of rattail-demo
|
||||
pyramid_config.include('rattail_demo.web.static')
|
||||
pyramid_config.include('rattail_demo.web.subscribers')
|
||||
pyramid_config.include('rattail_demo.web.views')
|
||||
|
||||
# configure PostgreSQL some more
|
||||
app.configure_postgresql(pyramid_config)
|
||||
# for graceful handling of postgres restart
|
||||
pyramid_config.add_tween('tailbone.tweens.sqlerror_tween_factory',
|
||||
under='pyramid_tm.tm_tween_factory')
|
||||
|
||||
return pyramid_config.make_wsgi_app()
|
||||
|
||||
|
||||
def asgi_main():
|
||||
"""
|
||||
This function returns an ASGI application.
|
||||
"""
|
||||
from tailbone.asgi import make_asgi_app
|
||||
|
||||
return make_asgi_app(main)
|
||||
|
|
97
rattail_demo/web/menus.py
Normal file
97
rattail_demo/web/menus.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Web Menus
|
||||
"""
|
||||
|
||||
from tailbone import menus as base
|
||||
from tailbone_corepos.menus import make_corepos_menu
|
||||
|
||||
|
||||
class DemoMenuHandler(base.TailboneMenuHandler):
|
||||
"""
|
||||
Demo menu handler
|
||||
"""
|
||||
|
||||
def make_menus(self, request, **kwargs):
|
||||
|
||||
people_menu = self.make_people_menu(request)
|
||||
|
||||
products_menu = self.make_products_menu(request)
|
||||
|
||||
vendors_menu = self.make_vendors_menu(request)
|
||||
|
||||
corepos_menu = make_corepos_menu(request)
|
||||
|
||||
shopfoo_menu = {
|
||||
'title': "Shopfoo",
|
||||
'type': 'menu',
|
||||
'items': [
|
||||
{
|
||||
'title': "Products",
|
||||
'route': 'shopfoo.products',
|
||||
'perm': 'shopfoo.products.list',
|
||||
},
|
||||
{
|
||||
'title': "Product Exports",
|
||||
'route': 'shopfoo.product_exports',
|
||||
'perm': 'shopfoo.product_exports.list',
|
||||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "WooCommerce Products",
|
||||
'route': 'woocommerce.products',
|
||||
'perm': 'woocommerce.products.list',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
reports_menu = self.make_reports_menu(request, include_trainwreck=True)
|
||||
|
||||
batch_menu = self.make_batches_menu(request)
|
||||
|
||||
tempmon_menu = self.make_tempmon_menu(request)
|
||||
|
||||
other_menu = {
|
||||
'title': "Other",
|
||||
'type': 'menu',
|
||||
'items': [
|
||||
{
|
||||
'title': "Documentation",
|
||||
'url': 'https://rattailproject.org/moin/RattailDemo',
|
||||
'target': '_blank',
|
||||
},
|
||||
{
|
||||
'title': "Source Code",
|
||||
'url': 'https://forgejo.wuttaproject.org/rattail/rattail-demo',
|
||||
'target': '_blank',
|
||||
},
|
||||
{
|
||||
'title': "RattailProject.org",
|
||||
'url': 'https://rattailproject.org',
|
||||
'target': '_blank',
|
||||
},
|
||||
{'type': 'sep'},
|
||||
{
|
||||
'title': "Generate New Project",
|
||||
'route': 'generated_projects.create',
|
||||
'perm': 'generated_projects.create',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
admin_menu = self.make_admin_menu(request, include_stores=True)
|
||||
|
||||
menus = [
|
||||
people_menu,
|
||||
products_menu,
|
||||
vendors_menu,
|
||||
corepos_menu,
|
||||
shopfoo_menu,
|
||||
reports_menu,
|
||||
batch_menu,
|
||||
tempmon_menu,
|
||||
other_menu,
|
||||
admin_menu,
|
||||
]
|
||||
|
||||
return menus
|
22
rattail_demo/web/static/__init__.py
Normal file
22
rattail_demo/web/static/__init__.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Static assets
|
||||
"""
|
||||
|
||||
from fanstatic import Library, Resource
|
||||
|
||||
|
||||
# libcache
|
||||
libcache = Library('rattail_demo_libcache', 'libcache')
|
||||
bb_vue_js = Resource(libcache, 'vue.esm-browser-3.4.31.prod.js')
|
||||
bb_oruga_js = Resource(libcache, 'oruga-0.8.12.js')
|
||||
bb_oruga_bulma_js = Resource(libcache, 'oruga-bulma-0.3.0.js')
|
||||
bb_oruga_bulma_css = Resource(libcache, 'oruga-bulma-0.3.0.css')
|
||||
bb_fontawesome_svg_core_js = Resource(libcache, 'fontawesome-svg-core-6.5.2.js')
|
||||
bb_free_solid_svg_icons_js = Resource(libcache, 'free-solid-svg-icons-6.5.2.js')
|
||||
bb_vue_fontawesome_js = Resource(libcache, 'vue-fontawesome-3.0.6.index.es.js')
|
||||
|
||||
|
||||
def includeme(config):
|
||||
config.include('tailbone.static')
|
||||
config.add_static_view('rattail_demo', 'rattail_demo.web:static', cache_max_age=3600)
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
14015
rattail_demo/web/static/libcache/oruga-0.8.12.js
Normal file
14015
rattail_demo/web/static/libcache/oruga-0.8.12.js
Normal file
File diff suppressed because it is too large
Load diff
14859
rattail_demo/web/static/libcache/oruga-bulma-0.3.0.css
Normal file
14859
rattail_demo/web/static/libcache/oruga-bulma-0.3.0.css
Normal file
File diff suppressed because it is too large
Load diff
478
rattail_demo/web/static/libcache/oruga-bulma-0.3.0.js
Normal file
478
rattail_demo/web/static/libcache/oruga-bulma-0.3.0.js
Normal file
|
@ -0,0 +1,478 @@
|
|||
const bulmaConfig = {
|
||||
field: {
|
||||
override: true,
|
||||
rootClass: "field",
|
||||
labelClass: "label",
|
||||
labelSizeClass: "is-",
|
||||
messageClass: "help",
|
||||
variantMessageClass: "is-",
|
||||
addonsClass: "has-addons",
|
||||
groupedClass: "is-grouped",
|
||||
groupMultilineClass: "is-grouped-multiline",
|
||||
horizontalClass: "is-horizontal",
|
||||
labelHorizontalClass: "field-label",
|
||||
bodyHorizontalClass: "field-body",
|
||||
bodyClass: "control",
|
||||
},
|
||||
input: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["control"];
|
||||
if (props.icon)
|
||||
classes.push("has-icons-left");
|
||||
return classes.join(" ").trim();
|
||||
},
|
||||
inputClass: "input",
|
||||
textareaClass: "textarea",
|
||||
roundedClass: "is-rounded",
|
||||
variantClass: "is-",
|
||||
sizeClass: "is-",
|
||||
expandedClass: "is-expanded",
|
||||
iconLeftClass: "is-left",
|
||||
iconRightClass: "is-right",
|
||||
counterClass: "help counter",
|
||||
hasIconRightClass: "has-icons-right",
|
||||
},
|
||||
select: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["control", "select"];
|
||||
if (props.size)
|
||||
classes.push(`is-${props.size}`);
|
||||
if (props.rounded)
|
||||
classes.push("is-rounded");
|
||||
if (props.multiple)
|
||||
classes.push("is-multiple");
|
||||
if (props.icon)
|
||||
classes.push("has-icons-left");
|
||||
if (props.iconRight)
|
||||
classes.push("has-icons-right");
|
||||
return classes.join(" ").trim();
|
||||
},
|
||||
expandedClass: "is-fullwidth",
|
||||
iconLeftClass: "is-left",
|
||||
iconRightClass: "is-right",
|
||||
placeholderClass: "is-empty",
|
||||
rootVariantClass: "is-",
|
||||
},
|
||||
icon: {
|
||||
override: true,
|
||||
rootClass: "icon",
|
||||
variantClass: "has-text-",
|
||||
sizeClass: "is-",
|
||||
clickableClass: "is-clickable",
|
||||
spinClass: "is-spin",
|
||||
},
|
||||
checkbox: {
|
||||
override: true,
|
||||
rootClass: "b-checkbox checkbox",
|
||||
disabledClass: "is-disabled",
|
||||
inputClass: "check",
|
||||
labelClass: "control-label",
|
||||
variantClass: "is-",
|
||||
sizeClass: "is-",
|
||||
},
|
||||
radio: {
|
||||
override: true,
|
||||
rootClass: "b-radio radio",
|
||||
disabledClass: "is-disabled",
|
||||
inputClass: "check",
|
||||
labelClass: "control-label",
|
||||
variantClass: "is-",
|
||||
sizeClass: "is-",
|
||||
},
|
||||
switch: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["switch"];
|
||||
if (props.rounded)
|
||||
classes.push("is-rounded");
|
||||
if (props.position === "left")
|
||||
classes.push("has-left-label");
|
||||
return classes.join(" ");
|
||||
},
|
||||
switchClass: (_, { props }) => {
|
||||
const classes = ["check"];
|
||||
if (props.variant)
|
||||
classes.push(`is-${props.variant}`);
|
||||
if (props.passiveVariant)
|
||||
classes.push(`is-${props.passiveVariant}-passive`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
labelClass: "control-label",
|
||||
sizeClass: "is-",
|
||||
disabledClass: "is-disabled",
|
||||
},
|
||||
autocomplete: {
|
||||
override: true,
|
||||
rootClass: "autocomplete control",
|
||||
itemClass: "dropdown-item",
|
||||
itemHoverClass: "is-hovered",
|
||||
itemEmptyClass: "is-disabled",
|
||||
itemGroupTitleClass: "has-text-weight-bold",
|
||||
},
|
||||
taginput: {
|
||||
override: true,
|
||||
rootClass: "taginput control",
|
||||
containerClass: "taginput-container is-focusable",
|
||||
itemClass: "tag",
|
||||
closeClass: "delete is-small",
|
||||
},
|
||||
pagination: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["pagination"];
|
||||
if (props.rounded)
|
||||
classes.push("is-rounded");
|
||||
return classes.join(" ");
|
||||
},
|
||||
sizeClass: "is-",
|
||||
simpleClass: "is-simple",
|
||||
orderClass: "is-",
|
||||
listClass: "pagination-list",
|
||||
linkClass: "pagination-link",
|
||||
linkCurrentClass: "is-current",
|
||||
linkDisabledClass: "is-disabled",
|
||||
nextButtonClass: "pagination-next",
|
||||
prevButtonClass: "pagination-previous",
|
||||
infoClass: "info",
|
||||
},
|
||||
slider: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["b-slider"];
|
||||
if (props.variant)
|
||||
classes.push(`is-${props.variant}`);
|
||||
if (props.rounded)
|
||||
classes.push("is-rounded");
|
||||
return classes.join(" ");
|
||||
},
|
||||
disabledClass: "is-disabled",
|
||||
trackClass: "b-slider-track",
|
||||
fillClass: "b-slider-fill",
|
||||
thumbWrapperClass: "b-slider-thumb-wrapper",
|
||||
thumbWrapperDraggingClass: "is-dragging",
|
||||
sizeClass: "is-",
|
||||
thumbClass: "b-slider-thumb",
|
||||
tickLabelClass: "b-slider-tick-label",
|
||||
tickHiddenClass: "is-tick-hidden",
|
||||
tickClass: "b-slider-tick",
|
||||
},
|
||||
tabs: {
|
||||
override: true,
|
||||
itemTag: "a",
|
||||
rootClass: "b-tabs",
|
||||
contentClass: "tab-content",
|
||||
multilineClass: "is-multiline",
|
||||
navTabsClass: (_, { props }) => {
|
||||
const classes = ["tabs"];
|
||||
if (props.type)
|
||||
classes.push(`is-${props.type}`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
expandedClass: "is-fullwidth",
|
||||
verticalClass: "is-vertical",
|
||||
positionClass: "is-",
|
||||
navSizeClass: "is-",
|
||||
navPositionClass: "is-",
|
||||
transitioningClass: "is-transitioning",
|
||||
itemClass: "tab-item",
|
||||
itemHeaderActiveClass: () => "is-active",
|
||||
itemHeaderDisabledClass: () => "is-disabled",
|
||||
},
|
||||
table: {
|
||||
override: true,
|
||||
rootClass: "b-table",
|
||||
wrapperClass: "table-wrapper",
|
||||
tableClass: "table",
|
||||
borderedClass: "is-bordered",
|
||||
stripedClass: "is-striped",
|
||||
narrowedClass: "is-narrow",
|
||||
hoverableClass: "is-hoverable",
|
||||
emptyClass: "is-empty",
|
||||
detailedClass: "detail",
|
||||
footerClass: "table-footer",
|
||||
paginationWrapperClass: "level",
|
||||
scrollableClass: "table-container",
|
||||
stickyHeaderClass: "has-sticky-header",
|
||||
trSelectedClass: "is-selected",
|
||||
thSortableClass: "is-sortable",
|
||||
thCurrentSortClass: "is-current-sort",
|
||||
thSortIconClass: "th-wrap sort-icon",
|
||||
thUnselectableClass: "is-unselectable",
|
||||
thStickyClass: "is-sticky",
|
||||
thCheckboxClass: "th-checkbox",
|
||||
thDetailedClass: "th-chevron-cell",
|
||||
tdDetailedChevronClass: "chevron-cell",
|
||||
thPositionClass: (position) => {
|
||||
if (position === "centered")
|
||||
return "is-centered";
|
||||
else if (position === "right")
|
||||
return "is-right";
|
||||
return;
|
||||
},
|
||||
tdPositionClass: (position) => {
|
||||
if (position === "centered")
|
||||
return "has-text-centered";
|
||||
else if (position === "right")
|
||||
return "has-text-right";
|
||||
return;
|
||||
},
|
||||
mobileClass: "is-mobile",
|
||||
mobileSortClass: "table-mobile-sort field",
|
||||
},
|
||||
tooltip: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["b-tooltip"];
|
||||
if (props.variant)
|
||||
classes.push(`is-${props.variant}`);
|
||||
else
|
||||
classes.push(`is-primary`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
contentClass: "b-tooltip-content",
|
||||
triggerClass: "b-tooltip-trigger",
|
||||
alwaysClass: "is-always",
|
||||
multilineClass: "is-multiline",
|
||||
variantClass: "is-",
|
||||
positionClass: "is-",
|
||||
},
|
||||
steps: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["b-steps"];
|
||||
if (props.variant)
|
||||
classes.push(`is-${props.variant}`);
|
||||
if (props.disables)
|
||||
classes.push("is-disabled");
|
||||
return classes.join(" ");
|
||||
},
|
||||
stepsClass: (_, { props }) => {
|
||||
const classes = ["steps"];
|
||||
if (props.animated)
|
||||
classes.push("is-animated");
|
||||
if (props.rounded)
|
||||
classes.push("is-rounded");
|
||||
if (props.labelPosition === "left")
|
||||
classes.push("has-label-left");
|
||||
if (props.labelPosition === "right")
|
||||
classes.push("has-label-right");
|
||||
return classes.join(" ");
|
||||
},
|
||||
itemClass: "step-link",
|
||||
itemHeaderClass: "step-item",
|
||||
itemHeaderVariantClass: "is-",
|
||||
itemHeaderActiveClass: "is-active",
|
||||
itemHeaderPreviousClass: "is-previous",
|
||||
stepLinkClass: "step-link",
|
||||
stepLinkLabelClass: "step-title",
|
||||
stepLinkClickableClass: "is-clickable",
|
||||
stepMarkerClass: "step-marker",
|
||||
stepNavigationClass: "step-navigation",
|
||||
stepContentClass: "step-content",
|
||||
verticalClass: "is-vertical",
|
||||
positionClass: "is-",
|
||||
stepContentTransitioningClass: "is-transitioning",
|
||||
sizeClass: "is-",
|
||||
},
|
||||
button: {
|
||||
override: true,
|
||||
rootClass: "button",
|
||||
sizeClass: "is-",
|
||||
variantClass: "is-",
|
||||
roundedClass: "is-rounded",
|
||||
expandedClass: "is-fullwidth",
|
||||
loadingClass: "is-loading",
|
||||
outlinedClass: () => "is-outlined",
|
||||
invertedClass: () => "is-inverted",
|
||||
wrapperClass: "button-wrapper",
|
||||
},
|
||||
menu: {
|
||||
override: true,
|
||||
rootClass: "menu",
|
||||
listClass: "menu-list",
|
||||
listLabelClass: "menu-label",
|
||||
},
|
||||
skeleton: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["b-skeleton"];
|
||||
if (props.animated)
|
||||
classes.push("is-animated");
|
||||
return classes.join(" ");
|
||||
},
|
||||
itemClass: "b-skeleton-item",
|
||||
itemRoundedClass: "is-rounded",
|
||||
},
|
||||
notification: {
|
||||
override: true,
|
||||
rootClass: (_, { props }) => {
|
||||
const classes = ["notification"];
|
||||
if (props.variant)
|
||||
classes.push(`is-${props.variant}`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
wrapperClass: "media",
|
||||
contentClass: "media-content",
|
||||
iconClass: "media-left",
|
||||
closeClass: "delete",
|
||||
positionClass: "is-",
|
||||
noticeClass: "b-notices",
|
||||
noticePositionClass: "is-",
|
||||
variantClass: "is-",
|
||||
},
|
||||
dropdown: {
|
||||
override: true,
|
||||
itemTag: "a",
|
||||
rootClass: ["dropdown", "dropdown-menu-animation"],
|
||||
triggerClass: "dropdown-trigger",
|
||||
menuClass: "dropdown-content dropdown-menu",
|
||||
disabledClass: "is-disabled",
|
||||
expandedClass: "is-expanded",
|
||||
inlineClass: "is-inline",
|
||||
itemClass: "dropdown-item",
|
||||
itemActiveClass: "is-active",
|
||||
itemDisabledClass: "is-disabled",
|
||||
mobileClass: "is-mobile-modal",
|
||||
menuMobileOverlayClass: "background",
|
||||
positionClass: "is-",
|
||||
activeClass: "is-active",
|
||||
hoverableClass: "is-hoverable",
|
||||
position: "bottom-right",
|
||||
},
|
||||
datepicker: {
|
||||
override: true,
|
||||
rootClass: "datepicker",
|
||||
headerClass: "datepicker-header",
|
||||
footerClass: "datepicker-footer",
|
||||
boxClass: "dropdown-item",
|
||||
tableClass: "datepicker-table",
|
||||
tableHeadClass: "datepicker-header",
|
||||
tableHeadCellClass: "datepicker-cell",
|
||||
headerButtonsClass: "pagination field is-centered",
|
||||
prevButtonClass: "pagination-previous",
|
||||
nextButtonClass: "pagination-next",
|
||||
listsClass: "pagination-list",
|
||||
tableBodyClass: (_, { props }) => {
|
||||
const classes = ["datepicker-body"];
|
||||
if (props.events)
|
||||
classes.push(`has-events`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
tableRowClass: "datepicker-row",
|
||||
tableCellClass: "datepicker-cell",
|
||||
tableCellSelectableClass: "is-selectable",
|
||||
tableCellUnselectableClass: "is-unselectable",
|
||||
tableCellTodayClass: "is-today",
|
||||
tableCellSelectedClass: "is-selected",
|
||||
tableCellWithinHoveredClass: "is-within-hovered",
|
||||
tableCellFirstHoveredClass: "is-first-hovered",
|
||||
tableCellLastHoveredClass: "is-last-hovered",
|
||||
tableCellFirstSelectedClass: "is-first-selected",
|
||||
tableCellLastSelectedClass: "is-last-selected",
|
||||
tableCellWithinSelectedClass: "is-within-selected",
|
||||
tableCellInvisibleClass: "",
|
||||
tableCellNearbyClass: "is-nearby",
|
||||
tableCellEventsClass: (_, { props }) => {
|
||||
const classes = ["has-event"];
|
||||
if (props.indicators)
|
||||
classes.push(`${props.indicators}`);
|
||||
return classes.join(" ");
|
||||
},
|
||||
tableEventVariantClass: "is-",
|
||||
tableEventsClass: "events",
|
||||
tableEventClass: "event",
|
||||
monthBodyClass: "datepicker-body",
|
||||
monthCellClass: "datepicker-cell",
|
||||
monthCellFirstHoveredClass: "is-first-hovered",
|
||||
monthCellFirstSelectedClass: "is-first-selected",
|
||||
monthCellLastHoveredClass: "is-last-hovered",
|
||||
monthCellLastSelectedClass: "is-last-selected",
|
||||
monthCellSelectableClass: "is-selectable",
|
||||
monthCellSelectedClass: "is-selected",
|
||||
monthCellTodayClass: "is-today",
|
||||
monthCellUnselectableClass: "is-unselectable",
|
||||
monthCellWithinHoveredClass: "is-within-hovered",
|
||||
monthCellWithinSelectedClass: "is-within-selected",
|
||||
monthClass: "datepicker-table",
|
||||
monthTableClass: "datepicker-months",
|
||||
},
|
||||
modal: {
|
||||
override: true,
|
||||
rootClass: "modal",
|
||||
activeClass: "is-active",
|
||||
overlayClass: "modal-background",
|
||||
contentClass: "modal-content animation-content",
|
||||
closeClass: "modal-close is-large",
|
||||
fullScreenClass: "is-full-screen",
|
||||
scrollClipClass: "is-clipped",
|
||||
},
|
||||
sidebar: {
|
||||
override: true,
|
||||
rootClass: "b-sidebar",
|
||||
variantClass: "is-",
|
||||
positionClass: "is-",
|
||||
activeClass: "is-active",
|
||||
contentClass: "sidebar-content is-fixed",
|
||||
expandOnHoverClass: "is-mini-expand",
|
||||
fullheightClass: "is-fullheight",
|
||||
fullwidthClass: "is-fullwidth",
|
||||
mobileClass: (_, { props }) => {
|
||||
if (props.mobile && props.mobile !== "reduce") {
|
||||
return `is-${props.mobile}-mobile`;
|
||||
}
|
||||
},
|
||||
overlayClass: "sidebar-background",
|
||||
reduceClass: "is-mini-mobile",
|
||||
},
|
||||
loading: {
|
||||
fullPageClass: "is-full-page",
|
||||
overlayClass: "loading-overlay",
|
||||
iconClass: "icon",
|
||||
rootClass: "loading",
|
||||
},
|
||||
timepicker: {
|
||||
override: true,
|
||||
rootClass: "timepicker control",
|
||||
boxClass: "dropdown-item",
|
||||
selectClasses: {
|
||||
rootClass: "select control",
|
||||
},
|
||||
separatorClass: "is-colon control",
|
||||
footerClass: "timepicker-footer",
|
||||
sizeClass: "is-",
|
||||
},
|
||||
carousel: {
|
||||
override: true,
|
||||
rootClass: "carousel",
|
||||
overlayClass: "is-overlay",
|
||||
wrapperClass: "carousel-scene",
|
||||
itemsClass: "carousel-items",
|
||||
itemsDraggingClass: "is-dragging",
|
||||
arrowIconClass: "carousel-arrow",
|
||||
arrowIconPrevClass: "has-icons-left",
|
||||
arrowIconNextClass: "has-icons-right",
|
||||
indicatorsClass: "carousel-indicator",
|
||||
indicatorClass: "indicator-item",
|
||||
indicatorsInsideClass: "is-inside",
|
||||
indicatorsInsidePositionClass: "is-",
|
||||
indicatorItemClass: "indicator-style",
|
||||
indicatorItemActiveClass: "is-active",
|
||||
indicatorItemStyleClass: "is-",
|
||||
// CarouselItem
|
||||
itemClass: "carousel-item",
|
||||
itemActiveClass: "is-active",
|
||||
},
|
||||
upload: {
|
||||
override: true,
|
||||
rootClass: "upload control",
|
||||
draggableClass: "upload-draggable",
|
||||
variantClass: "is-",
|
||||
expandedClass: "is-expanded",
|
||||
disabledClass: "is-disabled",
|
||||
hoveredClass: "is-hovered",
|
||||
},
|
||||
};
|
||||
|
||||
export { bulmaConfig };
|
|
@ -0,0 +1,626 @@
|
|||
import { parse, icon, config, text } from '@fortawesome/fontawesome-svg-core';
|
||||
import { h, defineComponent, computed, watch } from 'vue';
|
||||
|
||||
function ownKeys(object, enumerableOnly) {
|
||||
var keys = Object.keys(object);
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var symbols = Object.getOwnPropertySymbols(object);
|
||||
enumerableOnly && (symbols = symbols.filter(function (sym) {
|
||||
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
||||
})), keys.push.apply(keys, symbols);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
function _objectSpread2(target) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var source = null != arguments[i] ? arguments[i] : {};
|
||||
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
|
||||
_defineProperty(target, key, source[key]);
|
||||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
|
||||
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
||||
});
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function _typeof(obj) {
|
||||
"@babel/helpers - typeof";
|
||||
|
||||
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
|
||||
return typeof obj;
|
||||
} : function (obj) {
|
||||
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||
}, _typeof(obj);
|
||||
}
|
||||
function _defineProperty(obj, key, value) {
|
||||
key = _toPropertyKey(key);
|
||||
if (key in obj) {
|
||||
Object.defineProperty(obj, key, {
|
||||
value: value,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function _objectWithoutPropertiesLoose(source, excluded) {
|
||||
if (source == null) return {};
|
||||
var target = {};
|
||||
var sourceKeys = Object.keys(source);
|
||||
var key, i;
|
||||
for (i = 0; i < sourceKeys.length; i++) {
|
||||
key = sourceKeys[i];
|
||||
if (excluded.indexOf(key) >= 0) continue;
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function _objectWithoutProperties(source, excluded) {
|
||||
if (source == null) return {};
|
||||
var target = _objectWithoutPropertiesLoose(source, excluded);
|
||||
var key, i;
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
|
||||
for (i = 0; i < sourceSymbolKeys.length; i++) {
|
||||
key = sourceSymbolKeys[i];
|
||||
if (excluded.indexOf(key) >= 0) continue;
|
||||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
|
||||
target[key] = source[key];
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
function _toConsumableArray(arr) {
|
||||
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
|
||||
}
|
||||
function _arrayWithoutHoles(arr) {
|
||||
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
|
||||
}
|
||||
function _iterableToArray(iter) {
|
||||
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
||||
}
|
||||
function _unsupportedIterableToArray(o, minLen) {
|
||||
if (!o) return;
|
||||
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
|
||||
var n = Object.prototype.toString.call(o).slice(8, -1);
|
||||
if (n === "Object" && o.constructor) n = o.constructor.name;
|
||||
if (n === "Map" || n === "Set") return Array.from(o);
|
||||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
|
||||
}
|
||||
function _arrayLikeToArray(arr, len) {
|
||||
if (len == null || len > arr.length) len = arr.length;
|
||||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
||||
return arr2;
|
||||
}
|
||||
function _nonIterableSpread() {
|
||||
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
||||
}
|
||||
function _toPrimitive(input, hint) {
|
||||
if (typeof input !== "object" || input === null) return input;
|
||||
var prim = input[Symbol.toPrimitive];
|
||||
if (prim !== undefined) {
|
||||
var res = prim.call(input, hint || "default");
|
||||
if (typeof res !== "object") return res;
|
||||
throw new TypeError("@@toPrimitive must return a primitive value.");
|
||||
}
|
||||
return (hint === "string" ? String : Number)(input);
|
||||
}
|
||||
function _toPropertyKey(arg) {
|
||||
var key = _toPrimitive(arg, "string");
|
||||
return typeof key === "symbol" ? key : String(key);
|
||||
}
|
||||
|
||||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||
|
||||
var humps$1 = {exports: {}};
|
||||
|
||||
(function (module) {
|
||||
(function(global) {
|
||||
|
||||
var _processKeys = function(convert, obj, options) {
|
||||
if(!_isObject(obj) || _isDate(obj) || _isRegExp(obj) || _isBoolean(obj) || _isFunction(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
var output,
|
||||
i = 0,
|
||||
l = 0;
|
||||
|
||||
if(_isArray(obj)) {
|
||||
output = [];
|
||||
for(l=obj.length; i<l; i++) {
|
||||
output.push(_processKeys(convert, obj[i], options));
|
||||
}
|
||||
}
|
||||
else {
|
||||
output = {};
|
||||
for(var key in obj) {
|
||||
if(Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
output[convert(key, options)] = _processKeys(convert, obj[key], options);
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
// String conversion methods
|
||||
|
||||
var separateWords = function(string, options) {
|
||||
options = options || {};
|
||||
var separator = options.separator || '_';
|
||||
var split = options.split || /(?=[A-Z])/;
|
||||
|
||||
return string.split(split).join(separator);
|
||||
};
|
||||
|
||||
var camelize = function(string) {
|
||||
if (_isNumerical(string)) {
|
||||
return string;
|
||||
}
|
||||
string = string.replace(/[\-_\s]+(.)?/g, function(match, chr) {
|
||||
return chr ? chr.toUpperCase() : '';
|
||||
});
|
||||
// Ensure 1st char is always lowercase
|
||||
return string.substr(0, 1).toLowerCase() + string.substr(1);
|
||||
};
|
||||
|
||||
var pascalize = function(string) {
|
||||
var camelized = camelize(string);
|
||||
// Ensure 1st char is always uppercase
|
||||
return camelized.substr(0, 1).toUpperCase() + camelized.substr(1);
|
||||
};
|
||||
|
||||
var decamelize = function(string, options) {
|
||||
return separateWords(string, options).toLowerCase();
|
||||
};
|
||||
|
||||
// Utilities
|
||||
// Taken from Underscore.js
|
||||
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
var _isFunction = function(obj) {
|
||||
return typeof(obj) === 'function';
|
||||
};
|
||||
var _isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
};
|
||||
var _isArray = function(obj) {
|
||||
return toString.call(obj) == '[object Array]';
|
||||
};
|
||||
var _isDate = function(obj) {
|
||||
return toString.call(obj) == '[object Date]';
|
||||
};
|
||||
var _isRegExp = function(obj) {
|
||||
return toString.call(obj) == '[object RegExp]';
|
||||
};
|
||||
var _isBoolean = function(obj) {
|
||||
return toString.call(obj) == '[object Boolean]';
|
||||
};
|
||||
|
||||
// Performant way to determine if obj coerces to a number
|
||||
var _isNumerical = function(obj) {
|
||||
obj = obj - 0;
|
||||
return obj === obj;
|
||||
};
|
||||
|
||||
// Sets up function which handles processing keys
|
||||
// allowing the convert function to be modified by a callback
|
||||
var _processor = function(convert, options) {
|
||||
var callback = options && 'process' in options ? options.process : options;
|
||||
|
||||
if(typeof(callback) !== 'function') {
|
||||
return convert;
|
||||
}
|
||||
|
||||
return function(string, options) {
|
||||
return callback(string, convert, options);
|
||||
}
|
||||
};
|
||||
|
||||
var humps = {
|
||||
camelize: camelize,
|
||||
decamelize: decamelize,
|
||||
pascalize: pascalize,
|
||||
depascalize: decamelize,
|
||||
camelizeKeys: function(object, options) {
|
||||
return _processKeys(_processor(camelize, options), object);
|
||||
},
|
||||
decamelizeKeys: function(object, options) {
|
||||
return _processKeys(_processor(decamelize, options), object, options);
|
||||
},
|
||||
pascalizeKeys: function(object, options) {
|
||||
return _processKeys(_processor(pascalize, options), object);
|
||||
},
|
||||
depascalizeKeys: function () {
|
||||
return this.decamelizeKeys.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
if (module.exports) {
|
||||
module.exports = humps;
|
||||
} else {
|
||||
global.humps = humps;
|
||||
}
|
||||
|
||||
})(commonjsGlobal);
|
||||
} (humps$1));
|
||||
|
||||
var humps = humps$1.exports;
|
||||
|
||||
var _excluded = ["class", "style"];
|
||||
|
||||
/**
|
||||
* Converts a CSS style into a plain Javascript object.
|
||||
* @param {String} style The style to converts into a plain Javascript object.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function styleToObject(style) {
|
||||
return style.split(';').map(function (s) {
|
||||
return s.trim();
|
||||
}).filter(function (s) {
|
||||
return s;
|
||||
}).reduce(function (output, pair) {
|
||||
var idx = pair.indexOf(':');
|
||||
var prop = humps.camelize(pair.slice(0, idx));
|
||||
var value = pair.slice(idx + 1).trim();
|
||||
output[prop] = value;
|
||||
return output;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a CSS class list into a plain Javascript object.
|
||||
* @param {Array<String>} classes The class list to convert.
|
||||
* @returns {Object}
|
||||
*/
|
||||
function classToObject(classes) {
|
||||
return classes.split(/\s+/).reduce(function (output, className) {
|
||||
output[className] = true;
|
||||
return output;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a FontAwesome abstract element of an icon into a Vue VNode.
|
||||
* @param {AbstractElement | String} abstractElement The element to convert.
|
||||
* @param {Object} props The user-defined props.
|
||||
* @param {Object} attrs The user-defined native HTML attributes.
|
||||
* @returns {VNode}
|
||||
*/
|
||||
function convert(abstractElement) {
|
||||
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
||||
var attrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
||||
// If the abstract element is a string, we'll just return a string render function
|
||||
if (typeof abstractElement === 'string') {
|
||||
return abstractElement;
|
||||
}
|
||||
|
||||
// Converting abstract element children into Vue VNodes
|
||||
var children = (abstractElement.children || []).map(function (child) {
|
||||
return convert(child);
|
||||
});
|
||||
|
||||
// Converting abstract element attributes into valid Vue format
|
||||
var mixins = Object.keys(abstractElement.attributes || {}).reduce(function (mixins, key) {
|
||||
var value = abstractElement.attributes[key];
|
||||
switch (key) {
|
||||
case 'class':
|
||||
mixins.class = classToObject(value);
|
||||
break;
|
||||
case 'style':
|
||||
mixins.style = styleToObject(value);
|
||||
break;
|
||||
default:
|
||||
mixins.attrs[key] = value;
|
||||
}
|
||||
return mixins;
|
||||
}, {
|
||||
attrs: {},
|
||||
class: {},
|
||||
style: {}
|
||||
});
|
||||
|
||||
// Now, we'll return the VNode
|
||||
attrs.class;
|
||||
var _attrs$style = attrs.style,
|
||||
aStyle = _attrs$style === void 0 ? {} : _attrs$style,
|
||||
otherAttrs = _objectWithoutProperties(attrs, _excluded);
|
||||
return h(abstractElement.tag, _objectSpread2(_objectSpread2(_objectSpread2({}, props), {}, {
|
||||
class: mixins.class,
|
||||
style: _objectSpread2(_objectSpread2({}, mixins.style), aStyle)
|
||||
}, mixins.attrs), otherAttrs), children);
|
||||
}
|
||||
|
||||
var PRODUCTION = false;
|
||||
try {
|
||||
PRODUCTION = process.env.NODE_ENV === 'production';
|
||||
} catch (e) {}
|
||||
function log () {
|
||||
if (!PRODUCTION && console && typeof console.error === 'function') {
|
||||
var _console;
|
||||
(_console = console).error.apply(_console, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
function objectWithKey(key, value) {
|
||||
return Array.isArray(value) && value.length > 0 || !Array.isArray(value) && value ? _defineProperty({}, key, value) : {};
|
||||
}
|
||||
function classList(props) {
|
||||
var _classes;
|
||||
var classes = (_classes = {
|
||||
'fa-spin': props.spin,
|
||||
'fa-pulse': props.pulse,
|
||||
'fa-fw': props.fixedWidth,
|
||||
'fa-border': props.border,
|
||||
'fa-li': props.listItem,
|
||||
'fa-inverse': props.inverse,
|
||||
'fa-flip': props.flip === true,
|
||||
'fa-flip-horizontal': props.flip === 'horizontal' || props.flip === 'both',
|
||||
'fa-flip-vertical': props.flip === 'vertical' || props.flip === 'both'
|
||||
}, _defineProperty(_classes, "fa-".concat(props.size), props.size !== null), _defineProperty(_classes, "fa-rotate-".concat(props.rotation), props.rotation !== null), _defineProperty(_classes, "fa-pull-".concat(props.pull), props.pull !== null), _defineProperty(_classes, 'fa-swap-opacity', props.swapOpacity), _defineProperty(_classes, 'fa-bounce', props.bounce), _defineProperty(_classes, 'fa-shake', props.shake), _defineProperty(_classes, 'fa-beat', props.beat), _defineProperty(_classes, 'fa-fade', props.fade), _defineProperty(_classes, 'fa-beat-fade', props.beatFade), _defineProperty(_classes, 'fa-flash', props.flash), _defineProperty(_classes, 'fa-spin-pulse', props.spinPulse), _defineProperty(_classes, 'fa-spin-reverse', props.spinReverse), _classes);
|
||||
return Object.keys(classes).map(function (key) {
|
||||
return classes[key] ? key : null;
|
||||
}).filter(function (key) {
|
||||
return key;
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeIconArgs(icon) {
|
||||
if (icon && _typeof(icon) === 'object' && icon.prefix && icon.iconName && icon.icon) {
|
||||
return icon;
|
||||
}
|
||||
if (parse.icon) {
|
||||
return parse.icon(icon);
|
||||
}
|
||||
if (icon === null) {
|
||||
return null;
|
||||
}
|
||||
if (_typeof(icon) === 'object' && icon.prefix && icon.iconName) {
|
||||
return icon;
|
||||
}
|
||||
if (Array.isArray(icon) && icon.length === 2) {
|
||||
return {
|
||||
prefix: icon[0],
|
||||
iconName: icon[1]
|
||||
};
|
||||
}
|
||||
if (typeof icon === 'string') {
|
||||
return {
|
||||
prefix: 'fas',
|
||||
iconName: icon
|
||||
};
|
||||
}
|
||||
}
|
||||
var FontAwesomeIcon = defineComponent({
|
||||
name: 'FontAwesomeIcon',
|
||||
props: {
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fixedWidth: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
flip: {
|
||||
type: [Boolean, String],
|
||||
default: false,
|
||||
validator: function validator(value) {
|
||||
return [true, false, 'horizontal', 'vertical', 'both'].indexOf(value) > -1;
|
||||
}
|
||||
},
|
||||
icon: {
|
||||
type: [Object, Array, String],
|
||||
required: true
|
||||
},
|
||||
mask: {
|
||||
type: [Object, Array, String],
|
||||
default: null
|
||||
},
|
||||
maskId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
listItem: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
pull: {
|
||||
type: String,
|
||||
default: null,
|
||||
validator: function validator(value) {
|
||||
return ['right', 'left'].indexOf(value) > -1;
|
||||
}
|
||||
},
|
||||
pulse: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
rotation: {
|
||||
type: [String, Number],
|
||||
default: null,
|
||||
validator: function validator(value) {
|
||||
return [90, 180, 270].indexOf(Number.parseInt(value, 10)) > -1;
|
||||
}
|
||||
},
|
||||
swapOpacity: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: null,
|
||||
validator: function validator(value) {
|
||||
return ['2xs', 'xs', 'sm', 'lg', 'xl', '2xl', '1x', '2x', '3x', '4x', '5x', '6x', '7x', '8x', '9x', '10x'].indexOf(value) > -1;
|
||||
}
|
||||
},
|
||||
spin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
transform: {
|
||||
type: [String, Object],
|
||||
default: null
|
||||
},
|
||||
symbol: {
|
||||
type: [Boolean, String],
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
titleId: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
inverse: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bounce: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
shake: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
beat: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fade: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
beatFade: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
flash: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
spinPulse: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
spinReverse: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup: function setup(props, _ref) {
|
||||
var attrs = _ref.attrs;
|
||||
var icon$1 = computed(function () {
|
||||
return normalizeIconArgs(props.icon);
|
||||
});
|
||||
var classes = computed(function () {
|
||||
return objectWithKey('classes', classList(props));
|
||||
});
|
||||
var transform = computed(function () {
|
||||
return objectWithKey('transform', typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform);
|
||||
});
|
||||
var mask = computed(function () {
|
||||
return objectWithKey('mask', normalizeIconArgs(props.mask));
|
||||
});
|
||||
var renderedIcon = computed(function () {
|
||||
return icon(icon$1.value, _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({}, classes.value), transform.value), mask.value), {}, {
|
||||
symbol: props.symbol,
|
||||
title: props.title,
|
||||
titleId: props.titleId,
|
||||
maskId: props.maskId
|
||||
}));
|
||||
});
|
||||
watch(renderedIcon, function (value) {
|
||||
if (!value) {
|
||||
return log('Could not find one or more icon(s)', icon$1.value, mask.value);
|
||||
}
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
var vnode = computed(function () {
|
||||
return renderedIcon.value ? convert(renderedIcon.value.abstract[0], {}, attrs) : null;
|
||||
});
|
||||
return function () {
|
||||
return vnode.value;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var FontAwesomeLayers = defineComponent({
|
||||
name: 'FontAwesomeLayers',
|
||||
props: {
|
||||
fixedWidth: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup: function setup(props, _ref) {
|
||||
var slots = _ref.slots;
|
||||
var familyPrefix = config.familyPrefix;
|
||||
var className = computed(function () {
|
||||
return ["".concat(familyPrefix, "-layers")].concat(_toConsumableArray(props.fixedWidth ? ["".concat(familyPrefix, "-fw")] : []));
|
||||
});
|
||||
return function () {
|
||||
return h('div', {
|
||||
class: className.value
|
||||
}, slots.default ? slots.default() : []);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
var FontAwesomeLayersText = defineComponent({
|
||||
name: 'FontAwesomeLayersText',
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
transform: {
|
||||
type: [String, Object],
|
||||
default: null
|
||||
},
|
||||
counter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
position: {
|
||||
type: String,
|
||||
default: null,
|
||||
validator: function validator(value) {
|
||||
return ['bottom-left', 'bottom-right', 'top-left', 'top-right'].indexOf(value) > -1;
|
||||
}
|
||||
}
|
||||
},
|
||||
setup: function setup(props, _ref) {
|
||||
var attrs = _ref.attrs;
|
||||
var familyPrefix = config.familyPrefix;
|
||||
var classes = computed(function () {
|
||||
return objectWithKey('classes', [].concat(_toConsumableArray(props.counter ? ["".concat(familyPrefix, "-layers-counter")] : []), _toConsumableArray(props.position ? ["".concat(familyPrefix, "-layers-").concat(props.position)] : [])));
|
||||
});
|
||||
var transform = computed(function () {
|
||||
return objectWithKey('transform', typeof props.transform === 'string' ? parse.transform(props.transform) : props.transform);
|
||||
});
|
||||
var abstractElement = computed(function () {
|
||||
var _text = text(props.value.toString(), _objectSpread2(_objectSpread2({}, transform.value), classes.value)),
|
||||
abstract = _text.abstract;
|
||||
if (props.counter) {
|
||||
abstract[0].attributes.class = abstract[0].attributes.class.replace('fa-layers-text', '');
|
||||
}
|
||||
return abstract[0];
|
||||
});
|
||||
var vnode = computed(function () {
|
||||
return convert(abstractElement.value, {}, attrs);
|
||||
});
|
||||
return function () {
|
||||
return vnode.value;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText };
|
File diff suppressed because one or more lines are too long
11
rattail_demo/web/subscribers.py
Normal file
11
rattail_demo/web/subscribers.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Pyramid Event Subscribers
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
|
||||
def includeme(config):
|
||||
config.include('tailbone.subscribers')
|
||||
config.add_subscriber('tailbone.subscribers.add_inbox_count', 'pyramid.events.BeforeRender')
|
|
@ -1,14 +0,0 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
<%inherit file="tailbone:templates/themes/better/base.mako" />
|
||||
|
||||
<%def name="global_title()">${"[STAGE] " if not request.rattail_config.production() else ''}Rattail Demo</%def>
|
||||
|
||||
<%def name="favicon()">
|
||||
<link rel="icon" type="image/x-icon" href="${request.static_url('tailbone:static/img/rattail.ico')}" />
|
||||
</%def>
|
||||
|
||||
<%def name="header_logo()">
|
||||
${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", height='49')}
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
6
rattail_demo/web/templates/base_meta.mako
Normal file
6
rattail_demo/web/templates/base_meta.mako
Normal file
|
@ -0,0 +1,6 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="tailbone:templates/base_meta.mako" />
|
||||
|
||||
<%def name="header_logo()">
|
||||
${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", style="height: 55px;")}
|
||||
</%def>
|
|
@ -0,0 +1,11 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/index.mako" />
|
||||
|
||||
<%def name="context_menu_items()">
|
||||
${parent.context_menu_items()}
|
||||
% if request.has_perm('{}.import_file'.format(permission_prefix)):
|
||||
<li>${h.link_to("Import {} from Square CSV".format(model_title_plural), url('{}.import_square'.format(route_prefix)))}</li>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
|
@ -1,7 +0,0 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
<%inherit file="tailbone:templates/home.mako" />
|
||||
|
||||
<div class="logo">
|
||||
${h.image(request.static_url('tailbone:static/img/home_logo.png'), "Rattail Logo")}
|
||||
<h1>Welcome to the Rattail Demo</h1>
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="tailbone:templates/login.mako" />
|
||||
|
||||
<%def name="extra_styles()">
|
||||
|
@ -11,8 +11,12 @@
|
|||
</style>
|
||||
</%def>
|
||||
|
||||
${parent.body()}
|
||||
<%def name="page_content()">
|
||||
${parent.page_content()}
|
||||
<p class="tips">
|
||||
Login with <strong>chuck / admin</strong> for full demo access
|
||||
</p>
|
||||
</%def>
|
||||
|
||||
<p class="tips">
|
||||
Login with <strong>chuck / admin</strong> for full demo access
|
||||
</p>
|
||||
|
||||
${parent.body()}
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
## -*- coding: utf-8 -*-
|
||||
|
||||
<%def name="main_menu_items()">
|
||||
|
||||
% if request.has_any_perm('products.list', 'vendors.list', 'brands.list', 'families.list', 'reportcodes.list'):
|
||||
<li>
|
||||
<a>Products</a>
|
||||
<ul>
|
||||
% if request.has_perm('products.list'):
|
||||
<li>${h.link_to("Products", url('products'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('vendors.list'):
|
||||
<li>${h.link_to("Vendors", url('vendors'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('brands.list'):
|
||||
<li>${h.link_to("Brands", url('brands'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('families.list'):
|
||||
<li>${h.link_to("Families", url('families'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('reportcodes.list'):
|
||||
<li>${h.link_to("Report Codes", url('reportcodes'))}</li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
% if request.has_any_perm('people.list', 'customers.list', 'employees.list'):
|
||||
<li>
|
||||
<a>People</a>
|
||||
<ul>
|
||||
% if request.has_perm('people.list'):
|
||||
<li>${h.link_to("All People", url('people'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('customers.list'):
|
||||
<li>${h.link_to("Customers", url('customers'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('employees.list'):
|
||||
<li>${h.link_to("Employees", url('employees'))}</li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
% if request.has_any_perm('stores.list', 'departments.list', 'subdepartments.list'):
|
||||
<li>
|
||||
<a>Company</a>
|
||||
<ul>
|
||||
% if request.has_perm('stores.list'):
|
||||
<li>${h.link_to("Stores", url('stores'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('departments.list'):
|
||||
<li>${h.link_to("Departments", url('departments'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('subdepartments.list'):
|
||||
<li>${h.link_to("Subdepartments", url('subdepartments'))}</li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
% if request.has_any_perm('batch.handheld.list', 'batch.inventory.list'):
|
||||
<li>
|
||||
<a>Batches</a>
|
||||
<ul>
|
||||
% if request.has_perm('batch.handheld.list'):
|
||||
<li>${h.link_to("Handheld", url('batch.handheld'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('batch.inventory.list'):
|
||||
<li>${h.link_to("Inventory", url('batch.inventory'))}</li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
% if request.has_any_perm('users.list', 'roles.list', 'settings.list'):
|
||||
<li>
|
||||
<a>Admin</a>
|
||||
<ul>
|
||||
% if request.has_perm('users.list'):
|
||||
<li>${h.link_to("Users", url('users'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('roles.list'):
|
||||
<li>${h.link_to("Roles", url('roles'))}</li>
|
||||
% endif
|
||||
% if request.has_perm('settings.list'):
|
||||
<li>${h.link_to("Settings", url('settings'))}</li>
|
||||
% endif
|
||||
</ul>
|
||||
</li>
|
||||
% endif
|
||||
|
||||
% if request.user:
|
||||
<li>
|
||||
<a${' class="root-user"' if request.is_root else ''|n}>${request.user}${" ({})".format(inbox_count) if inbox_count else ''}</a>
|
||||
<ul>
|
||||
% if request.is_root:
|
||||
<li class="root-user">${h.link_to("Stop being root", url('stop_root'))}</li>
|
||||
% elif request.is_admin:
|
||||
<li class="root-user">${h.link_to("Become root", url('become_root'))}</li>
|
||||
% endif
|
||||
<li>${h.link_to("Change Password", url('change_password'))}</li>
|
||||
<li>${h.link_to("Logout", url('logout'))}</li>
|
||||
</ul>
|
||||
</li>
|
||||
% else:
|
||||
<li>${h.link_to("Login", url('login'))}</li>
|
||||
% endif
|
||||
|
||||
</%def>
|
|
@ -1,50 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Web views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from tailbone import views as base
|
||||
|
||||
|
||||
def bogus_error(request):
|
||||
"""
|
||||
A special view which simply raises an error, for the sake of testing
|
||||
uncaught exception handling.
|
||||
"""
|
||||
raise Exception("Congratulations, you have triggered a bogus error.")
|
||||
from tailbone.views import essentials
|
||||
|
||||
|
||||
def includeme(config):
|
||||
|
||||
# TODO: merge these views into core/common
|
||||
config.add_route('home', '/')
|
||||
config.add_view(base.home, route_name='home', renderer='/home.mako')
|
||||
config.add_route('bogus_error', '/bogus-error')
|
||||
config.add_view(bogus_error, route_name='bogus_error',
|
||||
permission='admin')
|
||||
|
||||
# core views
|
||||
config.include('rattail_demo.web.views.common')
|
||||
config.include('rattail_demo.web.views.auth')
|
||||
# tailbone essentials
|
||||
essentials.defaults(config, **{
|
||||
'tailbone.views.upgrades': 'rattail_demo.web.views.upgrades',
|
||||
})
|
||||
|
||||
# main table views
|
||||
config.include('tailbone.views.brands')
|
||||
config.include('tailbone.views.categories')
|
||||
config.include('tailbone.views.customers')
|
||||
config.include('tailbone.views.customergroups')
|
||||
config.include('tailbone.views.departments')
|
||||
config.include('tailbone.views.employees')
|
||||
config.include('tailbone.views.families')
|
||||
config.include('rattail_demo.web.views.people')
|
||||
config.include('tailbone.views.products')
|
||||
config.include('tailbone.views.members')
|
||||
config.include('tailbone.views.messages')
|
||||
config.include('rattail_demo.web.views.products')
|
||||
config.include('tailbone.views.reportcodes')
|
||||
config.include('tailbone.views.roles')
|
||||
config.include('tailbone.views.settings')
|
||||
config.include('tailbone.views.stores')
|
||||
config.include('tailbone.views.subdepartments')
|
||||
config.include('rattail_demo.web.views.users')
|
||||
config.include('tailbone.views.tempmon')
|
||||
config.include('tailbone.views.vendors')
|
||||
config.include('tailbone.views.uoms')
|
||||
|
||||
# purchasing / receiving
|
||||
config.include('tailbone_corepos.views.purchases')
|
||||
config.include('tailbone.views.purchases.credits')
|
||||
config.include('tailbone.views.purchasing')
|
||||
|
||||
# core-pos views
|
||||
config.include('tailbone_corepos.views')
|
||||
config.include('tailbone_corepos.views.corepos')
|
||||
|
||||
# shopfoo views
|
||||
config.include('rattail_demo.web.views.shopfoo')
|
||||
|
||||
# woocommerce views
|
||||
config.include('tailbone_woocommerce.views')
|
||||
config.include('tailbone_woocommerce.views.woocommerce')
|
||||
|
||||
# batch views
|
||||
config.include('tailbone.views.handheld')
|
||||
config.include('tailbone.views.inventory')
|
||||
config.include('tailbone.views.batch.handheld')
|
||||
config.include('tailbone.views.batch.inventory')
|
||||
config.include('tailbone.views.batch.importer')
|
||||
config.include('tailbone.views.batch.vendorcatalog')
|
||||
|
||||
# trainwreck
|
||||
config.include('tailbone.views.trainwreck.defaults')
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Auth views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from pyramid import httpexceptions
|
||||
|
||||
from tailbone.views import auth as base
|
||||
|
||||
|
||||
def change_password(request):
|
||||
# prevent password change for 'chuck'
|
||||
if request.user and request.user.username == 'chuck':
|
||||
request.session.flash("Cannot change password for 'chuck' in Rattail Demo")
|
||||
return httpexceptions.HTTPFound(location=request.get_referrer())
|
||||
return base.change_password(request)
|
||||
|
||||
|
||||
def includeme(config):
|
||||
# TODO: this is way too much duplication, surely..
|
||||
base.add_routes(config)
|
||||
|
||||
config.add_forbidden_view(base.forbidden)
|
||||
|
||||
config.add_view(base.login, route_name='login',
|
||||
renderer='/login.mako')
|
||||
|
||||
config.add_view(base.logout, route_name='logout')
|
||||
|
||||
config.add_view(base.become_root, route_name='become_root')
|
||||
config.add_view(base.stop_root, route_name='stop_root')
|
||||
|
||||
config.add_view(change_password, route_name='change_password',
|
||||
renderer='/change_password.mako')
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Common views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from tailbone.views import common as base
|
||||
|
||||
import rattail_demo
|
||||
|
||||
|
||||
class CommonView(base.CommonView):
|
||||
|
||||
project_title = "Rattail Demo"
|
||||
project_version = rattail_demo.__version__
|
||||
|
||||
|
||||
def includeme(config):
|
||||
CommonView.defaults(config)
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Person views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from tailbone.views import people as base
|
||||
|
||||
|
||||
class PeopleView(base.PeopleView):
|
||||
"""
|
||||
Prevent edit/delete for Chuck Norris
|
||||
"""
|
||||
|
||||
def editable_instance(self, person):
|
||||
return person.uuid != '30d1fe06bcf411e6a7c23ca9f40bc550'
|
||||
|
||||
def deletable_instance(self, person):
|
||||
return person.uuid != '30d1fe06bcf411e6a7c23ca9f40bc550'
|
||||
|
||||
|
||||
def includeme(config):
|
||||
PeopleView.defaults(config)
|
28
rattail_demo/web/views/products.py
Normal file
28
rattail_demo/web/views/products.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Product views
|
||||
"""
|
||||
|
||||
from webhelpers2.html import tags
|
||||
|
||||
from tailbone.views import products as base
|
||||
|
||||
|
||||
class ProductView(base.ProductView):
|
||||
"""
|
||||
Product overrides for online demo
|
||||
"""
|
||||
|
||||
def get_xref_links(self, product):
|
||||
links = super(ProductView, self).get_xref_links(product)
|
||||
|
||||
if product.demo_shopfoo_product:
|
||||
url = self.request.route_url('shopfoo.products.view',
|
||||
uuid=product.demo_shopfoo_product.uuid)
|
||||
links.append(tags.link_to("View Shopfoo Product", url))
|
||||
|
||||
return links
|
||||
|
||||
|
||||
def includeme(config):
|
||||
base.defaults(config, **{'ProductView': ProductView})
|
9
rattail_demo/web/views/shopfoo/__init__.py
Normal file
9
rattail_demo/web/views/shopfoo/__init__.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Shopfoo views
|
||||
"""
|
||||
|
||||
|
||||
def includeme(config):
|
||||
config.include('rattail_demo.web.views.shopfoo.products')
|
||||
config.include('rattail_demo.web.views.shopfoo.exports')
|
42
rattail_demo/web/views/shopfoo/exports.py
Normal file
42
rattail_demo/web/views/shopfoo/exports.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Views for Shopfoo product exports
|
||||
"""
|
||||
|
||||
from rattail_demo.db import model
|
||||
|
||||
from tailbone.views.exports import ExportMasterView
|
||||
|
||||
|
||||
class ShopfooProductExportView(ExportMasterView):
|
||||
"""
|
||||
Master view for Shopfoo product exports.
|
||||
"""
|
||||
model_class = model.ShopfooProductExport
|
||||
route_prefix = 'shopfoo.product_exports'
|
||||
url_prefix = '/shopfoo/exports/product'
|
||||
downloadable = True
|
||||
editable = True
|
||||
delete_export_files = True
|
||||
|
||||
grid_columns = [
|
||||
'id',
|
||||
'created',
|
||||
'created_by',
|
||||
'filename',
|
||||
'record_count',
|
||||
'uploaded',
|
||||
]
|
||||
|
||||
form_fields = [
|
||||
'id',
|
||||
'created',
|
||||
'created_by',
|
||||
'record_count',
|
||||
'filename',
|
||||
'uploaded',
|
||||
]
|
||||
|
||||
|
||||
def includeme(config):
|
||||
ShopfooProductExportView.defaults(config)
|
70
rattail_demo/web/views/shopfoo/products.py
Normal file
70
rattail_demo/web/views/shopfoo/products.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Shopfoo product views
|
||||
"""
|
||||
|
||||
from rattail_demo.db import model
|
||||
|
||||
from tailbone.views import MasterView
|
||||
|
||||
|
||||
class ShopfooProductView(MasterView):
|
||||
"""
|
||||
Shopfoo Product views
|
||||
"""
|
||||
model_class = model.ShopfooProduct
|
||||
url_prefix = '/shopfoo/products'
|
||||
route_prefix = 'shopfoo.products'
|
||||
creatable = False
|
||||
editable = False
|
||||
bulk_deletable = True
|
||||
has_versions = True
|
||||
|
||||
labels = {
|
||||
'upc': "UPC",
|
||||
}
|
||||
|
||||
grid_columns = [
|
||||
'upc',
|
||||
'description',
|
||||
'price',
|
||||
'enabled',
|
||||
]
|
||||
|
||||
form_fields = [
|
||||
'product',
|
||||
'upc',
|
||||
'description',
|
||||
'price',
|
||||
'enabled',
|
||||
]
|
||||
|
||||
def configure_grid(self, g):
|
||||
super(ShopfooProductView, self).configure_grid(g)
|
||||
|
||||
g.filters['upc'].default_active = True
|
||||
g.filters['upc'].default_verb = 'equal'
|
||||
|
||||
g.filters['description'].default_active = True
|
||||
g.filters['description'].default_verb = 'contains'
|
||||
|
||||
g.set_sort_defaults('upc')
|
||||
|
||||
g.set_type('price', 'currency')
|
||||
|
||||
g.set_link('upc')
|
||||
g.set_link('description')
|
||||
|
||||
def grid_extra_class(self, product, i):
|
||||
if not product.enabled:
|
||||
return 'warning'
|
||||
|
||||
def configure_form(self, f):
|
||||
super(ShopfooProductView, self).configure_form(f)
|
||||
|
||||
f.set_renderer('product', self.render_product)
|
||||
f.set_type('price', 'currency')
|
||||
|
||||
|
||||
def includeme(config):
|
||||
ShopfooProductView.defaults(config)
|
27
rattail_demo/web/views/upgrades.py
Normal file
27
rattail_demo/web/views/upgrades.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Upgrade views
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from tailbone.views import upgrades as base
|
||||
|
||||
|
||||
class UpgradeView(base.UpgradeView):
|
||||
|
||||
def get_changelog_projects(self):
|
||||
projects = super(UpgradeView, self).get_changelog_projects()
|
||||
|
||||
projects.update({
|
||||
'rattail_demo': {
|
||||
'commit_url': 'https://forgejo.wuttaproject.org/rattail/rattail-demo/compare/{{old_version}}...{{new_version}}',
|
||||
'release_url': 'https://forgejo.wuttaproject.org/rattail/rattail-demo/src/tag/v{{new_version}}/CHANGELOG.md',
|
||||
},
|
||||
})
|
||||
|
||||
return projects
|
||||
|
||||
|
||||
def includeme(config):
|
||||
base.defaults(config, **{'UpgradeView': UpgradeView})
|
|
@ -1,24 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
User views
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from tailbone.views import users as base
|
||||
|
||||
|
||||
class UsersView(base.UsersView):
|
||||
"""
|
||||
Prevent edit/delete for 'chuck'
|
||||
"""
|
||||
|
||||
def editable_instance(self, user):
|
||||
return user.uuid != '28eeee92bcf411e6a7c23ca9f40bc550'
|
||||
|
||||
def deletable_instance(self, user):
|
||||
return user.uuid != '28eeee92bcf411e6a7c23ca9f40bc550'
|
||||
|
||||
|
||||
def includeme(config):
|
||||
UsersView.defaults(config)
|
13
rattail_demo/web/webapi.py
Normal file
13
rattail_demo/web/webapi.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Rattail Demo web API
|
||||
"""
|
||||
|
||||
from tailbone import webapi as base
|
||||
|
||||
|
||||
def main(global_config, **settings):
|
||||
"""
|
||||
This function returns a Pyramid WSGI application.
|
||||
"""
|
||||
return base.main(global_config, **settings)
|
80
setup.py
80
setup.py
|
@ -1,80 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Setup script for Rattail Demo
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
execfile(os.path.join(here, 'rattail_demo', '_version.py'))
|
||||
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
|
||||
'Tailbone', # 0.5.49
|
||||
'xlrd', # 1.0.0
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name = "rattail-demo",
|
||||
version = __version__,
|
||||
author = "Lance Edgar",
|
||||
author_email = "lance@edbob.org",
|
||||
url = "https://rattailproject.org/",
|
||||
description = "Rattail Software Demo",
|
||||
long_description = README,
|
||||
|
||||
classifiers = [
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Topic :: Office/Business',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
|
||||
install_requires = requires,
|
||||
packages = find_packages(),
|
||||
|
||||
entry_points = {
|
||||
'paste.app_factory': [
|
||||
'main = rattail_demo.web.app:main',
|
||||
],
|
||||
},
|
||||
)
|
23
tasks.py
Normal file
23
tasks.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""
|
||||
Tasks for rattail-demo
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from invoke import task
|
||||
|
||||
|
||||
@task
|
||||
def release(c):
|
||||
"""
|
||||
Release a new version of 'rattail-demo'
|
||||
"""
|
||||
if os.path.exists('dist'):
|
||||
shutil.rmtree('dist')
|
||||
if os.path.exists('rattail_demo.egg-info'):
|
||||
shutil.rmtree('rattail_demo.egg-info')
|
||||
|
||||
c.run('python -m build --sdist')
|
||||
c.run('twine upload dist/*')
|
Loading…
Reference in a new issue