Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

New Implementation #63

Merged
merged 31 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
UX Rendering
Add filtering to step change management in the UI to prevent
out-of-order changes from being applied and only rendering
changes when they are significant (body has changed from its
previous state).

Also added unit testing for recently added CLI commands.
  • Loading branch information
sernst committed Nov 21, 2019
commit b5cc76ea0662ebedfb71be30de755575d55ee5c1
42 changes: 39 additions & 3 deletions cauldron-app/src/notebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ function scrollToStep(stepName, position) {
cauldron.scrollToAnchor(targets[0].name, position || defaultPosition);
}

/**
* We're only interested in step changes that are actually changes and that
* occur more recently than the previous one. This filters down the source
* changes into only significant and meaningful ones.
*
* @param changes
* The source changes to filter down to meaningful ones.
* @returns {*}
* An array containing the significant changes.
*/
function filterStepChanges(changes) {
const { previousStepChanges } = store.getters;

return changes.filter((c) => {
const previous = previousStepChanges[c.name] || { step: { body: '' } };

return (
c.step.body !== previous.step.body
// Older Cauldron versions did not all have timestamps in changes, so we
// use defaults here just in case.
&& (c.timestamp || 1) > (previous.timestamp || 0)
);
});
}

/**
*
* @param renames
Expand All @@ -85,9 +110,10 @@ function scrollToStep(stepName, position) {
* @returns {Promise<void>|Promise<T>}
*/
function applyStepModifications(renames, changes, stepName) {
const newChanges = filterStepChanges(changes || []);
const isUnmodified = (
Object.keys(renames || {}).length === 0
&& (changes || []).length === 0
&& (newChanges || []).length === 0
);

if (isUnmodified) {
Expand All @@ -101,8 +127,18 @@ function applyStepModifications(renames, changes, stepName) {

return cauldron.processStepRenames(renames || {})
.then(() => {
cauldron.processStepUpdates(changes || []);
return utils.thenWait(500);
cauldron.processStepUpdates(newChanges);

// Update changes in the store for future reference to prevent insignificant
// changes to steps where the body does not change from updating the dom and
// wasting rendering resources.
const { previousStepChanges } = store.getters;
const updatedChanges = newChanges
.reduce((all, c) => Object.assign(all, { [c.name]: c }), {});
const combinedChanges = Object.assign({}, previousStepChanges, updatedChanges);
store.commit('previousStepChanges', combinedChanges);

return utils.thenWait(300);
})
.then(() => {
if (!stepName && !store.getters.followSteps) {
Expand Down
5 changes: 5 additions & 0 deletions cauldron-app/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default new Vuex.Store({
status: createEmptyStatus(),
loading: [],
isNotebookLoading: false,
previousStepChanges: {},
},
mutations: {
followSteps(state, value) {
Expand Down Expand Up @@ -59,6 +60,9 @@ export default new Vuex.Store({
isNotebookLoading(state, value) {
state.isNotebookLoading = value || false;
},
previousStepChanges(state, value) {
state.previousStepChanges = value || {};
},
},
getters: {
followSteps: state => state.followSteps,
Expand All @@ -74,5 +78,6 @@ export default new Vuex.Store({
view: state => ((state.status || {}).data || {}).view || null,
loading: state => state.loading,
isNotebookLoading: state => state.isNotebookLoading,
previousStepChanges: state => state.previousStepChanges,
},
});
1 change: 1 addition & 0 deletions cauldron-app/src/views/project/Project.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ function closeProject() {
return http.execute('close')
.then(() => {
this.$store.commit('project', null);
this.$store.commit('previousStepChanges', {});
http.markStatusDirty();
});
}
Expand Down
2 changes: 1 addition & 1 deletion cauldron-web/src/project.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import $ from 'jquery';
import d3 from 'd3/dist/d3.min';
import * as d3 from 'd3';
import Handsontable from 'handsontable';
import katex from 'katex';
import jstree from 'jstree';
Expand Down
8 changes: 1 addition & 7 deletions cauldron/cli/commands/listing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,7 @@ def execute(


def autocomplete(segment: str, line: str, parts: typing.List[str]):
"""

:param segment:
:param line:
:param parts:
:return:
"""
"""..."""
if len(parts) < 2:
return autocompletion.matches(
segment,
Expand Down
4 changes: 2 additions & 2 deletions cauldron/cli/commands/listing/_remover.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def execute_removal(
if identifier is None:
return (
context.response
.notify(
.fail(
kind='ABORTED',
code='NO_IDENTIFIER_SET',
message='No project identifier specified for removal.'
Expand All @@ -61,7 +61,7 @@ def execute_removal(
if not match:
return (
context.response
.notify(
.fail(
kind='ABORTED',
code='NO_MATCH_FOUND',
message='No matching project found to remove.'
Expand Down
24 changes: 2 additions & 22 deletions cauldron/cli/commands/listing/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,8 @@
from cauldron.session.projects import specio


def print_path_group(header, paths):
if not paths:
return

environ.log_header(header, level=6, indent_by=2)
entries = []
for p in paths:
parts = p.rstrip(os.sep).split(os.sep)
name = parts[-1]
if name.startswith('@'):
name = name.split(':', 1)[-1]
entries.append(
'* "{name}" -> {path}'.format(name=name, path=p)
)

environ.log('\n'.join(entries), indent_by=4)


def get_known_root_paths():
"""..."""
aliases = environ.configs.fetch('folder_aliases', {})
root_paths = list(set(
[os.path.dirname(p) for p in environ.configs.fetch('recent_paths', [])]
Expand All @@ -47,10 +30,7 @@ def get_known_root_paths():


def echo_known_projects(response: Response) -> Response:
"""

:return:
"""
"""..."""
environ.configs.load()
project_specs = specio.ProjectSpecsReader()

Expand Down
2 changes: 1 addition & 1 deletion cauldron/cli/commands/ls.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def execute(
context: cli.CommandContext,
directory: str = None
) -> Response:
"""Execute listing of current directory."""
"""Execute listing of the specified directory."""
response = context.response

current_directory = environ.paths.clean(directory or os.curdir)
Expand Down
2 changes: 2 additions & 0 deletions cauldron/cli/commands/run/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import time
import typing
from argparse import ArgumentParser
from collections import OrderedDict
Expand Down Expand Up @@ -297,6 +298,7 @@ def run_local(
step_changes.append(dict(
name=ps.definition.name,
action='updated',
timestamp=time.time(),
step=writing.step_writer.serialize(ps)._asdict()
))

Expand Down
3 changes: 3 additions & 0 deletions cauldron/cli/commands/steps/actions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import time
import typing

from cauldron.cli.commands.steps import renaming as step_support
Expand Down Expand Up @@ -151,6 +152,7 @@ def create_step(
name=result.definition.name,
filename=result.filename,
action='added',
timestamp=time.time(),
step=writing.step_writer.serialize(result)._asdict(),
after=None if index < 1 else project.steps[index - 1].definition.name
)]
Expand Down Expand Up @@ -262,6 +264,7 @@ def modify_step(
name=new_step.definition.name,
filename=new_step.filename,
action='modified',
timestamp=time.time(),
after=before_step
)]

Expand Down
2 changes: 2 additions & 0 deletions cauldron/cli/commands/steps/removal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import time
import uuid

from cauldron.cli.commands.steps import renaming as step_support
Expand Down Expand Up @@ -54,6 +55,7 @@ def remove_step(
step_changes = [dict(
name=removed_name,
filename=step.filename,
timestamp=time.time(),
action='removed'
)]

Expand Down
2 changes: 1 addition & 1 deletion cauldron/cli/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def error_overload(message):

try:
parsed_args = parser.parse_args(args_list)
except TypeError: # pragma: no-cover
except TypeError: # pragma: no cover
parsed_args = None

if response.failed:
Expand Down
5 changes: 3 additions & 2 deletions cauldron/cli/server/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
import logging
import os
import site
import time
import typing
from argparse import ArgumentParser

from flask import Flask

import cauldron as cd
from cauldron import environ
from cauldron import templating
from cauldron.render.encoding import ComplexFlaskJsonEncoder
from cauldron.session import writing
from flask import Flask

APPLICATION = Flask('Cauldron')
APPLICATION.json_encoder = ComplexFlaskJsonEncoder
Expand Down Expand Up @@ -69,6 +69,7 @@ def get_changes(step):
name=step.definition.name,
action='updated',
step=step_data._asdict(),
timestamp=time.time(),
written=write
)

Expand Down
13 changes: 9 additions & 4 deletions cauldron/environ/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ def notify(
kind: str = None,
message: str = None,
code: str = None,
level: str = None,
**kwargs
) -> ResponseMessage:
"""..."""
Expand All @@ -328,9 +329,9 @@ def notify(
data=kwargs
)

if kind == 'ERROR':
if 'ERROR' in (kind, level):
self.errors.append(rm)
elif kind == 'WARNING':
elif 'WARNING' in (kind, level):
self.warnings.append(rm)
else:
self.messages.append(rm)
Expand Down Expand Up @@ -365,6 +366,7 @@ def fail(
message: str = None,
code: str = None,
error: Exception = None,
kind: str = 'ERROR',
**kwargs
) -> ResponseMessage:
"""..."""
Expand All @@ -374,26 +376,29 @@ def fail(
error = '{}'.format(error) if error else None

return self.notify(
kind='ERROR',
kind=kind,
message=cli.as_single_line(message),
code=code,
failed=True,
stack=stack,
error=error,
level='ERROR',
**kwargs
)

def warn(
self,
message: str = None,
code: str = None,
kind: str = 'WARNING',
**kwargs
) -> ResponseMessage:
"""..."""
return self.notify(
kind='WARNING',
kind=kind,
message=message,
code=code,
level='WARNING',
**kwargs
)

Expand Down
2 changes: 1 addition & 1 deletion cauldron/render/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def plotly(
dom_id = found.group('id')

# Plotly < 4.0 requires manually inserting the static value.
if static and dom.find('"staticPlot": ') < 0: # pragma: no-cover
if static and dom.find('"staticPlot": ') < 0: # pragma: no cover
insert_index = dom.index('"showLink":')
dom = ''.join([
dom[:insert_index],
Expand Down
7 changes: 6 additions & 1 deletion cauldron/render/texts.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,13 @@ def markdown(
font_size=font_size
)

pattern = re.compile('src="(?P<url>[^"]+)"')
pattern = re.compile(r'src="(?P<url>[^"]+)"')
body = pattern.sub(r'data-src="\g<url>"', body)

# Force all links to open in new windows/tabs
pattern = re.compile(r'href="(?P<url>[^"]+)"')
body = pattern.sub(r'href="\g<url>" target="_blank"', body)

return dict(
body=body,
library_includes=library_includes,
Expand Down
Loading