Skip to content

Commit fb0e388

Browse files
authored
Merge pull request #111 from mikofski/quickstart
quickstart and file-structure changes
2 parents 2174cba + 3e5c33b commit fb0e388

33 files changed

Lines changed: 147 additions & 108 deletions

carousel-quickstart.py

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
"""
77

88
import argparse
9-
import json
109
import logging
1110
import os
1211
import re
1312
from carousel import __version__
1413
import sys
14+
from dulwich import porcelain, config as gitconfig
1515

1616
# set up logging
1717
logging.basicConfig(level=logging.INFO)
@@ -20,31 +20,50 @@
2020
# constants
2121
CWD = os.getcwd()
2222
OKAY = r'[\w\d]'
23-
LAYERS = ['simulations', 'outputs', 'calculations', 'formulas', 'data']
24-
DFLT_MODEL = dict.fromkeys(LAYERS)
25-
DFLT = 'my_model.json'
26-
MODELS = 'models'
27-
PATHS = [MODELS] + LAYERS
2823
INIT_CONTENT = """
2924
import os
3025
3126
__version__ = '0.1'
32-
__author__ = 'your name'
33-
__email__ = 'your.name@company.com'
27+
__author__ = '%s'
28+
__email__ = '%s'
3429
35-
PKG_PATH = os.path.abspath(os.path.dirname(__file__))
36-
PROJ_PATH = os.path.dirname(PKG_PATH)
30+
PROJ_PATH = os.path.abspath(os.path.dirname(__file__))
31+
# TODO: change PROJ_PATH to MODELPATH everywhere
3732
"""
3833
DESCRIPTION = """
39-
Creates a Carousel project. See documentation for more information.
34+
Create a Carousel project file structure. See documentation for more detail.
4035
"""
36+
GIT_GLOBAL = os.path.expanduser(os.path.join('~', '.gitconfig'))
37+
UNKNOWN = 'unknown'
38+
USERNAME = (os.environ.get('USERNAME', UNKNOWN) if sys.platform == 'win32'
39+
else os.environ.get('USER', UNKNOWN)) # default username
40+
HOSTNAME = os.uname()[1]
41+
USEREMAIL = '%s@%s' % (USERNAME, HOSTNAME)
42+
43+
44+
def get_gitconfig(git_path, section, name):
45+
try:
46+
return gitconfig.ConfigFile.from_path(git_path).get(section, name)
47+
except (IOError, KeyError):
48+
return None
49+
4150

4251
# run from command line
4352
if __name__ == '__main__':
4453
parser = argparse.ArgumentParser(description=DESCRIPTION)
4554
parser.add_argument('project', help='name of Carousel project to create')
4655
parser.add_argument('--version', action='version',
4756
version=('%(prog)s' + ' %s' % __version__))
57+
parser.add_argument('-g', '--git', action='store_true',
58+
help='initialize Git repository for project')
59+
parser.add_argument(
60+
'-f', '--folders', action='append', default=['data'],
61+
help=('create layer folders in project package, list each folder'
62+
' separately, can be used more than once, default is "data"'))
63+
parser.add_argument('--author', help="Project author's full name",
64+
default=USERNAME)
65+
parser.add_argument('--email', help="Project author's email",
66+
default=USEREMAIL)
4867
args = parser.parse_args()
4968
# exit with error if no project name specified
5069
if len(sys.argv) < 2:
@@ -63,16 +82,47 @@
6382
sys.exit('The path, %s, already exists.' % project_name)
6483
os.mkdir(project_name) # make project folder
6584
LOGGER.info('Project created at path, %s.', project_name)
66-
# make project file structure
67-
for p in PATHS + [project_pkg]:
68-
os.mkdir(os.path.join(project_name, p))
69-
LOGGER.debug('created folder: %s', p)
7085
# make project package
86+
os.mkdir(os.path.join(project_name, project_pkg)) # make project package
87+
LOGGER.info('Project package, %s, created.', project_pkg)
7188
pkg_init = os.path.join(project_name, project_pkg, '__init__.py')
89+
# try to get user info from git config
90+
username = useremail = None
91+
if args.author == USERNAME:
92+
username = get_gitconfig(GIT_GLOBAL, 'user', 'name')
93+
if username:
94+
args.author = username
95+
if args.email == USEREMAIL:
96+
useremail = get_gitconfig(GIT_GLOBAL, 'user', 'email')
97+
if useremail:
98+
args.email = useremail
7299
with open(pkg_init, 'w') as init:
73100
init.write('"""\nThis is the %s package.\n"""\n' % project_pkg)
74-
init.write(INIT_CONTENT)
75-
# make default model
76-
with open(os.path.join(project_name, MODELS, DFLT), 'w') as dflt:
77-
json.dump(DFLT_MODEL, dflt, indent=2)
78-
LOGGER.debug('created %s in %s/%s', DFLT, project_name, MODELS)
101+
init.write(INIT_CONTENT % (args.author, args.email))
102+
LOGGER.info('Package file created: %s.', pkg_init)
103+
if args.git:
104+
repo = porcelain.init(project_name)
105+
LOGGER.info('Project Git repository initialized: %s.', repo)
106+
porcelain.add(repo, os.path.relpath(pkg_init, project_name))
107+
LOGGER.info('Project package added to index')
108+
# if global git config username and email not set, use args if specified
109+
kwargs = {}
110+
if not username:
111+
username = args.author
112+
if not useremail:
113+
useremail = args.email
114+
if not os.path.exists(GIT_GLOBAL):
115+
conf = repo.get_config()
116+
conf.set('user', 'name', username)
117+
conf.set('user', 'email', useremail)
118+
conf.write_to_path()
119+
else:
120+
kwargs['author'] = '%s <%s>' % (username, useremail)
121+
sha1 = porcelain.commit(repo, message='initial dump', **kwargs)
122+
LOGGER.info('Project initial commit: %s.', sha1)
123+
porcelain.log(repo, outstream=logging.root.handlers[0].stream)
124+
# make project layer folders
125+
for fp in args.folders:
126+
os.mkdir(os.path.join(project_name, project_pkg, fp))
127+
LOGGER.info('created folder: %s', fp)
128+
LOGGER.info('Carousel quickstart completed.')

carousel/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@ def get_current_version(*args, **kwargs):
4545
__email__ = u'mark.mikofski@sunpowercorp.com'
4646
__url__ = u'https://github.com/SunPower/Carousel'
4747
__version__ = VERSION
48-
__release__ = u'Caramel Corn'
48+
__release__ = u'Carousel'

carousel/docs/announcements/caramel-corn.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.. _caramel_corn:
22

3-
Caramel Corn (v0.3.1)
4-
=====================
3+
Caramel Corn CONSTANTS (v0.3.1)
4+
===============================
55
This version is a *major* release with several new features which will break
66
previous Carousel models. In particular, the following new features have been
77
introduced starting with v0.3:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.. _carousel:
2+
3+
Carousel (v0.3.2)
4+
=================
5+
6+
Quickstart
7+
----------
8+
The ``carousel-quickstart`` now only creates a *data* folder, and it puts the
9+
*data* folder inside the project package. It doesn't create any other folders
10+
explicitly, and it doesn't create a sample model parameter JSON file. There is
11+
an option to create additional *layer* folders inside the project package.
12+
13+
The ``carousel-quickstart`` also now provides an option to initialize the
14+
project as a Git repository. If found it will set the ``__author`` and
15+
``__email__`` fields in the project package ``__init__.py`` file from either
16+
the Git global configuration or from system environmental variables.
17+
18+
Setup Dulwich Requirement
19+
-------------------------
20+
Also, even though `Dulwich <https://www.dulwich.io/>`_ is now a requirement
21+
in ``setup.py``, the ``carousel/__init__.py`` file now has a *try/except* block
22+
around the ``dulwich`` import to make sure that installation goes smoothly.

carousel/docs/api/scripts.rst

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,12 @@ Creates a basic file structure to start a Carousel project. ::
1313
Project
1414
|
1515
+-+- project
16-
| |
17-
| +- __init__.py
18-
|
19-
+-+- models
20-
| |
21-
| +- my_model.json
22-
|
23-
+- simulation
24-
|
25-
+- outputs
26-
|
27-
+- calculations
28-
|
29-
+- formulas
30-
|
31-
+- data
16+
|
17+
+- __init__.py
18+
|
19+
+- data
3220

33-
The contents of ``project/__init__.py`` is the following::
21+
The contents of ``Project/project/__init__.py`` is the following::
3422

3523
"""
3624
This is the Project package.
@@ -42,18 +30,7 @@ The contents of ``project/__init__.py`` is the following::
4230
__author__ = 'your name'
4331
__email__ = 'your.name@company.com'
4432

45-
PKG_PATH = os.path.abspath(os.path.dirname(__file__))
46-
PROJ_PATH = os.path.dirname(PKG_PATH)
47-
48-
An empty model is contained in ``models/my_model.json``. ::
49-
50-
{
51-
"outputs": null,
52-
"formulas": null,
53-
"data": null,
54-
"calculations": null,
55-
"simulations": null
56-
}
33+
PROJ_PATH = os.path.abspath(os.path.dirname(__file__))
5734

5835
Call ``carousel-quickstart.py`` from the command line to see usage, help and
5936
version information. Some more detail is also given in :ref:`getting-started`.

carousel/docs/getting_started.rst

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,37 +15,22 @@ project. ::
1515

1616
$ carousel-quickstart.py MyCarouselProject.
1717

18-
This creates a new folder for ``MyCarouselProject`` with seven
19-
sub-folders. ::
18+
This creates a new folder for ``MyCarouselProject``, a Python package with the
19+
same name, and a *data* folder. ::
2020

2121
MyCarouselProject
2222
|
2323
+-+- mycarouselproject
24-
| |
25-
| +- __init__.py
26-
|
27-
+-+- models
28-
| |
29-
| +- my_model.json
30-
|
31-
+- simulation
32-
|
33-
+- outputs
34-
|
35-
+- calculations
36-
|
37-
+- formulas
38-
|
39-
+- data
24+
|
25+
+- __init__.py
26+
|
27+
+- data
4028

4129
The quickstart script adds the constant ``mycarouselproject.PROJ_PATH`` that
42-
refers to the project path ``MyCarouselProject/``. This path is useful, as we'll
43-
see in the :ref:`next tutorial <tutorial-1>`, and should be imported into the
44-
project package modules. The script also adds version, author and email
45-
information that you can complete if you want. Finally a sample model parameter
46-
file is created in ``MyCarouselProject/models/my_model.json`` - feel free to
47-
delete this if not used. The sample parameter file is used in the legacy style
48-
described in the :ref:`parameter-styles` section below.
30+
refers to the project path ``MyCarouselProject/mycarouselproject``. This path is
31+
useful, as we'll see in the :ref:`next tutorial <tutorial-1>`, and should be
32+
imported into the project package modules. The script also adds version, author
33+
and email information that you can complete if you want.
4934

5035
.. _tutorials:
5136

carousel/docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Announcements
5656
.. toctree::
5757
:maxdepth: 2
5858

59+
announcements/carousel
5960
announcements/caramel-corn
6061
announcements/brown-bicycle-bears
6162

carousel/docs/tutorials/tutorial_3.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ Carousel currently supports formulas that are written in Python as function
1313
definitions or strings that can be evaluated by the Python
1414
`numexpr <https://pypi.python.org/pypi/numexpr>`_ package. For the PV system
1515
power example, we will use formulas written as Python functions. To add the
16-
formulas we need for this example create a Python module in our project formulas
17-
folder called ``utils.py`` and copy the following code. ::
16+
formulas we need for this example create a Python package in our project package
17+
called ``formulas``, don't forget to add ``__init__.py`` to make it a package,
18+
and copy the following code into a Python module called ``utils.py`` inside the
19+
formulas folder, *i.e.*: ``PVPower/pvpower/formulas/utils.py``. ::
1820

1921
# -*- coding: utf-8 -*-
2022

@@ -131,7 +133,7 @@ that the formulas have the same names as the Python functions. ::
131133

132134
class Meta:
133135
module = ".utils"
134-
package = "formulas"
136+
package = "pvpower.formulas"
135137

136138

137139
Formula Attributes
@@ -183,22 +185,22 @@ formulas. Otherwise specify the path to the module or package as well. ::
183185
class Meta:
184186
formula_importer = PyModuleImporter
185187
module = '.utils' # relative module name
186-
package = 'formulas' # module package
187-
path = 'examples/PVPower' # path to package
188+
package = 'pvpower.formulas' # module package
189+
path = 'examples/PVPower' # path to package if not on PYTHONPATH
188190

189191

190192
class Irradiance(Formula):
191193
class Meta:
192194
formula_importer = PyModuleImporter
193195
module = 'irradiance' # module name
194196
package = None # no package
195-
path = 'examples/PVPower/formulas' # path to module
197+
path = 'examples/PVPower/pvpower/formulas' # path to module
196198

197199

198200
class Performance(formulas.Formula):
199201
class Meta:
200202
formula_importer = PyModuleImporter
201-
module = 'formulas.performance' # full module name including package
203+
module = 'pvpower.formulas.performance' # module name with package
202204
package = None
203205
path = 'examples/PVPower' # path to package
204206

carousel/docs/tutorials/tutorial_5.rst

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,20 +182,22 @@ for ``PVPowerData``, like Tuscon data, then we could declare it in the model. ::
182182

183183
data = [(PVPowerData, {'filename': 'Tuscon.json'})]
184184

185-
186185
Model Path
187186
~~~~~~~~~~
188-
The ``modelpath`` is a legacy attribute that is used with the folder structure
189-
that is created by ``carousel-quickstart``. It iss confusing since it's called
190-
*model path* not *project path*, but it refers to the project path that contains
191-
the model package and the layer folders created by ``carousel-quickstart``. Ha!
192-
Ha! Ha! The value of ``modelpath`` sets the relative path for to look for files
193-
loaded by :class:`~carousel.core.layers.Layer`. For new parameter style models
194-
this is actually only used by the data layer, which looks in project data folder
195-
created by ``carousel-quickstart``, unless ``path`` is passed as a ``sources``
196-
argument, and then it looks in ``os.path.join(modelpath, 'data', path)``. So,
197-
still confused? Just set ``modelpath`` to the name of the folder containing the
198-
data folder and hopefully you'll be okay. Sorry.
187+
The ``modelpath`` is an attribute that is used with the folder structure that is
188+
created by ``carousel-quickstart``. You should set this to the ``PROJ_PATH``
189+
module constant created in your project package by ``carousel-quickstart``.
190+
It's confusing since the attribute is *model path* not *project path*, but
191+
refers to the path that contains the layer folders created by
192+
``carousel-quickstart``. The value of ``modelpath`` is prepended to the name of
193+
the layer to set the relative path to look for files loaded by each
194+
:class:`~carousel.core.layers.Layer`. For new parameter style models this is
195+
actually only used by the data layer, since the model looks in the project
196+
*data* folder created by ``carousel-quickstart``, unless ``path`` is passed as a
197+
``sources`` argument, then it appends the value of ``path`` to the relative
198+
path. So, still confused? Just set ``modelpath`` to the name of the folder
199+
containing the *data* folder, which should be ``PROJ_PATH`` if you used
200+
``carousel-quickstart``, and you'll be okay.
199201

200202
Running Model Simulation
201203
------------------------

carousel/tests/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
PROJECT = 'PVPower'
1313
MODEL = 'sandia_performance_model'
1414
TESTS_DIR = os.path.abspath(os.path.dirname(__file__))
15-
PROJ_PATH = os.path.abspath(os.path.join(
15+
PKG_PATH = os.path.abspath(os.path.join(
1616
TESTS_DIR, '..', '..', 'examples', PROJECT
1717
))
18+
PROJ_PATH = os.path.join(PKG_PATH, PROJECT.lower())
1819

19-
sys.path.append(PROJ_PATH)
20+
sys.path.append(PKG_PATH)
2021
sandia_performance_model = importlib.import_module(
2122
'.%s' % MODEL, PROJECT.lower()
2223
)

0 commit comments

Comments
 (0)