Skip to content

[IMP] runbot: diffable error contents in error form #1099

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: 18.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
79 changes: 79 additions & 0 deletions runbot/static/src/js/fields/diff_display.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Component, onWillRender } from '@odoo/owl';

import { diff_match_patch } from "@runbot/libs/diff_match_patch/diff_match_patch";


export class DiffDisplay extends Component {
static template = 'runbot.DiffDisplay';
static props = {
fromValue: { type: String },
toValue: { type: String },
lineFilter: { type: Function, optional: true },
}
static defaultProps = {
lineFilter: (line) => line.type !== 'kept',
}

setup() {
onWillRender(() => {
this.lines = this.makeLines(this.props.fromValue, this.props.toValue);
});
}

makeLines(oldValue, newValue) {
const diff = this.makeDiff(oldValue, newValue);
const lines = this.prepareForRendering(diff);
return lines;
}

makeDiff(text1, text2) {
const dmp = new diff_match_patch();
const a = dmp.diff_linesToChars_(text1, text2);
const lineText1 = a.chars1;
const lineText2 = a.chars2;
const lineArray = a.lineArray;
const diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
dmp.diff_cleanupSemantic(diffs);
return diffs;
}

prepareForRendering(diffs) {
let preLineCounter = 0;
let postLineCounter = 0;
return diffs.reduce((lines, {0: diff_type, 1: data}) => {
data.split('\n').forEach(line => {
line = line
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
let type, colOne, colTwo;
switch (diff_type) {
case 0: //kept
type = 'kept'
colOne = ''
colTwo = postLineCounter;
preLineCounter++; postLineCounter++;
break;
case -1: //removed
type = 'removed';
colOne = preLineCounter;
colTwo = '-';
preLineCounter++;
break;
case 1: //added
type = 'added';
colOne = '+';
colTwo = postLineCounter;
postLineCounter++;
break;
default:
console.warn('Unknown diff_type', diff_type)
return;
}
lines.push({type, colOne, colTwo, line});
})
return lines
}, []);
}
}
14 changes: 14 additions & 0 deletions runbot/static/src/js/fields/diff_display.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="runbot.DiffDisplay">
<div class="code_diff">
<table>
<tr t-foreach="lines" t-as="line" t-key="line_index" t-if="props.lineFilter(line)">
<td class="col_number" t-out="line.colOne"/>
<td class="col_number" t-out="line.colTwo"/>
<td class="code" t-att-class="line.type" t-out="line.line"/>
</tr>
</table>
</div>
</t>
</templates>
84 changes: 84 additions & 0 deletions runbot/static/src/js/fields/field_error_content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Component, useEffect, useState } from '@odoo/owl';

import { registry } from '@web/core/registry';
import { useBus } from '@web/core/utils/hooks';
import { standardFieldProps } from "@web/views/fields/standard_field_props";
import { TextField } from '@web/views/fields/text/text_field';
import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field";
import { CheckBox } from "@web/core/checkbox/checkbox";

import { diff_match_patch } from "@runbot/libs/diff_match_patch/diff_match_patch";
import { DiffDisplay } from './diff_display';


export class ErrorContentOne2ManyList extends X2ManyField {
static template = 'runbot.ErrorContentOne2ManyList';
static components = {...X2ManyField.components, CheckBox};

setup() {
super.setup(...arguments);
this.creates = [];
this.state = useState({
useDiff: localStorage.getItem('runbot.error_content_diff_mode') === 'true',
});

useEffect(() => {
localStorage.setItem('runbot.error_content_diff_mode', this.state.useDiff);
this.env.bus.trigger(
'RUNBOT.TOGGLE-DIFF-MODE', {
mode: this.state.useDiff,
},
);
}, () => [this.state.useDiff]);
}

get displayControlPanelButtons() {
return true;
}

onToggle(state) {
this.state.useDiff = state;
}
}

export const errorContentOne2ManyList = {
...x2ManyField,
component: ErrorContentOne2ManyList,
};

export class FieldErrorContentContent extends Component {
static template = 'runbot.FieldErrorContentContent';
static components = { TextField, DiffDisplay };
static props = {...standardFieldProps};

setup() {
// We assume that the content is readonly here
this.otherRecords = this.props.record.model.root.data.error_content_ids.records;
this.state = useState({
useDiff: localStorage.getItem('runbot.error_content_diff_mode') === 'true',
});

useBus(
this.env.bus, 'RUNBOT.TOGGLE-DIFF-MODE',
({detail: {mode}}) => this.state.useDiff = mode,
);
}

get isParent() {
return this.otherRecords[0] === this.props.record;
}

get parent() {
return this.otherRecords[0];
}
}

export const fieldErrorContentContent = {
component: FieldErrorContentContent,
displayName: "Error Content diffable (list)",
supportedTypes: ["html", "text", "char"],
};


registry.category('fields').add('error_content_list', errorContentOne2ManyList);
registry.category('fields').add('list.error_content_content', fieldErrorContentContent);
27 changes: 27 additions & 0 deletions runbot/static/src/js/fields/field_error_content.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="runbot.ErrorContentOne2ManyList" t-inherit="web.X2ManyField" t-inherit-mode="primary">
<div class="o_x2m_control_panel d-empty-none mt-1 mb-4" position="attributes">
<attribute name="class">o_x2m_control_panel d-empty-none mt-1</attribute>
</div>
<div role="toolbar" position="inside">
<CheckBox
className="'o_boolean_toggle form-switch'"
value="state.useDiff"
onChange.bind="onToggle"
>
Toggle diff mode
</CheckBox>
</div>
</t>

<t t-name="runbot.FieldErrorContentContent">
<t t-if="isParent || !this.state.useDiff">
<TextField t-props="this.props"/>
</t>
<DiffDisplay t-else=""
fromValue="parent.data.content" toValue="props.record.data.content"
lineFilter="(line) => line.type === 'added'"
/>
</t>
</templates>
56 changes: 5 additions & 51 deletions runbot/static/src/js/fields/tracking_value.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
import { patch } from "@web/core/utils/patch";
import { Message } from "@mail/core/common/message";
import { diff_match_patch } from "@runbot/libs/diff_match_patch/diff_match_patch";
import { DiffDisplay } from './diff_display';

patch(Message, {
components: {...Message.components, DiffDisplay},
});

patch(Message.prototype, {
setup() {
Expand All @@ -13,9 +18,6 @@ patch(Message.prototype, {
const newValue = trackingValue.newValue.value;
return ((oldValue && typeof oldValue=== 'string' && oldValue.includes('\n')) && (newValue && typeof oldValue=== 'string' && newValue.includes('\n')))
},
formatTracking(trackingType, trackingValue) {
return super.formatTracking(trackingType, trackingValue)
},
toggleKept() {
this.kept = !this.kept;
},
Expand All @@ -29,52 +31,4 @@ patch(Message.prototype, {
navigator.clipboard.writeText(trackingValue.newValue.value);
};
},
lines(trackingValue) {
const oldValue = trackingValue.oldValue.value;
const newValue = trackingValue.newValue.value;
const diff = this.makeDiff(oldValue, newValue);
const lines = this.prepareForRendering(diff);
return lines;
},
makeDiff(text1, text2) {
var dmp = new diff_match_patch();
var a = dmp.diff_linesToChars_(text1, text2);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmp.diff_main(lineText1, lineText2, false);
dmp.diff_charsToLines_(diffs, lineArray);
dmp.diff_cleanupSemantic(diffs);
return diffs;
},
prepareForRendering(diffs) {
var lines = [];
var pre_line_counter = 0
var post_line_counter = 0
for (var x = 0; x < diffs.length; x++) {
var diff_type = diffs[x][0];
var data = diffs[x][1];
var data_lines = data.split('\n');
for (var line_index in data_lines) {
var line = data_lines[line_index];
line = line.replace(/&/g, '&amp;');
line = line.replace(/</g, '&lt;');
line = line.replace(/>/g, '&gt;');
//text = text.replace(/\n/g, '<br>');
//text = text.replace(/ /g, '&nbsp&nbsp');
if (diff_type == -1) {
lines.push({type:'removed', pre_line_counter: pre_line_counter, post_line_counter: '-', line: line})
pre_line_counter += 1
} else if (diff_type == 0) {
lines.push({type:'kept', pre_line_counter: '', post_line_counter: post_line_counter, line: line})
pre_line_counter += 1
post_line_counter +=1
} else if (diff_type == 1) {
lines.push({type:'added', pre_line_counter: '+', post_line_counter: post_line_counter, line: line})
post_line_counter +=1
}
}
}
return lines;
},
});
15 changes: 4 additions & 11 deletions runbot/static/src/js/fields/tracking_value.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,10 @@
<button class="btn btn-sm btn-outline-primary" t-on-click="copyNewToClipboard(trackingValue)">Copy new value to clipboard</button>
</div>
<div class="o-mail-Message-trackingField ms-1 fst-italic text-muted">(<t t-out="trackingValue.changedField"/>)</div>
<div class="code_diff">
<table>
<t t-foreach="lines(trackingValue)" t-as="line" t-key="line_index">
<tr t-if="kept or line.type!=='kept'">
<td class="col_number" t-out="line.pre_line_counter"/>
<td class="col_number" t-out="line.post_line_counter"/>
<td class="code" t-att-class="line.type" t-out="line.line"/>
</tr>
</t>
</table>
</div>
<DiffDisplay
fromValue="trackingValue.oldValue.value" toValue="trackingValue.newValue.value"
lineFilter="kept ? () => true : undefined"
/>
</t>
<t t-else="">
<span class="o-mail-Message-trackingOld me-1 px-1 text-muted fw-bold" t-out="formatTrackingOrNone(trackingValue.fieldType, trackingValue.oldValue)"/>
Expand Down
4 changes: 2 additions & 2 deletions runbot/views/build_error_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<button name="action_view_errors" string="See all linked errors" type="object" class="oe_highlight"/>
<group string="Base info">
<field name="name"/>
<field name="error_content_ids" readonly="1">
<field name="error_content_ids" readonly="1" widget="error_content_list">
<list limit="5">
<field name="content" readonly="1"/>
<field name="content" readonly="1" widget="error_content_content"/>
<!--field name="module_name" readonly="1"/-->
<!--field name="function" readonly="1"/-->
<!--field name="file_path" readonly="1"/-->
Expand Down