Skip to content

Commit

Permalink
feat(cli): add ability to run custom cli commands
Browse files Browse the repository at this point in the history
  • Loading branch information
mda-hdm committed Nov 9, 2022
1 parent 5b5ba85 commit db45339
Show file tree
Hide file tree
Showing 13 changed files with 217 additions and 58 deletions.
3 changes: 2 additions & 1 deletion cli/__init__.py
@@ -1,3 +1,4 @@
from cli.command import *
from cli.app import *
from cli.config import *
from cli.environment import *
from cli.log import *
13 changes: 13 additions & 0 deletions cli/app.py
@@ -0,0 +1,13 @@
from pyrevit import HOST_APP
from cli.config import Config


class App():

@staticmethod
def open(detach=True):
config = Config(detach)

return HOST_APP.uiapp.Application.OpenDocumentFile(
config.modelPath, config.openOptions
)
34 changes: 24 additions & 10 deletions cli/command.py
Expand Up @@ -2,13 +2,15 @@
import sys
import json
import os
import glob
from os.path import dirname, join, abspath, isfile, exists


class Command:

def __init__(self, command):
from cli import setEnv, CliLog
from environment import setEnv
from log import CliLog
configFile = self.getConfigFile()
config = self.getConfig(configFile)
try:
Expand All @@ -19,15 +21,23 @@ def __init__(self, command):
self.target = os.path.join(os.path.dirname(__file__), 'target.rvt')
self.revitVersion = revitVersion
self.pyRevitBin = self.getPyRevitBin()
self.task = self.getTask(command)
self.env = setEnv(configFile)
task = self.getTask(command)
self.env = setEnv(configFile, task)
self.log = CliLog(self.env.log)
self.log.write('\nRunning: \n{}\n'.format(self.task))
self.log.write('\nRunning: \n{}\n'.format(task))

def getTask(self, command):
task = join(dirname(__file__), 'commands', '{}.py'.format(command))
if not exists(task):
print('Command not found!')
parent = os.path.dirname
root = parent(parent(parent(__file__)))
for n in range(1, 7):
depth = n * '*/'
path = os.path.join(root, depth, '{}.cli.py'.format(command))
tasks = glob.glob(path)
if tasks:
return tasks[0]
print('Command "{}" not found!'.format(command))
sys.exit()
return task

Expand All @@ -42,10 +52,13 @@ def getConfig(self, configFile):
return config

def getConfigFile(self):
configFile = abspath(join(os.getcwd(), sys.argv[2]))
if not isfile(configFile):
configFile = sys.argv[2]
return configFile
try:
configFile = abspath(join(os.getcwd(), sys.argv[2]))
if not isfile(configFile):
configFile = sys.argv[2]
return configFile
except:
return None

def getPyRevitBin(self):
pyRevitPath = abspath(join(dirname(__file__), '../../../'))
Expand All @@ -56,9 +69,10 @@ def getPyRevitBin(self):

def run(self):
bufferFile = '{}.buffer'.format(self.env.log)
execWrapper = os.path.join(os.path.dirname(__file__), 'exec.py')
code = os.system(
'{} run {} {} {} --purge > {}'.format(
self.pyRevitBin, self.task, self.target, self.revitVersion, bufferFile
self.pyRevitBin, execWrapper, self.target, self.revitVersion, bufferFile
)
)
buffer = getBuffer(bufferFile)
Expand Down
13 changes: 2 additions & 11 deletions cli/commands/analyze.py
@@ -1,19 +1,10 @@
import sys
from os.path import dirname
from pyrevit import HOST_APP

sys.path.append(dirname(dirname(dirname(__file__))))

import revitron
from cli import getLogFromEnv
from cli.config import Config
from cli import App, Config, getLogFromEnv

config = Config()
cliLog = getLogFromEnv()

revitron.DOC = HOST_APP.uiapp.Application.OpenDocumentFile(
config.modelPath, config.openOptions
)
revitron.DOC = App.open(True)

analyzer = revitron.ModelAnalyzer(config.file, cliLog)
analyzer.snapshot()
Expand Down
29 changes: 4 additions & 25 deletions cli/commands/compact.py
@@ -1,34 +1,13 @@
import sys
import os
from os.path import dirname
from pyrevit import HOST_APP

sys.path.append(dirname(dirname(dirname(__file__))))

import revitron
from cli import getEnv, getLogFromEnv
from cli.config import Config
from cli import App, getLogFromEnv

env = getEnv()
log = getLogFromEnv().write
config = Config(detach=False)

revitron.DOC = HOST_APP.uiapp.Application.OpenDocumentFile(
config.modelPath, config.openOptions
)
revitron.DOC = App.open(False)

try:
syncOptions = revitron.DB.SynchronizeWithCentralOptions()
syncOptions.Compact = True
syncOptions.Comment = 'Compact model'
syncOptions.SaveLocalAfter = False
syncOptions.SaveLocalBefore = False
revitron.DOC.SynchronizeWithCentral(
revitron.DB.TransactWithCentralOptions(), syncOptions
)
if revitron.Document().synchronize(compact=True, comment='Compact model'):
log('Synching finished successfully')
except Exception as e:
else:
log('Synching failed')
log(str(e))

revitron.DOC.Close(False)
3 changes: 3 additions & 0 deletions cli/config.py
Expand Up @@ -43,6 +43,9 @@ def __init__(self, detach=True):
print('Invalid model configuration')
sys.exit(1)

def get(self):
return self._config

@property
def config(self):
return self._config
Expand Down
12 changes: 8 additions & 4 deletions cli/environment.py
Expand Up @@ -3,29 +3,33 @@
import tempfile


def setEnv(configFile):
def setEnv(configFile, task):
processId = uuid.uuid4().hex
log = r'{}\revitron.cli.{}.log'.format(tempfile.gettempdir(), processId)
os.environ['REVITRON_CLI_PROCESS'] = processId
os.environ['REVITRON_CLI_CONFIG'] = configFile
os.environ['REVITRON_CLI_LOG'] = log
return Environment(configFile, processId, log)
os.environ['REVITRON_CLI_TASK'] = task
return Environment(configFile, processId, log, task)


def getEnv():
processId = os.getenv('REVITRON_CLI_PROCESS')
configFile = os.getenv('REVITRON_CLI_CONFIG')
log = os.getenv('REVITRON_CLI_LOG')
return Environment(processId, configFile, log)
task = os.getenv('REVITRON_CLI_TASK')
return Environment(processId, configFile, log, task)


class Environment():

configFile = None
processId = None
log = None
task = None

def __init__(self, configFile, processId, log):
def __init__(self, configFile, processId, log, task):
self.processId = processId
self.configFile = configFile
self.log = log
self.task = task
13 changes: 13 additions & 0 deletions cli/exec.py
@@ -0,0 +1,13 @@
import sys
from os.path import dirname

sys.path.append(dirname(dirname(__file__)))

from cli import getEnv, getLogFromEnv

env = getEnv()
log = getLogFromEnv().write

log('Running task {}'.format(env.task))

execfile(env.task)
6 changes: 1 addition & 5 deletions cli/revitron.bat
Expand Up @@ -11,9 +11,5 @@ set args=%*
if "%~1" == "" (
echo No command specified!
) else (
if exist %command% (
python %run% %args%
) else (
echo Command "%1" not found!
)
python %run% %args%
)
4 changes: 2 additions & 2 deletions cli/run.py
Expand Up @@ -3,7 +3,7 @@

sys.path.append(dirname(dirname(__file__)))

import cli
from command import Command

cmd = cli.Command(sys.argv[1])
cmd = Command(sys.argv[1])
cmd.run()
119 changes: 119 additions & 0 deletions docs/source/cli.rst
@@ -0,0 +1,119 @@
Command Line
============

Revitron ships its own little wrapper for the *pyRevit* CLI that includes some additional features such as the
ability to handle cloud models and to pass configuration files.

.. code-block::
revitron [command] "path\to\config.json"
Standard Commands
-----------------

Revitron ships with two default commands.

=============== =====================================================================================
Command Description
=============== =====================================================================================
``analyze`` Creates analytical snapshots from models. More `here <analyze.html>`_.
``compact`` Compacts a model. The configuration JSON file only has to include `model` and `revit`
fields.
=============== =====================================================================================

Compact Example
~~~~~~~~~~~~~~~

The configuration for the ``compact`` command operating on cloud models looks as follows:

.. code-block:: json
{
"model": {
"type": "cloud",
"projectGUID":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"modelGUID":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"region": "EMEA"
},
"revit": "2022"
}
Alternatively local models have a slightly different configuration:

.. code-block:: json
{
"model": {
"type": "local",
"path": "C:\\path\\to\\model.rvt"
},
"revit": "2022"
}
The compacting can be started with the following command. Manually or as a command used in a task scheduler.

.. code-block::
revitron compact "path\to\config.json"
Custom Commands
---------------

Revitron supports custom commands as well to be used with the CLI. In order to be used as a command,
a command has to meet the following requirements for its location and its content.

Location
~~~~~~~~

In order to be picked up as a valid command, a custom command file must have the ``.cli.py`` extension
and be located somewhere subdirectory tree of the main extension directory of pyRevit. It is strongly recommended
to ship commands as Revitron packages that will be installed automatically in the correct location by the Revitron
package manager.

Anatomy
~~~~~~~

You can use one of the following example commands below in order to quickly get a custom command up and running:

.. code-block:: python
import revitron
from cli import App, Config, getLogFromEnv
# Get the config dictionary from the JSON file.
config = Config().get()
# Get a log handle to use for logging.
log = getLogFromEnv().write
log('Hello')
# Open the model that is configured in the JSON file.
# Use App.open(True) in order to open and detach the file.
revitron.DOC = App.open(True)
# Implement your functionality here.
# Use the config dict in order to access your configuration stored
# in the JSON file that is passed as CLI argument.
revitron.DOC.Close(False)
In order to sync changes that have been applied by your command, you can use the following boiler plate
that includes synching as well.

.. code-block:: python
import revitron
from cli import App, Config, getLogFromEnv
config = Config().get()
log = getLogFromEnv().write
revitron.DOC = App.open(False)
# Implement your functionality here before synching ...
if revitron.Document().synchronize(compact=True, comment='Some comment'):
log('Synching finished successfully')
else:
log('Synching failed')
revitron.DOC.Close(False)
1 change: 1 addition & 0 deletions docs/source/index.rst
Expand Up @@ -22,6 +22,7 @@ Revitron

get-started
revitron
cli
analyze
cheat-sheet
genindex
Expand Down

0 comments on commit db45339

Please sign in to comment.