Massive overhaul of "generate project" feature
previous incarnation was woefully lacking. new feature is much more extensible. still need to remove old POS integration specifics in some places. and a couple of unrelated things that snuck in.. - deprecate `rattail.util.OrderedDict` - deprecate `rattail.util.import_module_path()` - deprecate `rattail.util.import_reload()`
This commit is contained in:
parent
026d98551c
commit
2ed63b1c1a
|
@ -24,10 +24,11 @@
|
|||
Tailbone Web API - "Common" Views
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import rattail
|
||||
from rattail.db import model
|
||||
from rattail.mail import send_email
|
||||
from rattail.util import OrderedDict
|
||||
|
||||
from cornice import Service
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ Forms Core
|
|||
|
||||
import json
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
@ -346,6 +347,7 @@ class Form(object):
|
|||
self.schema = schema
|
||||
if self.fields is None and self.schema:
|
||||
self.set_fields([f.name for f in self.schema])
|
||||
self.grouping = None
|
||||
self.request = request
|
||||
self.readonly = readonly
|
||||
self.readonly_fields = set(readonly_fields or [])
|
||||
|
@ -371,6 +373,7 @@ class Form(object):
|
|||
self.validators = validators or {}
|
||||
self.required = required or {}
|
||||
self.helptext = helptext or {}
|
||||
self.dynamic_helptext = {}
|
||||
self.focus_spec = focus_spec
|
||||
self.action_url = action_url
|
||||
self.cancel_url = cancel_url
|
||||
|
@ -404,6 +407,9 @@ class Form(object):
|
|||
return get_fieldnames(self.request.rattail_config, self.model_class,
|
||||
columns=True, proxies=True, relations=True)
|
||||
|
||||
def set_grouping(self, items):
|
||||
self.grouping = OrderedDict(items)
|
||||
|
||||
def make_renderers(self):
|
||||
"""
|
||||
Return a default set of field renderers, based on :attr:`model_class`.
|
||||
|
@ -728,11 +734,15 @@ class Form(object):
|
|||
"""
|
||||
self.defaults[key] = value
|
||||
|
||||
def set_helptext(self, key, value):
|
||||
def set_helptext(self, key, value, dynamic=False):
|
||||
"""
|
||||
Set the help text for a given field.
|
||||
"""
|
||||
self.helptext[key] = value
|
||||
if value and dynamic:
|
||||
self.dynamic_helptext[key] = True
|
||||
else:
|
||||
self.dynamic_helptext.pop(key, None)
|
||||
|
||||
def has_helptext(self, key):
|
||||
"""
|
||||
|
@ -935,7 +945,10 @@ class Form(object):
|
|||
# TODO: older logic did this only if field was *not*
|
||||
# readonly, perhaps should add that back..
|
||||
if self.has_helptext(fieldname):
|
||||
attrs['message'] = self.render_helptext(fieldname)
|
||||
msgkey = 'message'
|
||||
if self.dynamic_helptext.get(fieldname):
|
||||
msgkey = ':message'
|
||||
attrs[msgkey] = self.render_helptext(fieldname)
|
||||
|
||||
# show errors if present
|
||||
error_messages = self.get_error_messages(field) if field else None
|
||||
|
|
|
@ -27,11 +27,11 @@ Grid Filters
|
|||
import re
|
||||
import datetime
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from rattail.gpc import GPC
|
||||
from rattail.util import OrderedDict
|
||||
from rattail.core import UNSPECIFIED
|
||||
from rattail.time import localtime, make_utc
|
||||
from rattail.util import prettify
|
||||
|
|
|
@ -24,15 +24,13 @@
|
|||
Template Context Helpers
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.time import localtime, make_utc
|
||||
from rattail.util import (pretty_quantity, pretty_hours, hours_as_decimal,
|
||||
OrderedDict)
|
||||
from rattail.util import pretty_quantity, pretty_hours, hours_as_decimal
|
||||
from rattail.db.util import maxlen
|
||||
|
||||
from webhelpers2.html import *
|
||||
|
|
|
@ -667,11 +667,18 @@ class MenuHandler(GenericHandler):
|
|||
'route': 'appinfo',
|
||||
'perm': 'appinfo.list',
|
||||
},
|
||||
])
|
||||
|
||||
if kwargs.get('include_label_settings', False):
|
||||
items.extend([
|
||||
{
|
||||
'title': "Label Settings",
|
||||
'route': 'labelprofiles',
|
||||
'perm': 'labelprofiles.list',
|
||||
},
|
||||
])
|
||||
|
||||
items.extend([
|
||||
{
|
||||
'title': "Raw Settings",
|
||||
'route': 'settings',
|
||||
|
@ -807,7 +814,7 @@ def make_menu_entry(request, item):
|
|||
try:
|
||||
entry['url'] = request.route_url(entry['route'])
|
||||
except KeyError: # happens if no such route
|
||||
log.debug("invalid route name for menu entry: %s", entry)
|
||||
log.warning("invalid route name for menu entry: %s", entry)
|
||||
entry['url'] = entry['route']
|
||||
entry['key'] = entry['route']
|
||||
else:
|
||||
|
|
|
@ -11,6 +11,19 @@
|
|||
<section>
|
||||
% if form_body is not Undefined and form_body:
|
||||
${form_body|n}
|
||||
% elif form.grouping:
|
||||
% for group in form.grouping:
|
||||
<nav class="panel">
|
||||
<p class="panel-heading">${group}</p>
|
||||
<div class="panel-block">
|
||||
<div>
|
||||
% for field in form.grouping[group]:
|
||||
${form.render_buefy_field(field)}
|
||||
% endfor
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
% endfor
|
||||
% else:
|
||||
% for field in form.fields:
|
||||
${form.render_buefy_field(field)}
|
||||
|
|
|
@ -1,480 +0,0 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/page.mako" />
|
||||
|
||||
<%def name="title()">Generate Project</%def>
|
||||
|
||||
<%def name="content_title()"></%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
<b-field horizontal label="Project Type">
|
||||
<b-select v-model="projectType">
|
||||
<option value="rattail">rattail</option>
|
||||
<option value="rattail_integration">rattail-integration</option>
|
||||
<option value="tailbone_integration">tailbone-integration</option>
|
||||
## <option value="byjove">byjove</option>
|
||||
<option value="fabric">fabric</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
<div v-if="projectType == 'rattail'">
|
||||
${h.form(request.current_route_url(), ref='rattailForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('project_type', value='rattail')}
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Naming</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Name"
|
||||
message="The "canonical" name generally used to refer to this project">
|
||||
<b-input name="name" v-model="rattail.name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Slug"
|
||||
message="Used for e.g. naming the project source code folder">
|
||||
<b-input name="slug" v-model="rattail.slug"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Organization"
|
||||
message="For use with "branding" etc.">
|
||||
<b-input name="organization" v-model="rattail.organization"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name for PyPI"
|
||||
message="It's a good idea to use org name as namespace prefix here">
|
||||
<b-input name="python_project_name" v-model="rattail.python_project_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name in Python"
|
||||
:message="`For example, ~/src/${'$'}{rattail.slug}/${'$'}{rattail.python_package_name}/__init__.py`">
|
||||
<b-input name="python_name" v-model="rattail.python_package_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Database</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Has Rattail DB"
|
||||
message="Note that a DB is required for the Web App">
|
||||
<b-checkbox name="has_db"
|
||||
v-model="rattail.has_rattail_db"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Extends Rattail DB Schema"
|
||||
message="For adding custom tables/columns to the core schema">
|
||||
<b-checkbox name="extends_db"
|
||||
v-model="rattail.extends_rattail_db_schema"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Uses Rattail Batch Schema"
|
||||
v-show="false"
|
||||
message="Needed for "dynamic" (e.g. import/export) batches">
|
||||
<b-checkbox name="has_batch_schema"
|
||||
v-model="rattail.uses_rattail_batch_schema"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Web App</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Has Tailbone Web App">
|
||||
<b-checkbox name="has_web"
|
||||
v-model="rattail.has_tailbone_web_app"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Has Tailbone Web API"
|
||||
v-show="false"
|
||||
message="Needed for e.g. Vue.js SPA mobile apps">
|
||||
<b-checkbox name="has_web_api"
|
||||
v-model="rattail.has_tailbone_web_api"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Integrations</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Integrates w/ Catapult"
|
||||
message="Add schema, import/export logic etc. for ECRS Catapult">
|
||||
<b-checkbox name="integrates_catapult"
|
||||
v-model="rattail.integrates_with_catapult"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Integrates w/ CORE-POS"
|
||||
v-show="false">
|
||||
<b-checkbox name="integrates_corepos"
|
||||
v-model="rattail.integrates_with_corepos"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Integrates w/ LOC SMS"
|
||||
message="Add schema, import/export logic etc. for LOC SMS">
|
||||
<b-checkbox name="integrates_locsms"
|
||||
v-model="rattail.integrates_with_locsms"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Has DataSync Service"
|
||||
v-show="false">
|
||||
<b-checkbox name="has_datasync"
|
||||
v-model="rattail.has_datasync_service"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Deployment</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Uses Fabric">
|
||||
<b-checkbox name="uses_fabric"
|
||||
v-model="rattail.uses_fabric"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</div>
|
||||
|
||||
<div v-if="projectType == 'rattail_integration'">
|
||||
${h.form(request.current_route_url(), ref='rattail_integrationForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('project_type', value='rattail_integration')}
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Naming</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Integration Name"
|
||||
message="Name of the system to be integrated">
|
||||
<b-input name="integration_name" v-model="rattail_integration.integration_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Integration URL"
|
||||
message="Reference URL for the system to be integrated">
|
||||
<b-input name="integration_url" v-model="rattail_integration.integration_url"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name for PyPI"
|
||||
message="Also will be used as slug, e.g. for folder name">
|
||||
<b-input name="python_project_name" v-model="rattail_integration.python_project_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
${h.hidden('slug', **{'v-model': 'rattail_integration.python_project_name'})}
|
||||
|
||||
<b-field horizontal label="Package Name in Python"
|
||||
:message="`For example, ~/src/${'$'}{rattail_integration.python_project_name}/${'$'}{rattail_integration.python_package_name}/__init__.py`">
|
||||
<b-input name="python_name" v-model="rattail_integration.python_package_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Options</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Extends Config"
|
||||
message="Adds custom config extension">
|
||||
<b-checkbox name="extends_config"
|
||||
v-model="rattail_integration.extends_config"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Extends Rattail Schema"
|
||||
message="Adds custom tables/columns to the Rattail DB schema">
|
||||
<b-checkbox name="extends_db"
|
||||
v-model="rattail_integration.extends_db"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</div>
|
||||
|
||||
<div v-if="projectType == 'tailbone_integration'">
|
||||
${h.form(request.current_route_url(), ref='tailbone_integrationForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('project_type', value='tailbone_integration')}
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Naming</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Integration Name"
|
||||
message="Name of the system to be integrated">
|
||||
<b-input name="integration_name" v-model="tailbone_integration.integration_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Integration URL"
|
||||
message="Reference URL for the system to be integrated">
|
||||
<b-input name="integration_url" v-model="tailbone_integration.integration_url"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name for PyPI"
|
||||
message="Also will be used as slug, e.g. for folder name">
|
||||
<b-input name="python_project_name" v-model="tailbone_integration.python_project_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
${h.hidden('slug', **{'v-model': 'tailbone_integration.python_project_name'})}
|
||||
|
||||
<b-field horizontal label="Package Name in Python"
|
||||
:message="`For example, ~/src/${'$'}{tailbone_integration.python_project_name}/${'$'}{tailbone_integration.python_package_name}/__init__.py`">
|
||||
<b-input name="python_name" v-model="tailbone_integration.python_package_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Options</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Has Static Files"
|
||||
message="Register a subfolder for static files (images etc.)">
|
||||
<b-checkbox name="has_static_files"
|
||||
v-model="tailbone_integration.has_static_files"
|
||||
native-value="true">
|
||||
</b-checkbox>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${h.end_form()}
|
||||
</div>
|
||||
|
||||
<div v-if="projectType == 'byjove'">
|
||||
${h.form(request.current_route_url(), ref='byjoveForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('project_type', value='byjove')}
|
||||
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Naming</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Name">
|
||||
<b-input name="name" v-model="byjove.name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Slug">
|
||||
<b-input name="slug" v-model="byjove.slug"></b-input>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${h.end_form()}
|
||||
</div>
|
||||
|
||||
<div v-if="projectType == 'fabric'">
|
||||
${h.form(request.current_route_url(), ref='fabricForm')}
|
||||
${h.csrf_token(request)}
|
||||
${h.hidden('project_type', value='fabric')}
|
||||
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Naming</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Name"
|
||||
message="The "canonical" name generally used to refer to this project">
|
||||
<b-input name="name" v-model="fabric.name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Slug"
|
||||
message="Used for e.g. naming the project source code folder">
|
||||
<b-input name="slug" v-model="fabric.slug"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Organization"
|
||||
message="For use with "branding" etc.">
|
||||
<b-input name="organization" v-model="fabric.organization"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name for PyPI"
|
||||
message="It's a good idea to use org name as namespace prefix here">
|
||||
<b-input name="python_project_name" v-model="fabric.python_project_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
<b-field horizontal label="Package Name in Python"
|
||||
:message="`For example, ~/src/${'$'}{fabric.slug}/${'$'}{fabric.python_package_name}/__init__.py`">
|
||||
<b-input name="python_name" v-model="fabric.python_package_name"></b-input>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">Theo</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
||||
<b-field horizontal label="Integrates With"
|
||||
message="Which POS system should Theo integrate with, if any">
|
||||
<b-select name="integrates_with" v-model="fabric.integrates_with">
|
||||
<option value="">(nothing)</option>
|
||||
<option value="catapult">ECRS Catapult</option>
|
||||
<option value="corepos">CORE-POS</option>
|
||||
## <option value="locsms">LOC SMS</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${h.end_form()}
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div class="buttons" style="padding-left: 8rem;">
|
||||
<b-button type="is-primary"
|
||||
@click="submitProjectForm()">
|
||||
Generate Project
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
</%def>
|
||||
|
||||
<%def name="modify_this_page_vars()">
|
||||
${parent.modify_this_page_vars()}
|
||||
<script type="text/javascript">
|
||||
|
||||
ThisPageData.projectType = 'rattail'
|
||||
|
||||
ThisPageData.rattail = {
|
||||
name: "Okay-Then",
|
||||
slug: "okay-then",
|
||||
organization: "Acme Foods",
|
||||
python_project_name: "Acme-Okay-Then",
|
||||
python_package_name: "okay_then",
|
||||
has_rattail_db: true,
|
||||
extends_rattail_db_schema: true,
|
||||
uses_rattail_batch_schema: false,
|
||||
has_tailbone_web_app: true,
|
||||
has_tailbone_web_api: false,
|
||||
has_datasync_service: false,
|
||||
integrates_with_catapult: false,
|
||||
integrates_with_corepos: false,
|
||||
integrates_with_locsms: false,
|
||||
uses_fabric: true,
|
||||
}
|
||||
|
||||
ThisPageData.rattail_integration = {
|
||||
integration_name: "Foo",
|
||||
integration_url: "https://www.example.com/",
|
||||
python_project_name: "rattail-foo",
|
||||
python_package_name: "rattail_foo",
|
||||
extends_config: true,
|
||||
extends_db: true,
|
||||
}
|
||||
|
||||
ThisPageData.tailbone_integration = {
|
||||
integration_name: "Foo",
|
||||
integration_url: "https://www.example.com/",
|
||||
python_project_name: "tailbone-foo",
|
||||
python_package_name: "tailbone_foo",
|
||||
}
|
||||
|
||||
ThisPageData.byjove = {
|
||||
name: "Okay-Then-Mobile",
|
||||
slug: "okay-then-mobile",
|
||||
}
|
||||
|
||||
ThisPageData.fabric = {
|
||||
name: "AcmeFab",
|
||||
slug: "acmefab",
|
||||
organization: "Acme Foods",
|
||||
python_project_name: "Acme-Fabric",
|
||||
python_package_name: "acmefab",
|
||||
integrates_with: '',
|
||||
}
|
||||
|
||||
ThisPage.methods.submitProjectForm = function() {
|
||||
let form = this.$refs[this.projectType + 'Form']
|
||||
form.submit()
|
||||
}
|
||||
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
24
tailbone/templates/generated-projects/create.mako
Normal file
24
tailbone/templates/generated-projects/create.mako
Normal file
|
@ -0,0 +1,24 @@
|
|||
## -*- coding: utf-8; -*-
|
||||
<%inherit file="/master/create.mako" />
|
||||
|
||||
<%def name="title()">${index_title}</%def>
|
||||
|
||||
<%def name="content_title()"></%def>
|
||||
|
||||
<%def name="page_content()">
|
||||
% if project_type:
|
||||
<b-field grouped>
|
||||
<b-field horizontal expanded label="Project Type">
|
||||
${project_type}
|
||||
</b-field>
|
||||
<once-button type="is-primary"
|
||||
tag="a" href="${url('generated_projects.create')}"
|
||||
text="Start Over">
|
||||
</once-button>
|
||||
</b-field>
|
||||
% endif
|
||||
${parent.page_content()}
|
||||
</%def>
|
||||
|
||||
|
||||
${parent.body()}
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,10 +24,9 @@
|
|||
Views for handheld batches
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.util import OrderedDict
|
||||
|
||||
import colander
|
||||
from webhelpers2.html import tags
|
||||
|
|
|
@ -27,12 +27,13 @@ Views for inventory batches
|
|||
import re
|
||||
import decimal
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail import pod
|
||||
from rattail.db import model
|
||||
from rattail.db.util import make_full_description
|
||||
from rattail.gpc import GPC
|
||||
from rattail.util import pretty_quantity, OrderedDict
|
||||
from rattail.util import pretty_quantity
|
||||
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,10 +24,9 @@
|
|||
Views for generic product batches
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.util import OrderedDict
|
||||
|
||||
import colander
|
||||
from webhelpers2.html import HTML
|
||||
|
|
|
@ -25,9 +25,10 @@ Various common views
|
|||
"""
|
||||
|
||||
import os
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.batch import consume_batch_id
|
||||
from rattail.util import OrderedDict, simple_error, import_module_path
|
||||
from rattail.util import simple_error, import_module_path
|
||||
from rattail.files import resource_path
|
||||
|
||||
from pyramid import httpexceptions
|
||||
|
|
|
@ -32,6 +32,7 @@ import getpass
|
|||
import shutil
|
||||
import tempfile
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import json
|
||||
import sqlalchemy as sa
|
||||
|
@ -41,7 +42,7 @@ from sqlalchemy_utils.functions import get_primary_keys, get_columns
|
|||
|
||||
from rattail.db import model, Session as RattailSession
|
||||
from rattail.db.continuum import model_transaction_query
|
||||
from rattail.util import prettify, OrderedDict, simple_error
|
||||
from rattail.util import prettify, simple_error, get_class_hierarchy
|
||||
from rattail.time import localtime
|
||||
from rattail.threads import Thread
|
||||
from rattail.csvutil import UnicodeDictWriter
|
||||
|
@ -268,17 +269,7 @@ class MasterView(View):
|
|||
return labels
|
||||
|
||||
def get_class_hierarchy(self):
|
||||
hierarchy = []
|
||||
|
||||
def traverse(cls):
|
||||
if cls is not object:
|
||||
hierarchy.append(cls)
|
||||
for parent in cls.__bases__:
|
||||
traverse(parent)
|
||||
|
||||
traverse(self.__class__)
|
||||
hierarchy.reverse()
|
||||
return hierarchy
|
||||
return get_class_hierarchy(self.__class__)
|
||||
|
||||
def set_row_labels(self, obj):
|
||||
labels = self.collect_row_labels()
|
||||
|
@ -2215,6 +2206,7 @@ class MasterView(View):
|
|||
"""
|
||||
Returns the master view's index URL.
|
||||
"""
|
||||
if self.listable:
|
||||
route = self.get_route_prefix()
|
||||
return self.request.route_url(route, **kwargs)
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ Person Views
|
|||
|
||||
import datetime
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
@ -33,7 +34,7 @@ from sqlalchemy import orm
|
|||
from rattail.db import model, api
|
||||
from rattail.db.util import maxlen
|
||||
from rattail.time import localtime
|
||||
from rattail.util import OrderedDict, simple_error
|
||||
from rattail.util import simple_error
|
||||
|
||||
import colander
|
||||
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
"""
|
||||
|
||||
import copy
|
||||
from collections import OrderedDict
|
||||
|
||||
from rattail.core import Object
|
||||
from rattail.util import OrderedDict
|
||||
|
||||
from webhelpers2.html import HTML
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ Product Views
|
|||
|
||||
import re
|
||||
import logging
|
||||
|
||||
from collections import OrderedDict
|
||||
import humanize
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
|
@ -37,7 +37,7 @@ from rattail.db import model, api, auth, Session as RattailSession
|
|||
from rattail.gpc import GPC
|
||||
from rattail.threads import Thread
|
||||
from rattail.exceptions import LabelPrintingError
|
||||
from rattail.util import load_object, pretty_quantity, OrderedDict, simple_error
|
||||
from rattail.util import load_object, pretty_quantity, simple_error
|
||||
from rattail.time import localtime, make_utc
|
||||
|
||||
import colander
|
||||
|
|
|
@ -24,207 +24,358 @@
|
|||
Project views
|
||||
"""
|
||||
|
||||
import os
|
||||
import zipfile
|
||||
# from collections import OrderedDict
|
||||
from collections import OrderedDict
|
||||
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
|
||||
from rattail.projects import PythonProjectGenerator, PoserProjectGenerator
|
||||
|
||||
from tailbone import forms
|
||||
from tailbone.views import View
|
||||
from tailbone.views import MasterView
|
||||
|
||||
|
||||
class GenerateProject(colander.MappingSchema):
|
||||
"""
|
||||
Base schema for the "generate project" form
|
||||
"""
|
||||
name = colander.SchemaNode(colander.String())
|
||||
|
||||
slug = colander.SchemaNode(colander.String())
|
||||
|
||||
organization = colander.SchemaNode(colander.String())
|
||||
|
||||
python_project_name = colander.SchemaNode(colander.String())
|
||||
|
||||
python_name = colander.SchemaNode(colander.String())
|
||||
|
||||
has_db = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
extends_db = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
has_batch_schema = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
has_web = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
has_web_api = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
has_datasync = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
# has_filemon = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
# has_tempmon = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
# has_bouncer = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
integrates_catapult = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
integrates_corepos = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
# integrates_instacart = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
integrates_locsms = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
# integrates_mailchimp = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
uses_fabric = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
|
||||
class GenerateRattailIntegrationProject(colander.MappingSchema):
|
||||
"""
|
||||
Schema to generate new rattail-integration project
|
||||
"""
|
||||
integration_name = colander.SchemaNode(colander.String())
|
||||
|
||||
integration_url = colander.SchemaNode(colander.String())
|
||||
|
||||
slug = colander.SchemaNode(colander.String())
|
||||
|
||||
python_project_name = colander.SchemaNode(colander.String())
|
||||
|
||||
python_name = colander.SchemaNode(colander.String())
|
||||
|
||||
extends_config = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
extends_db = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
|
||||
class GenerateTailboneIntegrationProject(colander.MappingSchema):
|
||||
"""
|
||||
Schema to generate new tailbone-integration project
|
||||
"""
|
||||
integration_name = colander.SchemaNode(colander.String())
|
||||
|
||||
integration_url = colander.SchemaNode(colander.String())
|
||||
|
||||
slug = colander.SchemaNode(colander.String())
|
||||
|
||||
python_project_name = colander.SchemaNode(colander.String())
|
||||
|
||||
python_name = colander.SchemaNode(colander.String())
|
||||
|
||||
has_static_files = colander.SchemaNode(colander.Boolean())
|
||||
|
||||
|
||||
class GenerateByjoveProject(colander.MappingSchema):
|
||||
"""
|
||||
Schema for generating a new 'byjove' project
|
||||
"""
|
||||
name = colander.SchemaNode(colander.String())
|
||||
|
||||
slug = colander.SchemaNode(colander.String())
|
||||
|
||||
|
||||
class GenerateFabricProject(colander.MappingSchema):
|
||||
"""
|
||||
Schema for generating a new 'fabric' project
|
||||
"""
|
||||
name = colander.SchemaNode(colander.String())
|
||||
|
||||
slug = colander.SchemaNode(colander.String())
|
||||
|
||||
organization = colander.SchemaNode(colander.String())
|
||||
|
||||
python_project_name = colander.SchemaNode(colander.String())
|
||||
|
||||
python_name = colander.SchemaNode(colander.String())
|
||||
|
||||
integrates_with = colander.SchemaNode(colander.String(),
|
||||
missing=colander.null)
|
||||
|
||||
|
||||
class GenerateProjectView(View):
|
||||
class GeneratedProjectView(MasterView):
|
||||
"""
|
||||
View for generating new project source code
|
||||
"""
|
||||
model_title = "Generated Project"
|
||||
model_key = 'folder'
|
||||
route_prefix = 'generated_projects'
|
||||
url_prefix = '/generated-projects'
|
||||
listable = False
|
||||
viewable = False
|
||||
editable = False
|
||||
deletable = False
|
||||
|
||||
def __init__(self, request):
|
||||
super(GenerateProjectView, self).__init__(request)
|
||||
self.project_handler = self.get_handler()
|
||||
# TODO: deprecate / remove this
|
||||
self.handler = self.project_handler
|
||||
super(GeneratedProjectView, self).__init__(request)
|
||||
self.project_handler = self.get_project_handler()
|
||||
|
||||
def get_handler(self):
|
||||
from rattail.projects.handler import RattailProjectHandler
|
||||
return RattailProjectHandler(self.rattail_config)
|
||||
def get_project_handler(self):
|
||||
app = self.get_rattail_app()
|
||||
return app.get_project_handler()
|
||||
|
||||
def __call__(self):
|
||||
def create(self):
|
||||
supported = self.project_handler.get_supported_project_generators()
|
||||
supported_keys = list(supported)
|
||||
|
||||
# choices = OrderedDict([
|
||||
# ('has_db', {'prompt': "Does project need its own Rattail DB?",
|
||||
# 'type': 'bool'}),
|
||||
# ])
|
||||
|
||||
project_type = 'rattail'
|
||||
if self.request.method == 'POST':
|
||||
project_type = self.request.POST.get('project_type', 'rattail')
|
||||
if project_type not in self.project_handler.get_supported_project_types():
|
||||
raise ValueError("Unknown project type: {}".format(project_type))
|
||||
|
||||
if project_type == 'byjove':
|
||||
schema = GenerateByjoveProject
|
||||
elif project_type == 'fabric':
|
||||
schema = GenerateFabricProject
|
||||
elif project_type == 'rattail_integration':
|
||||
schema = GenerateRattailIntegrationProject
|
||||
elif project_type == 'tailbone_integration':
|
||||
schema = GenerateTailboneIntegrationProject
|
||||
else:
|
||||
schema = GenerateProject
|
||||
form = forms.Form(schema=schema(), request=self.request)
|
||||
project_type = self.request.matchdict.get('project_type')
|
||||
if project_type:
|
||||
form = self.make_project_form(project_type)
|
||||
if form.validate(newstyle=True):
|
||||
zipped = self.generate_project(project_type, form)
|
||||
return self.file_response(zipped)
|
||||
# self.request.session.flash("New project was generated: {}".format(form.validated['name']))
|
||||
# return self.redirect(self.request.current_route_url())
|
||||
|
||||
return {
|
||||
else: # no project_type
|
||||
|
||||
# make form to accept user choice of report type
|
||||
schema = colander.Schema()
|
||||
values = [(typ, typ) for typ in supported_keys]
|
||||
schema.add(colander.SchemaNode(name='project_type',
|
||||
typ=colander.String(),
|
||||
validator=colander.OneOf(supported_keys),
|
||||
widget=dfwidget.SelectWidget(values=values)))
|
||||
form = forms.Form(schema=schema, request=self.request)
|
||||
form.submit_label = "Continue"
|
||||
|
||||
# if form validates, then user has chosen a project type, so
|
||||
# we redirect to the appropriate "generate project" page
|
||||
if form.validate(newstyle=True):
|
||||
raise self.redirect(self.request.route_url(
|
||||
'generate_specific_project',
|
||||
project_type=form.validated['project_type']))
|
||||
|
||||
return self.render_to_response('create', {
|
||||
'index_title': "Generate Project",
|
||||
'handler': self.handler,
|
||||
# 'choices': choices,
|
||||
}
|
||||
'project_type': project_type,
|
||||
'form': form,
|
||||
})
|
||||
|
||||
def generate_project(self, project_type, form):
|
||||
options = form.validated
|
||||
slug = options['slug']
|
||||
path = self.handler.generate_project(project_type, slug, options)
|
||||
context = dict(form.validated)
|
||||
output = self.project_handler.generate_project(project_type,
|
||||
context=context)
|
||||
return self.project_handler.zip_output(output)
|
||||
|
||||
zipped = '{}.zip'.format(path)
|
||||
with zipfile.ZipFile(zipped, 'w', zipfile.ZIP_DEFLATED) as z:
|
||||
self.zipdir(z, path, slug)
|
||||
return zipped
|
||||
def make_project_form(self, project_type):
|
||||
|
||||
def zipdir(self, zipf, path, slug):
|
||||
for root, dirs, files in os.walk(path):
|
||||
relative_root = os.path.join(slug, root[len(path)+1:])
|
||||
for fname in files:
|
||||
zipf.write(os.path.join(root, fname),
|
||||
arcname=os.path.join(relative_root, fname))
|
||||
# make form
|
||||
schema = self.project_handler.make_project_schema(project_type)
|
||||
form = forms.Form(schema=schema, request=self.request)
|
||||
form.auto_disable = False
|
||||
form.auto_disable_save = False
|
||||
form.submit_label = "Generate Project"
|
||||
form.cancel_url = self.request.route_url('generated_projects.create')
|
||||
|
||||
# apply normal config
|
||||
self.configure_form_common(form, project_type)
|
||||
|
||||
# let supplemental views further configure form
|
||||
for supp in self.iter_view_supplements():
|
||||
configure = getattr(supp, 'configure_form_{}'.format(project_type), None)
|
||||
if configure:
|
||||
configure(form)
|
||||
|
||||
# if master view has more configure logic, do that too
|
||||
configure = getattr(self, 'configure_form_{}'.format(project_type), None)
|
||||
if configure:
|
||||
configure(form)
|
||||
|
||||
return form
|
||||
|
||||
def configure_form_common(self, form, project_type):
|
||||
generator = self.project_handler.get_project_generator(project_type,
|
||||
require=True)
|
||||
|
||||
# python-based projects
|
||||
if isinstance(generator, PythonProjectGenerator):
|
||||
self.configure_form_python(form)
|
||||
|
||||
# poser-based projects
|
||||
if isinstance(generator, PoserProjectGenerator):
|
||||
self.configure_form_poser(form)
|
||||
|
||||
def configure_form_python(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'name',
|
||||
'pkg_name',
|
||||
'pypi_name',
|
||||
]),
|
||||
])
|
||||
|
||||
# name
|
||||
f.set_label('name', "Project Name")
|
||||
f.set_helptext('name', "Human-friendly name generally used to refer to this project.")
|
||||
f.set_default('name', "Poser Plus")
|
||||
|
||||
# pkg_name
|
||||
f.set_label('pkg_name', "Package Name in Python")
|
||||
f.set_helptext('pkg_name', "`For example, ~/src/${field_model_pkg_name.replace(/_/g, '-')}/${field_model_pkg_name}/__init__.py`",
|
||||
dynamic=True)
|
||||
f.set_default('pkg_name', "poser_plus")
|
||||
|
||||
# pypi_name
|
||||
f.set_label('pypi_name', "Package Name for PyPI")
|
||||
f.set_helptext('pypi_name', "It's a good idea to use org name as namespace prefix here")
|
||||
f.set_default('pypi_name', "Acme-Poser-Plus")
|
||||
|
||||
def configure_form_poser(self, f):
|
||||
|
||||
# extends_config
|
||||
f.set_label('extends_config', "Extend Config")
|
||||
f.set_helptext('extends_config', "Needed to customize default config values etc.")
|
||||
f.set_default('extends_config', True)
|
||||
|
||||
# has_cli
|
||||
f.set_label('has_cli', "Use Separate CLI")
|
||||
f.set_helptext('has_cli', "`Needed for e.g. '${field_model_pkg_name} install' command.`",
|
||||
dynamic=True)
|
||||
f.set_default('has_cli', True)
|
||||
|
||||
# organization
|
||||
f.set_helptext('organization', 'For use with branding etc.')
|
||||
f.set_default('organization', "Acme Foods")
|
||||
|
||||
# has_db
|
||||
f.set_label('has_db', "Use Rattail DB")
|
||||
f.set_helptext('has_db', "Note that a DB is required for the Web App")
|
||||
f.set_default('has_db', True)
|
||||
|
||||
# extends_db
|
||||
f.set_label('extends_db', "Extend DB Schema")
|
||||
f.set_helptext('extends_db', "For adding custom tables/columns to the core schema")
|
||||
f.set_default('extends_db', True)
|
||||
|
||||
# has_batch_schema
|
||||
f.set_label('has_batch_schema', "Add Batch Schema")
|
||||
f.set_helptext('has_batch_schema', 'Usually not needed - it\'s for "dynamic" (e.g. import/export) batches')
|
||||
|
||||
# has_web
|
||||
f.set_label('has_web', "Use Tailbone Web App")
|
||||
f.set_default('has_web', True)
|
||||
|
||||
# has_web_api
|
||||
f.set_label('has_web_api', "Use Tailbone Web API")
|
||||
f.set_helptext('has_web_api', "Needed for e.g. Vue.js SPA mobile apps")
|
||||
|
||||
# has_datasync
|
||||
f.set_label('has_datasync', "Use DataSync Service")
|
||||
|
||||
# uses_fabric
|
||||
f.set_label('uses_fabric', "Use Fabric")
|
||||
f.set_default('uses_fabric', True)
|
||||
|
||||
def configure_form_rattail(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'name',
|
||||
'pkg_name',
|
||||
'pypi_name',
|
||||
'organization',
|
||||
]),
|
||||
("Core", [
|
||||
'extends_config',
|
||||
'has_cli',
|
||||
]),
|
||||
("Database", [
|
||||
'has_db',
|
||||
'extends_db',
|
||||
'has_batch_schema',
|
||||
]),
|
||||
("Web", [
|
||||
'has_web',
|
||||
'has_web_api',
|
||||
]),
|
||||
("Integrations", [
|
||||
# 'integrates_catapult',
|
||||
# 'integrates_corepos',
|
||||
# 'integrates_locsms',
|
||||
'has_datasync',
|
||||
]),
|
||||
("Deployment", [
|
||||
'uses_fabric',
|
||||
]),
|
||||
])
|
||||
|
||||
# # integrates_catapult
|
||||
# f.set_label('integrates_catapult', "Integrate w/ Catapult")
|
||||
# f.set_helptext('integrates_catapult', "Add schema, import/export logic etc. for ECRS Catapult")
|
||||
|
||||
# # integrates_corepos
|
||||
# f.set_label('integrates_corepos', "Integrate w/ CORE-POS")
|
||||
# f.set_helptext('integrates_corepos', "Add schema, import/export logic etc. for CORE-POS")
|
||||
|
||||
# # integrates_locsms
|
||||
# f.set_label('integrates_locsms', "Integrate w/ LOC SMS")
|
||||
# f.set_helptext('integrates_locsms', "Add schema, import/export logic etc. for LOC SMS")
|
||||
|
||||
def configure_form_rattail_integration(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'integration_name',
|
||||
'integration_url',
|
||||
'name',
|
||||
'pkg_name',
|
||||
'pypi_name',
|
||||
]),
|
||||
("Options", [
|
||||
'extends_config',
|
||||
'extends_db',
|
||||
]),
|
||||
])
|
||||
|
||||
# integration_name
|
||||
f.set_helptext('integration_name', "Name of the system to be integrated")
|
||||
f.set_default('integration_name', "Foo")
|
||||
|
||||
# integration_url
|
||||
f.set_label('integration_url', "Integration URL")
|
||||
f.set_helptext('integration_url', "Reference URL for the system to be integrated")
|
||||
f.set_default('integration_url', "https://www.example.com/")
|
||||
|
||||
def configure_form_tailbone_integration(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'integration_name',
|
||||
'integration_url',
|
||||
'name',
|
||||
'pkg_name',
|
||||
'pypi_name',
|
||||
]),
|
||||
("Options", [
|
||||
'has_static_files',
|
||||
]),
|
||||
])
|
||||
|
||||
# integration_name
|
||||
f.set_helptext('integration_name', "Name of the system to be integrated")
|
||||
f.set_default('integration_name', "Foo")
|
||||
|
||||
# integration_url
|
||||
f.set_label('integration_url', "Integration URL")
|
||||
f.set_helptext('integration_url', "Reference URL for the system to be integrated")
|
||||
f.set_default('integration_url', "https://www.example.com/")
|
||||
|
||||
# has_static_files
|
||||
f.set_helptext('has_static_files', "Register a subfolder for static files (images etc.)")
|
||||
|
||||
def configure_form_byjove(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'name',
|
||||
'slug',
|
||||
]),
|
||||
])
|
||||
|
||||
# name
|
||||
f.set_default('name', "Okay Then Mobile")
|
||||
|
||||
# slug
|
||||
f.set_default('slug', "okay-then-mobile")
|
||||
|
||||
def configure_form_fabric(self, f):
|
||||
|
||||
f.set_grouping([
|
||||
("Naming", [
|
||||
'name',
|
||||
'pkg_name',
|
||||
'pypi_name',
|
||||
'organization',
|
||||
]),
|
||||
("Theo", [
|
||||
'integrates_with',
|
||||
]),
|
||||
])
|
||||
|
||||
# naming defaults
|
||||
f.set_default('name', "Acme Fabric")
|
||||
f.set_default('pkg_name', "acmefab")
|
||||
f.set_default('pypi_name', "Acme-Fabric")
|
||||
|
||||
# organization
|
||||
f.set_helptext('organization', 'For use with branding etc.')
|
||||
f.set_default('organization', "Acme Foods")
|
||||
|
||||
# integrates_with
|
||||
f.set_helptext('integrates_with', "Which POS system should Theo integrate with, if any")
|
||||
f.set_enum('integrates_with', OrderedDict([
|
||||
('', "(nothing)"),
|
||||
('catapult', "ECRS Catapult"),
|
||||
('corepos', "CORE-POS"),
|
||||
('locsms', "LOC SMS")
|
||||
]))
|
||||
f.set_default('integrates_with', '')
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
config.add_tailbone_permission('common', 'common.generate_project',
|
||||
"Generate new project source code")
|
||||
config.add_route('generate_project', '/generate-project')
|
||||
config.add_view(cls, route_name='generate_project',
|
||||
permission='common.generate_project',
|
||||
renderer='/generate_project.mako')
|
||||
cls._defaults(config)
|
||||
cls._generated_project_defaults(config)
|
||||
|
||||
@classmethod
|
||||
def _generated_project_defaults(cls, config):
|
||||
url_prefix = cls.get_url_prefix()
|
||||
permission_prefix = cls.get_permission_prefix()
|
||||
|
||||
# generate project (accept custom params, truly create)
|
||||
config.add_route('generate_specific_project',
|
||||
'{}/new/{{project_type}}'.format(url_prefix))
|
||||
config.add_view(cls, attr='create',
|
||||
route_name='generate_specific_project',
|
||||
permission='{}.create'.format(permission_prefix))
|
||||
|
||||
|
||||
def defaults(config, **kwargs):
|
||||
base = globals()
|
||||
|
||||
GenerateProjectView = kwargs.get('GenerateProjectView', base['GenerateProjectView'])
|
||||
GenerateProjectView.defaults(config)
|
||||
GeneratedProjectView = kwargs.get('GeneratedProjectView', base['GeneratedProjectView'])
|
||||
GeneratedProjectView.defaults(config)
|
||||
|
||||
|
||||
def includeme(config):
|
||||
|
|
|
@ -28,6 +28,7 @@ import os
|
|||
import re
|
||||
import decimal
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import humanize
|
||||
import sqlalchemy as sa
|
||||
|
@ -35,7 +36,7 @@ import sqlalchemy as sa
|
|||
from rattail import pod
|
||||
from rattail.db import model, Session as RattailSession
|
||||
from rattail.time import localtime, make_utc
|
||||
from rattail.util import pretty_quantity, prettify, OrderedDict, simple_error
|
||||
from rattail.util import pretty_quantity, prettify, simple_error
|
||||
from rattail.threads import Thread
|
||||
|
||||
import colander
|
||||
|
|
|
@ -29,13 +29,14 @@ import json
|
|||
import re
|
||||
import datetime
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
import rattail
|
||||
from rattail.db import model, Session as RattailSession
|
||||
from rattail.files import resource_path
|
||||
from rattail.time import localtime
|
||||
from rattail.threads import Thread
|
||||
from rattail.util import simple_error, OrderedDict
|
||||
from rattail.util import simple_error
|
||||
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
|
|
|
@ -28,12 +28,13 @@ import os
|
|||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
|
||||
import json
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.settings import Setting
|
||||
from rattail.util import import_module_path, OrderedDict
|
||||
from rattail.util import import_module_path
|
||||
|
||||
import colander
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import os
|
|||
import sys
|
||||
import warnings
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy_utils import get_mapper
|
||||
|
||||
from rattail.util import simple_error
|
||||
|
@ -96,8 +97,8 @@ class TableView(MasterView):
|
|||
where schemaname = 'public'
|
||||
order by n_live_tup desc;
|
||||
"""
|
||||
result = self.Session.execute(sql)
|
||||
return [dict(table_name=row['relname'], row_count=row['n_live_tup'])
|
||||
result = self.Session.execute(sa.text(sql))
|
||||
return [dict(table_name=row.relname, row_count=row.n_live_tup)
|
||||
for row in result]
|
||||
|
||||
def configure_grid(self, g):
|
||||
|
|
|
@ -29,6 +29,7 @@ import os
|
|||
import re
|
||||
import logging
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
@ -36,7 +37,6 @@ from rattail.core import Object
|
|||
from rattail.db import model, Session as RattailSession
|
||||
from rattail.time import make_utc
|
||||
from rattail.threads import Thread
|
||||
from rattail.util import OrderedDict
|
||||
|
||||
from deform import widget as dfwidget
|
||||
from webhelpers2.html import tags, HTML
|
||||
|
|
Loading…
Reference in a new issue