<commit_before>import PluginComponent from '../plugin.component';
import {DATA_MANIPULATION_NAME} from '../definition';
import {Command} from '@grid/core/command';
import {TemplatePath} from '@grid/core/template';
import {Action} from '@grid/core/action';
import {AppError} from '@grid/core/infrastructure';
import {isUndefined} from '@grid/core/utility';

TemplatePath
	.register(DATA_MANIPULATION_NAME, () => {
		return {
			model: 'dataManipulation',
			resource: 'content'
		};
	});

const Plugin = PluginComponent('data-manipulation', {models: ['dataManipulation']});
class DataManipulation extends Plugin {
	constructor() {
		super(...arguments);

		this.commitCommand = new Command({
			execute: e => {
				if (e.column.class !== 'data') {
					return;
				}

				const rowId = this.rowId(e.row);
				const edited = this.changes.edited;

				let entries = edited.get(rowId);
				if (!entries) {
					entries = [];
					edited.set(rowId, entries);
				}

				let entryIndex = entries.findIndex(entry => entry.column === e.column.key);
				let entry = entries[entryIndex];
				if (!entry) {
					entry = {
						column: e.column.key,
						oldValue: e.oldValue,
						oldLabel: e.oldLabel
					};

					entryIndex = entries.length;
					entries.push(entry);
				}

				entry.newValue = e.newValue;
				entry.newLabel = e.newLabel;

				// TODO: understand if we need to track label changes?
				if (!this.hasChanges(entry.newValue, entry.oldValue)) {
					entries.splice(entryIndex, 1);
					if (!entries.length) {
						edited.delete(rowId);
					}
				}
			}
		});

		this.actions = [
			new Action(
				new Command({
					execute: () => {
						const newRow = this.rowFactory(this.model.data().rows[0]);
						if (isUndefined(newRow)) {
							throw new AppError('data.manipulation', 'Setup rowFactory property to add new rows');
						}

						const rowId = this.rowId(newRow);
						const data = this.model.data;

						this.changes.added.add(rowId);
						data({
							rows: [newRow].concat(data().rows)
						});
					},
					shortcut: 'F7'
				}),
				'Add New Row',
				'add'
			)];

		this.rowActions = [
			new Action(
				new Command({
					canExecute: e => {
						const rowId = this.rowId(e.row);
						return !this.changes.deleted.has(rowId);
					},
					execute: e => {
						const rowId = this.rowId(e.row);
						const changes = this.changes;
						if (changes.added.has(rowId)) {
							changes.added.delete(rowId);
							const data = this.model.data;
							const rows = data().rows.filter(row => this.rowId(row) !== rowId);
							data({
								rows: rows
							});
						}
						else {
							changes.deleted.add(rowId);
						}
					}
				}),
				'Delete Row',
				'delete'
			),
			new Action(
				new Command({
					execute: e => {
						const rowId = this.rowId(e.row);
						this.changes.deleted.delete(rowId);
					},
					canExecute: e => {
						const rowId = this.rowId(e.row);
						return this.changes.deleted.has(rowId);
					}
				}),
				'Restore',
				'restore'
			),
			// new Action(
			// 	new Command({
			// 		execute: () => {
			// 			// TODO make edit form service
			// 		}
			// 	}),
			// 	'Edit Form',
			// 	'edit'
			// )
		];
	}

	onInit() {
		const model = this.model;
		this.rowId = model.dataManipulation().rowId;
		this.rowFactory = model.dataManipulation().rowFactory;

		model
			.edit({
				mode: 'cell',
				commit: this.commitCommand
			})
			.style({
				row: this.styleRow.bind(this),
				cell: this.styleCell.bind(this)
			})
			.action({
				items: this.actions.concat(model.action().items)
			});

		model.dataChanged.watch((e, off) => {
			if (e.hasChanges('columns')) {
				const rowOptionsColumn = model.data().columns.find(column => column.type === 'row-options');
				if (rowOptionsColumn) {
					rowOptionsColumn.editorOptions.actions.push(...this.rowActions);
					off();
				}
			}
		});
	}

	hasChanges(newValue, oldValue) {
		// TODO: understand if we need to parse values (e.g. '12' vs 12)
		return newValue !== oldValue;<commit_msg>data manipulation restore modified values<commit_after>import PluginComponent from '../plugin.component';
import {DATA_MANIPULATION_NAME} from '../definition';
import {Command} from '@grid/core/command';
import {TemplatePath} from '@grid/core/template';
import {Action} from '@grid/core/action';
import {AppError} from '@grid/core/infrastructure';
import {isUndefined} from '@grid/core/utility';
import * as columnService from '@grid/core/column/column.service';
import {set as setValue} from '@grid/core/services/value';
import {set as setLabel} from '@grid/core/services/label';


TemplatePath
	.register(DATA_MANIPULATION_NAME, () => {
		return {
			model: 'dataManipulation',
			resource: 'content'
		};
	});

const Plugin = PluginComponent('data-manipulation', {models: ['dataManipulation']});
class DataManipulation extends Plugin {
	constructor() {
		super(...arguments);

		this.commitCommand = new Command({
			execute: e => {
				if (e.column.class !== 'data') {
					return;
				}

				const rowId = this.rowId(e.row);
				const edited = this.changes.edited;

				let entries = edited.get(rowId);
				if (!entries) {
					entries = [];
					edited.set(rowId, entries);
				}

				let entryIndex = entries.findIndex(entry => entry.column === e.column.key);
				let entry = entries[entryIndex];
				if (!entry) {
					entry = {
						column: e.column.key,
						oldValue: e.oldValue,
						oldLabel: e.oldLabel
					};

					entryIndex = entries.length;
					entries.push(entry);
				}

				entry.newValue = e.newValue;
				entry.newLabel = e.newLabel;

				// TODO: understand if we need to track label changes?
				if (!this.hasChanges(entry.newValue, entry.oldValue)) {
					entries.splice(entryIndex, 1);
					if (!entries.length) {
						edited.delete(rowId);
					}
				}
			}
		});

		this.actions = [
			new Action(
				new Command({
					execute: () => {
						const newRow = this.rowFactory(this.model.data().rows[0]);
						if (isUndefined(newRow)) {
							throw new AppError('data.manipulation', 'Setup rowFactory property to add new rows');
						}

						const rowId = this.rowId(newRow);
						const data = this.model.data;

						this.changes.added.add(rowId);
						data({
							rows: [newRow].concat(data().rows)
						});
					},
					shortcut: 'F7'
				}),
				'Add New Row',
				'add'
			)];

		this.rowActions = [
			new Action(
				new Command({
					canExecute: e => {
						const rowId = this.rowId(e.row);
						return !this.changes.deleted.has(rowId);
					},
					execute: e => {
						const rowId = this.rowId(e.row);
						const changes = this.changes;
						if (changes.added.has(rowId)) {
							changes.added.delete(rowId);
							const data = this.model.data;
							const rows = data().rows.filter(row => this.rowId(row) !== rowId);
							data({rows});
						}
						else {
							changes.deleted.add(rowId);
						}
					}
				}),
				'Delete Row',
				'delete'
			),
			new Action(
				new Command({
					execute: e => {
						const rowId = this.rowId(e.row);
						if (this.changes.deleted.has(rowId)) {
							this.changes.deleted.delete(rowId);
						}

						if (this.changes.edited.has(rowId)) {
							try {
								const edits = this.changes.edited.get(rowId);
								const columnMap = columnService.map(this.model.data().columns);								
								for(const edit of edits) {
									const column = columnMap[edit.column];
									if (!column){
										throw new AppError('data.manipulation', `Column ${edit.column} is not found`);
									}
	
									setValue(e.row, column, edit.oldValue);
									setLabel(e.row, column, edit.oldLabel);
								}
							}
							finally {
								this.changes.edited.delete(rowId);	
							}
						}
					},
					canExecute: e => {
						const rowId = this.rowId(e.row);
						return this.changes.deleted.has(rowId) || this.changes.edited.has(rowId);
					}
				}),
				'Restore Row',
				'restore'
			),
			// new Action(
			// 	new Command({
			// 		execute: () => {
			// 			// TODO make edit form service
			// 		}
			// 	}),
			// 	'Edit Form',
			// 	'edit'
			// )
		];
	}

	onInit() {
		const model = this.model;
		this.rowId = model.dataManipulation().rowId;
		this.rowFactory = model.dataManipulation().rowFactory;

		model
			.edit({
				mode: 'cell',
				commit: this.commitCommand
			})
			.style({
				row: this.styleRow.bind(this),
				cell: this.styleCell.bind(this)
			})
			.action({
				items: this.actions.concat(model.action().items)
			});

		model.dataChanged.watch((e, off) => {
			if (e.hasChanges('columns')) {
				const rowOptionsColumn = model
					.data()
					.columns
					.find(column => column.type === 'row-options');
				
				if (rowOptionsColumn) {
					rowOptionsColumn.editorOptions.actions.push(...this.rowActions);
					off();
				}
			}
		});
	}

	hasChanges(newValue, oldValue) {
		// TODO: understand if we need to parse values (e.g. '12' vs 12)
		return newValue !== oldValue;
{'fraction_non_alphanumeric': 0.15695267927061193, 'fraction_numerical': 0.0015661707126076742, 'mean_word_length': 2.4279141104294477, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5211207', 'n_tokens_mistral': 4016, 'n_tokens_neox': 2935, 'n_words': 599}