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 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,4 @@
|
||||||
|
*~
|
||||||
|
*.pyc
|
||||||
|
dist/
|
||||||
rattail_demo.egg-info/
|
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
|
Pyramid web application
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
|
||||||
|
|
||||||
from tailbone import app
|
from tailbone import app
|
||||||
|
from tailbone_corepos.db import CoreOfficeSession, CoreTransSession
|
||||||
|
|
||||||
|
|
||||||
def main(global_config, **settings):
|
def main(global_config, **settings):
|
||||||
"""
|
"""
|
||||||
This function returns a Pyramid WSGI application.
|
This function returns a Pyramid WSGI application.
|
||||||
"""
|
"""
|
||||||
# set some defaults for PostgreSQL
|
# prefer demo templates over tailbone
|
||||||
app.provide_postgresql_settings(settings)
|
|
||||||
|
|
||||||
# prefer demo templates over tailbone; use 'better' theme
|
|
||||||
settings.setdefault('mako.directories', ['rattail_demo.web:templates',
|
settings.setdefault('mako.directories', ['rattail_demo.web:templates',
|
||||||
'tailbone:templates/themes/better',
|
'tailbone_corepos:templates',
|
||||||
|
'tailbone_woocommerce:templates',
|
||||||
'tailbone:templates',])
|
'tailbone:templates',])
|
||||||
|
|
||||||
|
# for graceful handling of postgres restart
|
||||||
|
settings.setdefault('retry.attempts', 2)
|
||||||
|
|
||||||
# make config objects
|
# make config objects
|
||||||
rattail_config = app.make_rattail_config(settings)
|
rattail_config = app.make_rattail_config(settings)
|
||||||
pyramid_config = app.make_pyramid_config(settings)
|
pyramid_config = app.make_pyramid_config(settings)
|
||||||
|
|
||||||
# bring in rest of rattail-demo etc.
|
# configure database sessions
|
||||||
pyramid_config.include('tailbone.static')
|
CoreOfficeSession.configure(bind=rattail_config.corepos_engine)
|
||||||
pyramid_config.include('tailbone.subscribers')
|
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')
|
pyramid_config.include('rattail_demo.web.views')
|
||||||
|
|
||||||
# configure PostgreSQL some more
|
# for graceful handling of postgres restart
|
||||||
app.configure_postgresql(pyramid_config)
|
pyramid_config.add_tween('tailbone.tweens.sqlerror_tween_factory',
|
||||||
|
under='pyramid_tm.tm_tween_factory')
|
||||||
|
|
||||||
return pyramid_config.make_wsgi_app()
|
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" />
|
<%inherit file="tailbone:templates/login.mako" />
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
|
@ -11,8 +11,12 @@
|
||||||
</style>
|
</style>
|
||||||
</%def>
|
</%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
|
${parent.body()}
|
||||||
</p>
|
|
||||||
|
|
|
@ -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
|
Web views
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from tailbone.views import essentials
|
||||||
|
|
||||||
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.")
|
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
|
|
||||||
# TODO: merge these views into core/common
|
# tailbone essentials
|
||||||
config.add_route('home', '/')
|
essentials.defaults(config, **{
|
||||||
config.add_view(base.home, route_name='home', renderer='/home.mako')
|
'tailbone.views.upgrades': 'rattail_demo.web.views.upgrades',
|
||||||
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')
|
|
||||||
|
|
||||||
# main table views
|
# main table views
|
||||||
config.include('tailbone.views.brands')
|
config.include('tailbone.views.brands')
|
||||||
|
config.include('tailbone.views.categories')
|
||||||
config.include('tailbone.views.customers')
|
config.include('tailbone.views.customers')
|
||||||
|
config.include('tailbone.views.customergroups')
|
||||||
config.include('tailbone.views.departments')
|
config.include('tailbone.views.departments')
|
||||||
config.include('tailbone.views.employees')
|
config.include('tailbone.views.employees')
|
||||||
config.include('tailbone.views.families')
|
config.include('tailbone.views.families')
|
||||||
config.include('rattail_demo.web.views.people')
|
config.include('tailbone.views.members')
|
||||||
config.include('tailbone.views.products')
|
config.include('tailbone.views.messages')
|
||||||
|
config.include('rattail_demo.web.views.products')
|
||||||
config.include('tailbone.views.reportcodes')
|
config.include('tailbone.views.reportcodes')
|
||||||
config.include('tailbone.views.roles')
|
|
||||||
config.include('tailbone.views.settings')
|
|
||||||
config.include('tailbone.views.stores')
|
config.include('tailbone.views.stores')
|
||||||
config.include('tailbone.views.subdepartments')
|
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.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
|
# batch views
|
||||||
config.include('tailbone.views.handheld')
|
config.include('tailbone.views.batch.handheld')
|
||||||
config.include('tailbone.views.inventory')
|
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