[MERGE] odoo command: scaffold, start, deploy
This commit is contained in:
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
# no multipart encoding in stdlib and this script is temporary
|
||||
sys.exit("This script requires the 'requests' module. ( pip install requests )")
|
||||
|
||||
session = requests.session()
|
||||
|
||||
def deploy_module(module_path, url, login, password, db=''):
|
||||
url = url.rstrip('/')
|
||||
authenticate(url, login, password, db)
|
||||
module_file = zip_module(module_path)
|
||||
try:
|
||||
return upload_module(url, module_file)
|
||||
finally:
|
||||
os.remove(module_file)
|
||||
|
||||
def upload_module(server, module_file):
|
||||
print("Uploading module file...")
|
||||
url = server + '/base_import_module/upload'
|
||||
files = dict(mod_file=open(module_file, 'rb'))
|
||||
res = session.post(url, files=files)
|
||||
if res.status_code != 200:
|
||||
raise Exception("Could not authenticate on server '%s'" % server)
|
||||
return res.text
|
||||
|
||||
def authenticate(server, login, password, db=''):
|
||||
print("Authenticating on server '%s' ..." % server)
|
||||
|
||||
# Fixate session with a given db if any
|
||||
session.get(server + '/web/login', params=dict(db=db))
|
||||
|
||||
args = dict(login=login, password=password, db=db)
|
||||
res = session.post(server + '/base_import_module/login', args)
|
||||
if res.status_code == 404:
|
||||
raise Exception("The server '%s' does not have the 'base_import_module' installed." % server)
|
||||
elif res.status_code != 200:
|
||||
raise Exception(res.text)
|
||||
|
||||
def zip_module(path):
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.isdir(path):
|
||||
raise Exception("Could not find module directory '%s'" % path)
|
||||
container, module_name = os.path.split(path)
|
||||
temp = tempfile.mktemp(suffix='.zip')
|
||||
try:
|
||||
print("Zipping module directory...")
|
||||
with zipfile.ZipFile(temp, 'w') as zfile:
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
zfile.write(file_path, file_path.split(container).pop())
|
||||
return temp
|
||||
except Exception:
|
||||
os.remove(temp)
|
||||
raise
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Deploy a module on an OpenERP server.')
|
||||
parser.add_argument('path', help="Path of the module to deploy")
|
||||
parser.add_argument('--url', dest='url', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069")
|
||||
parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.')
|
||||
parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)')
|
||||
parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)')
|
||||
parser.add_argument('--no-ssl-check', dest='no_ssl_check', action='store_true', help='Do not check ssl cert')
|
||||
if len(sys.argv) == 1:
|
||||
sys.exit(parser.print_help())
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.no_ssl_check:
|
||||
session.verify = False
|
||||
|
||||
try:
|
||||
result = deploy_module(args.path, args.url, args.login, args.password, args.db)
|
||||
print(result)
|
||||
except Exception, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
+10
-1
@@ -1,5 +1,6 @@
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
|
||||
import openerp
|
||||
from openerp import tools
|
||||
@@ -25,12 +26,18 @@ class Command(object):
|
||||
pass
|
||||
|
||||
class Help(Command):
|
||||
"""Display the list of available commands"""
|
||||
def run(self, args):
|
||||
print "Available commands:\n"
|
||||
padding = max([len(k) for k in commands.keys()]) + 2
|
||||
for k, v in commands.items():
|
||||
print " %s" % k
|
||||
print " %s%s" % (k.ljust(padding, ' '), v.__doc__ or '')
|
||||
print "\nUse '%s <command> --help' for individual command help." % sys.argv[0].split(os.path.sep)[-1]
|
||||
|
||||
import server
|
||||
import deploy
|
||||
import scaffold
|
||||
import start
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
@@ -47,6 +54,7 @@ def main():
|
||||
|
||||
# Subcommand discovery
|
||||
if len(args) and not args[0].startswith("-"):
|
||||
logging.disable(logging.CRITICAL)
|
||||
for m in module.get_modules():
|
||||
m = 'openerp.addons.' + m
|
||||
__import__(m)
|
||||
@@ -54,6 +62,7 @@ def main():
|
||||
#except Exception, e:
|
||||
# raise
|
||||
# print e
|
||||
logging.disable(logging.NOTSET)
|
||||
command = args[0]
|
||||
args = args[1:]
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import argparse
|
||||
import os
|
||||
import requests
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from . import Command
|
||||
|
||||
class Deploy(Command):
|
||||
"""Deploy a module on an Odoo instance"""
|
||||
def __init__(self):
|
||||
super(Deploy, self).__init__()
|
||||
self.session = requests.session()
|
||||
|
||||
def deploy_module(self, module_path, url, login, password, db=''):
|
||||
url = url.rstrip('/')
|
||||
self.authenticate(url, login, password, db)
|
||||
module_file = self.zip_module(module_path)
|
||||
try:
|
||||
return self.upload_module(url, module_file)
|
||||
finally:
|
||||
os.remove(module_file)
|
||||
|
||||
def upload_module(self, server, module_file):
|
||||
print("Uploading module file...")
|
||||
url = server + '/base_import_module/upload'
|
||||
files = dict(mod_file=open(module_file, 'rb'))
|
||||
res = self.session.post(url, files=files)
|
||||
if res.status_code != 200:
|
||||
raise Exception("Could not authenticate on server '%s'" % server)
|
||||
return res.text
|
||||
|
||||
def authenticate(self, server, login, password, db=''):
|
||||
print("Authenticating on server '%s' ..." % server)
|
||||
|
||||
# Fixate session with a given db if any
|
||||
self.session.get(server + '/web/login', params=dict(db=db))
|
||||
|
||||
args = dict(login=login, password=password, db=db)
|
||||
res = self.session.post(server + '/base_import_module/login', args)
|
||||
if res.status_code == 404:
|
||||
raise Exception("The server '%s' does not have the 'base_import_module' installed." % server)
|
||||
elif res.status_code != 200:
|
||||
raise Exception(res.text)
|
||||
|
||||
def zip_module(self, path):
|
||||
path = os.path.abspath(path)
|
||||
if not os.path.isdir(path):
|
||||
raise Exception("Could not find module directory '%s'" % path)
|
||||
container, module_name = os.path.split(path)
|
||||
temp = tempfile.mktemp(suffix='.zip')
|
||||
try:
|
||||
print("Zipping module directory...")
|
||||
with zipfile.ZipFile(temp, 'w') as zfile:
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
zfile.write(file_path, file_path.split(container).pop())
|
||||
return temp
|
||||
except Exception:
|
||||
os.remove(temp)
|
||||
raise
|
||||
|
||||
def run(self, cmdargs):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="%s deploy" % sys.argv[0].split(os.path.sep)[-1],
|
||||
description=self.__doc__
|
||||
)
|
||||
parser.add_argument('path', help="Path of the module to deploy")
|
||||
parser.add_argument('url', nargs='?', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069")
|
||||
parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.')
|
||||
parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)')
|
||||
parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)')
|
||||
parser.add_argument('--no-ssl-check', dest='no_ssl_check', action='store_true', help='Do not check ssl cert')
|
||||
if not cmdargs:
|
||||
sys.exit(parser.print_help())
|
||||
|
||||
args = parser.parse_args(args=cmdargs)
|
||||
|
||||
if args.no_ssl_check:
|
||||
self.session.verify = False
|
||||
|
||||
try:
|
||||
if not args.url.startswith('http://'):
|
||||
args.url = 'https://%s' % args.url
|
||||
result = self.deploy_module(args.path, args.url, args.login, args.password, args.db)
|
||||
print(result)
|
||||
except Exception, e:
|
||||
sys.exit("ERROR: %s" % e)
|
||||
@@ -0,0 +1,249 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import argparse
|
||||
import ast
|
||||
import functools
|
||||
import keyword
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import simplejson
|
||||
import sys
|
||||
|
||||
import jinja2
|
||||
|
||||
from . import Command
|
||||
|
||||
from openerp.modules.module import (get_module_root, MANIFEST, load_information_from_description_file as load_manifest)
|
||||
|
||||
|
||||
class Scaffold(Command):
|
||||
"Generate an Odoo module skeleton."
|
||||
|
||||
def run(self, cmdargs):
|
||||
# TODO: bash completion file
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="%s scaffold" % sys.argv[0].split(os.path.sep)[-1],
|
||||
description=self.__doc__
|
||||
)
|
||||
parser.add_argument('--init', type=identifier, help='Initialize a new Odoo module')
|
||||
|
||||
parser.add_argument('--dest', default=".",
|
||||
help='Directory where the module should be created/updated (default to current directory)')
|
||||
|
||||
parser.add_argument('--model', type=identifier, help="Name of the model to add")
|
||||
|
||||
parser.add_argument('--controller', type=identifier, help="Name of the controller to add")
|
||||
|
||||
parser.add_argument('--web', action='store_true', default=False,
|
||||
help="Generate structure for a webclient module")
|
||||
|
||||
parser.add_argument('--theme', action='store_true', default=False,
|
||||
help="Generate structure for a Website theme")
|
||||
|
||||
if not cmdargs:
|
||||
sys.exit(parser.print_help())
|
||||
args = parser.parse_args(args=cmdargs)
|
||||
|
||||
dest = directory(args.dest)
|
||||
if args.init:
|
||||
dest = os.path.join(dest, args.init)
|
||||
if os.path.exists(dest):
|
||||
die("Can't initialize module in `%s`: Directory already exists." % dest)
|
||||
if get_module_root(dest):
|
||||
die("Can't init a new module in another Odoo module, you probably want to run this "
|
||||
"command from your project's root")
|
||||
else:
|
||||
mroot = get_module_root(dest)
|
||||
if not mroot:
|
||||
die("The path `%s` provided does not point to an existing Odoo module. "
|
||||
"Forgot to `--init` ?" % dest)
|
||||
dest = mroot
|
||||
|
||||
logging.disable(logging.CRITICAL)
|
||||
scaffold = ScaffoldModule(dest)
|
||||
if args.model:
|
||||
scaffold.add_model(snake(args.model))
|
||||
if args.controller:
|
||||
scaffold.add_controller(args.controller)
|
||||
if args.web:
|
||||
scaffold.add_webclient_structure()
|
||||
if args.theme:
|
||||
scaffold.add_theme_structure()
|
||||
|
||||
class ScaffoldModule(object):
|
||||
"""
|
||||
Object for scaffolding existing or new Odoo modules
|
||||
|
||||
@param path: Path of an existing module or path of module to create
|
||||
"""
|
||||
def __init__(self, path):
|
||||
env = jinja2.Environment(loader=jinja2.PackageLoader(
|
||||
'openerp.cli', 'scaffold'))
|
||||
env.filters['snake'] = snake
|
||||
self.env = env
|
||||
self.path = functools.partial(os.path.join, directory(path))
|
||||
self.created = not os.path.exists(self.path())
|
||||
directory(path, create=True)
|
||||
self.module = self.path().split(os.path.sep)[-1]
|
||||
if self.created:
|
||||
manifest_base = os.path.splitext(MANIFEST)[0]
|
||||
self.render_file('%s.jinja2' % manifest_base, self.path('%s.py' % manifest_base))
|
||||
|
||||
def add_model(self, model):
|
||||
model_module = snake(model)
|
||||
model_file = self.path('models', '%s.py' % model_module)
|
||||
if os.path.exists(model_file):
|
||||
die("Model `%s` already exists !" % model_file)
|
||||
self.add_init_import(self.path('__init__.py'), 'models')
|
||||
self.add_init_import(self.path('models', '__init__.py'), model_module)
|
||||
|
||||
self.render_file('models.jinja2', model_file, model=model)
|
||||
self.render_file('ir.model.access.jinja2', self.path('security', 'ir.model.access.csv'),
|
||||
if_exists='append', model=model)
|
||||
self.append_manifest_list('data', 'security/ir.model.access.csv')
|
||||
|
||||
demo_file = 'data/%s_demo.xml' % self.module
|
||||
self.append_xml_data('record.jinja2', self.path(demo_file),
|
||||
model=model)
|
||||
self.append_manifest_list('demo', demo_file)
|
||||
|
||||
def add_controller(self, controller):
|
||||
controller_module = snake(controller)
|
||||
controller_file = self.path('controllers', '%s.py' % controller_module)
|
||||
if os.path.exists(controller_file):
|
||||
die("Controller `%s` already exists !" % controller_file)
|
||||
self.add_init_import(self.path('__init__.py'), 'controllers')
|
||||
# Check if the controller name correspond to a model and expose result to templates
|
||||
has_model = self.has_import(self.path('models', '__init__.py'), controller_module)
|
||||
self.add_init_import(self.path('controllers', '__init__.py'), controller_module)
|
||||
self.render_file('controllers.jinja2', controller_file, controller=controller,
|
||||
has_model=has_model)
|
||||
|
||||
def add_webclient_structure(self):
|
||||
self.append_manifest_list('depends', 'web')
|
||||
prefix = '%s.%%s' % self.module
|
||||
for ext in ('js', 'css', 'xml'):
|
||||
self.render_file('webclient_%s.jinja2' % ext,
|
||||
self.path('static', 'src', ext, prefix % ext))
|
||||
|
||||
def add_theme_structure(self):
|
||||
self.append_manifest_list('depends', 'website')
|
||||
css_file = '%s_theme.css' % self.module
|
||||
self.render_file('theme_css.jinja2', self.path('static', 'src', 'css', css_file))
|
||||
theme_file = '%s_theme.xml' % self.module
|
||||
self.append_xml_data('theme_xml.jinja2', self.path('views', theme_file), skip_if_exist=True)
|
||||
|
||||
def has_import(self, initfile, module):
|
||||
if not os.path.isfile(initfile):
|
||||
return False
|
||||
with open(initfile, 'r') as f:
|
||||
for imp in ast.parse(f.read()).body:
|
||||
if isinstance(imp, ast.Import):
|
||||
if module in [mod.name for mod in imp.names]:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_manifest(self, key=None, default=None):
|
||||
manifest = load_manifest(self.module, self.path())
|
||||
if key:
|
||||
return manifest.get(key, default)
|
||||
else:
|
||||
return manifest
|
||||
|
||||
def append_manifest_list(self, key, value, unique=True):
|
||||
# TODO: append value without altering serialized formatting
|
||||
vals = self.get_manifest(key, [])
|
||||
if unique and value in vals:
|
||||
return
|
||||
vals.append(value)
|
||||
self.change_manifest_key(key, vals)
|
||||
|
||||
def change_manifest_key(self, key, value):
|
||||
value = simplejson.dumps(value)
|
||||
with open(self.path(MANIFEST), 'r') as f:
|
||||
data = f.read()
|
||||
sdata = re.split('["\']%s["\']\s?:\s?\[[^\]]*\]' % key, data)
|
||||
add = "'%s': %s" % (key, value)
|
||||
if len(sdata) != 2:
|
||||
warn("Could not update `%s` key in manifest. You should add this by yourself:"
|
||||
"\n\n%s\n" % (key, add))
|
||||
else:
|
||||
with open(self.path(MANIFEST), 'w') as f:
|
||||
f.write(add.join(sdata))
|
||||
|
||||
def add_init_import(self, initfile, module):
|
||||
if not(os.path.exists(initfile) and self.has_import(initfile, module)):
|
||||
self.render_file('__init__.jinja2', initfile, if_exists='append', modules=[module])
|
||||
|
||||
def render_file(self, template, dest, if_exists='skip', **kwargs):
|
||||
mode = 'a'
|
||||
if os.path.exists(dest):
|
||||
if if_exists == 'replace':
|
||||
mode = 'w'
|
||||
elif if_exists != 'append':
|
||||
warn("File `%s` already exists. Skipping it..." % dest)
|
||||
return
|
||||
else:
|
||||
kwargs['file_created'] = True
|
||||
outdir = os.path.dirname(dest)
|
||||
if not os.path.exists(outdir):
|
||||
os.makedirs(outdir)
|
||||
content = self.env.get_template(template).render(module=self.module, **kwargs)
|
||||
with open(dest, mode) as f:
|
||||
f.write(content)
|
||||
|
||||
def append_xml_data(self, template, dest, skip_if_exist=False, **kwargs):
|
||||
if not os.path.exists(dest):
|
||||
self.render_file('xmldata.jinja2', dest, **kwargs)
|
||||
elif skip_if_exist:
|
||||
warn("File `%s` already exists. Skipping it..." % dest)
|
||||
with open(dest, 'r') as f:
|
||||
data = f.read()
|
||||
m = re.search('(^\s*)?</data>', data, re.MULTILINE)
|
||||
content = self.env.get_template(template).render(module=self.module, **kwargs)
|
||||
if not m:
|
||||
warn("Could not add data in `%s`. You should add this by yourself:"
|
||||
"\n\n%s\n" % (dest, content))
|
||||
else:
|
||||
data = data[:m.start()] + content + m.group() + data[m.end():]
|
||||
with open(self.path(dest), 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
def snake(s):
|
||||
""" snake cases ``s``
|
||||
|
||||
:param str s:
|
||||
:return: str
|
||||
"""
|
||||
# insert a space before each uppercase character preceded by a
|
||||
# non-uppercase letter
|
||||
s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s)
|
||||
# lowercase everything, split on whitespace and join
|
||||
return '_'.join(s.lower().split())
|
||||
|
||||
def identifier(s):
|
||||
if keyword.iskeyword(s):
|
||||
die("%s is a Python keyword and can not be used as a name" % s)
|
||||
if not re.match('[A-Za-z_][A-Za-z0-9_]*', s):
|
||||
die("%s is not a valid Python identifier" % s)
|
||||
return s
|
||||
|
||||
def directory(p, create=False):
|
||||
expanded = os.path.abspath(
|
||||
os.path.expanduser(
|
||||
os.path.expandvars(p)))
|
||||
if create and not os.path.exists(expanded):
|
||||
os.makedirs(expanded)
|
||||
if create and not os.path.isdir(expanded):
|
||||
die("%s exists but is not a directory" % p)
|
||||
return expanded
|
||||
|
||||
def die(message, code=1):
|
||||
print >>sys.stderr, message
|
||||
sys.exit(code)
|
||||
|
||||
def warn(message):
|
||||
# ASK: shall we use logger ?
|
||||
print "WARNING: " + message
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{% if file_created -%}# -*- coding: utf-8 -*-{%- endif %}
|
||||
{% for module in modules if module -%}import {{ module }}
|
||||
{%- endfor %}
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "{{ module }}",
|
||||
|
||||
'summary': """
|
||||
Short (1 phrase/line) summary of the module's purpose, used as
|
||||
subtitle on modules listing or apps.openerp.com""",
|
||||
|
||||
'description': """
|
||||
Long description of module's purpose
|
||||
""",
|
||||
|
||||
'author': "Your Company",
|
||||
'website': "http://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check <odoo>/addons/base/module/module_data.xml of the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['base','website'],
|
||||
'data': [
|
||||
],
|
||||
|
||||
'demo': [
|
||||
],
|
||||
|
||||
'tests': [
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import http
|
||||
from openerp.addons.web.controllers import main
|
||||
|
||||
class {{ controller }}(main.Home):
|
||||
@http.route('/{{ module }}/{{ controller }}', auth='public')
|
||||
def index(self):
|
||||
return "Hello, world!"
|
||||
{% if has_model %}
|
||||
@http.route('/{{ module }}/{{ controller }}/<model("{{ module }}.{{ controller }}"):{{ controller }}>'], type='http', auth='public')
|
||||
def {{ controller }}(self, {{ controller }}, **kw):
|
||||
return "Hello, %r!" % {{ controller }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,6 @@
|
||||
{% if file_created -%}id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink{%- endif %}
|
||||
{% if model -%}
|
||||
access_{{ module }}_{{ model }},{{- '' -}}
|
||||
access_{{ module }}_{{ model }},{{- '' -}}
|
||||
model_{{ module }}_{{ model }},,1,0,0,0
|
||||
{%- endif %}
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp.osv import orm, fields
|
||||
|
||||
class {{ model }}(orm.Model):
|
||||
_name = "{{ module }}.{{ model }}"
|
||||
|
||||
_columns = {
|
||||
'name': fields.char(),
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<record id="{{ module }}.{{ model }}_demo" model="{{ model }}">
|
||||
<field name="name">My first record for model '{{ module }}.{{ model }}'</field>
|
||||
</record>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@charset "utf-8";
|
||||
.{{ module }} {
|
||||
background: white;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<template id="assets_frontend" inherit_id="website.assets_frontend" name="Theme Zen">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/css" href="/{{ module }}/static/src/css/{{ module }}_theme.css" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="website.page_{{ module }}" name="Page {{module}}">
|
||||
Page {{ module }}
|
||||
</template>
|
||||
|
||||
<template id="{{module}}_snippets" name="{{ module }} Snippet" inherit_id="website.snippets">
|
||||
<xpath expr="//div[@id='snippet_content']" position="inside">
|
||||
<div>
|
||||
<div class="oe_snippet_thumbnail">
|
||||
<div style="background: white;box-shadow:none;-webkit-box-shadow:none;" class="oe_snippet_thumbnail_img">
|
||||
<i class="fa fa-file-code-o fa-5x text-muted"></i>
|
||||
</div>
|
||||
<span class="oe_snippet_thumbnail_title">{{ module }} Snipper</span>
|
||||
</div>
|
||||
<section class="oe_snippet_body mb16 mt16">
|
||||
<h1>{{ module }} snippet</h1>
|
||||
</section>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@charset "utf-8";
|
||||
.openerp .oe_{{ module }} {
|
||||
background: white;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
openerp.{{ module }} = function (instance) {
|
||||
|
||||
var _t = instance.web._t,
|
||||
_lt = instance.web._lt;
|
||||
var QWeb = instance.web.qweb;
|
||||
|
||||
instance.web.WebClient.include({
|
||||
show_application: function () {
|
||||
console.log("Show application");
|
||||
return this._super.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<t t-name="{{ module }}.MyWidget">
|
||||
<div class="oe_{{ module }}">
|
||||
Hello World!
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data{% if noupdate %} noupdate="1"{% endif %}>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -169,6 +169,7 @@ def main(args):
|
||||
sys.exit(rc)
|
||||
|
||||
class Server(Command):
|
||||
"""Start the odoo server (default command)"""
|
||||
def run(self, args):
|
||||
main(args)
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
from . import Command
|
||||
from .server import main
|
||||
from openerp.modules.module import get_module_root, MANIFEST
|
||||
from openerp.service.db import _create_empty_database, DatabaseExists
|
||||
|
||||
|
||||
class Start(Command):
|
||||
"""Quick start the Odoo server for your project"""
|
||||
|
||||
def get_module_list(self, path):
|
||||
mods = glob.glob(os.path.join(path, '*/%s' % MANIFEST))
|
||||
return [mod.split(os.path.sep)[-2] for mod in mods]
|
||||
|
||||
|
||||
def run(self, cmdargs):
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="%s start" % sys.argv[0].split(os.path.sep)[-1],
|
||||
description=self.__doc__
|
||||
)
|
||||
parser.add_argument('--path', default=".",
|
||||
help="Directory where your project's modules are stored (will autodetect from current dir)")
|
||||
parser.add_argument("-d", "--database", dest="db_name", default=None,
|
||||
help="Specify the database name (default to project's directory name")
|
||||
|
||||
|
||||
args, unknown = parser.parse_known_args(args=cmdargs)
|
||||
|
||||
project_path = os.path.abspath(os.path.expanduser(os.path.expandvars(args.path)))
|
||||
module_root = get_module_root(project_path)
|
||||
db_name = None
|
||||
if module_root:
|
||||
# started in a module so we choose this module name for database
|
||||
db_name = project_path.split(os.path.sep)[-1]
|
||||
# go to the parent's directory of the module root
|
||||
project_path = os.path.abspath(os.path.join(project_path, os.pardir))
|
||||
|
||||
# check if one of the subfolders has at least one module
|
||||
mods = self.get_module_list(project_path)
|
||||
if mods and '--addons-path' not in cmdargs:
|
||||
cmdargs.append('--addons-path=%s' % project_path)
|
||||
|
||||
if not args.db_name:
|
||||
args.db_name = db_name or project_path.split(os.path.sep)[-1]
|
||||
# TODO: forbid some database names ? eg template1, ...
|
||||
try:
|
||||
_create_empty_database(args.db_name)
|
||||
except DatabaseExists, e:
|
||||
pass
|
||||
except Exception, e:
|
||||
die("Could not create database `%s`. (%s)" % (args.db_name, e))
|
||||
cmdargs.extend(('-d', args.db_name))
|
||||
|
||||
if '--db-filter' not in cmdargs:
|
||||
cmdargs.append('--db-filter=^%s$' % args.db_name)
|
||||
|
||||
main(cmdargs)
|
||||
|
||||
def die(message, code=1):
|
||||
print >>sys.stderr, message
|
||||
sys.exit(code)
|
||||
+44
-10
@@ -37,10 +37,13 @@ import openerp.tools as tools
|
||||
import openerp.release as release
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
|
||||
MANIFEST = '__openerp__.py'
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
# addons path as a list
|
||||
ad_paths = []
|
||||
ad_paths = [tools.config.addons_data_dir]
|
||||
hooked = False
|
||||
|
||||
# Modules already loaded
|
||||
loaded = []
|
||||
@@ -83,17 +86,21 @@ def initialize_sys_path():
|
||||
PYTHONPATH.
|
||||
"""
|
||||
global ad_paths
|
||||
if ad_paths:
|
||||
return
|
||||
global hooked
|
||||
|
||||
ad_paths = [tools.config.addons_data_dir]
|
||||
ad_paths += map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
for ad in tools.config['addons_path'].split(','):
|
||||
ad = os.path.abspath(tools.ustr(ad.strip()))
|
||||
if ad not in ad_paths:
|
||||
ad_paths.append(ad)
|
||||
|
||||
# add base module path
|
||||
base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons'))
|
||||
ad_paths += [base_path]
|
||||
if base_path not in ad_paths:
|
||||
ad_paths.append(base_path)
|
||||
|
||||
sys.meta_path.append(AddonsImportHook())
|
||||
if not hooked:
|
||||
sys.meta_path.append(AddonsImportHook())
|
||||
hooked = True
|
||||
|
||||
def get_module_path(module, downloaded=False, display_warning=True):
|
||||
"""Return the path of the given module.
|
||||
@@ -169,6 +176,33 @@ def get_module_icon(module):
|
||||
return ('/' + module + '/') + '/'.join(iconpath)
|
||||
return '/base/' + '/'.join(iconpath)
|
||||
|
||||
def get_module_root(path):
|
||||
"""
|
||||
Get closest module's root begining from path
|
||||
|
||||
# Given:
|
||||
# /foo/bar/module_dir/static/src/...
|
||||
|
||||
get_module_root('/foo/bar/module_dir/static/')
|
||||
# returns '/foo/bar/module_dir'
|
||||
|
||||
get_module_root('/foo/bar/module_dir/')
|
||||
# returns '/foo/bar/module_dir'
|
||||
|
||||
get_module_root('/foo/bar')
|
||||
# returns None
|
||||
|
||||
@param path: Path from which the lookup should start
|
||||
|
||||
@return: Module root path or None if not found
|
||||
"""
|
||||
while not os.path.exists(os.path.join(path, MANIFEST)):
|
||||
new_path = os.path.abspath(os.path.join(path, os.pardir))
|
||||
if path == new_path:
|
||||
return None
|
||||
path = new_path
|
||||
return path
|
||||
|
||||
def load_information_from_description_file(module, mod_path=None):
|
||||
"""
|
||||
:param module: The name of the module (sale, purchase, ...)
|
||||
@@ -177,7 +211,7 @@ def load_information_from_description_file(module, mod_path=None):
|
||||
|
||||
if not mod_path:
|
||||
mod_path = get_module_path(module)
|
||||
terp_file = mod_path and opj(mod_path, '__openerp__.py') or False
|
||||
terp_file = mod_path and opj(mod_path, MANIFEST) or False
|
||||
if terp_file:
|
||||
info = {}
|
||||
if os.path.isfile(terp_file):
|
||||
@@ -219,7 +253,7 @@ def load_information_from_description_file(module, mod_path=None):
|
||||
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
# for 6.0
|
||||
_logger.debug('module %s: no __openerp__.py file found.', module)
|
||||
_logger.debug('module %s: no %s file found.', module, MANIFEST)
|
||||
return {}
|
||||
|
||||
def init_module_models(cr, module_name, obj_list):
|
||||
@@ -291,7 +325,7 @@ def get_modules():
|
||||
return name
|
||||
|
||||
def is_really_module(name):
|
||||
manifest_name = opj(dir, name, '__openerp__.py')
|
||||
manifest_name = opj(dir, name, MANIFEST)
|
||||
zipfile_name = opj(dir, name)
|
||||
return os.path.isfile(manifest_name)
|
||||
return map(clean, filter(is_really_module, os.listdir(dir)))
|
||||
|
||||
@@ -13,6 +13,7 @@ import psycopg2
|
||||
|
||||
import openerp
|
||||
from openerp import SUPERUSER_ID
|
||||
from openerp.exceptions import Warning
|
||||
import openerp.release
|
||||
import openerp.sql_db
|
||||
import openerp.tools
|
||||
@@ -25,6 +26,9 @@ self_actions = {}
|
||||
self_id = 0
|
||||
self_id_protect = threading.Semaphore()
|
||||
|
||||
class DatabaseExists(Warning):
|
||||
pass
|
||||
|
||||
# This should be moved to openerp.modules.db, along side initialize().
|
||||
def _initialize_db(id, db_name, demo, lang, user_password):
|
||||
try:
|
||||
@@ -81,7 +85,7 @@ def _create_empty_database(name):
|
||||
cr.execute("SELECT datname FROM pg_database WHERE datname = %s",
|
||||
(name,))
|
||||
if cr.fetchall():
|
||||
raise openerp.exceptions.Warning("database %r already exists!" % (name,))
|
||||
raise DatabaseExists("database %r already exists!" % (name,))
|
||||
else:
|
||||
cr.autocommit(True) # avoid transaction block
|
||||
cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))
|
||||
|
||||
Reference in New Issue
Block a user