From deab8ceeb015b4d98ec95a16117fa30ef38c0ba0 Mon Sep 17 00:00:00 2001 From: xseris Date: Wed, 12 Sep 2018 15:45:52 +0200 Subject: [PATCH 01/18] custom column names for separator based importer --- .../importers/SeparatorBasedImporter.java | 28 +++++++++++++++++++ .../separator-based-parser-ui.html | 2 ++ .../separator-based-parser-ui.js | 21 ++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/main/src/com/google/refine/importers/SeparatorBasedImporter.java b/main/src/com/google/refine/importers/SeparatorBasedImporter.java index 5d471776f..0ec95b394 100644 --- a/main/src/com/google/refine/importers/SeparatorBasedImporter.java +++ b/main/src/com/google/refine/importers/SeparatorBasedImporter.java @@ -101,6 +101,28 @@ public class SeparatorBasedImporter extends TabularImportingParserBase { boolean processQuotes = JSONUtilities.getBoolean(options, "processQuotes", true); boolean strictQuotes = JSONUtilities.getBoolean(options, "strictQuotes", false); + + List retrievedColumnNames = null; + if (options.has("columnNames")) { + String[] strings = JSONUtilities.getStringArray(options, "columnNames"); + if (strings.length > 0) { + retrievedColumnNames = new ArrayList(); + for (String s : strings) { + s = s.trim(); + if (!s.isEmpty()) { + retrievedColumnNames.add(s); + } + } + + if (retrievedColumnNames.size() > 0) { + JSONUtilities.safePut(options, "headerLines", 1); + } else { + retrievedColumnNames = null; + } + } + } + final List columnNames = retrievedColumnNames; + Character quote = CSVParser.DEFAULT_QUOTE_CHARACTER; String quoteCharacter = JSONUtilities.getString(options, "quoteCharacter", null); if (quoteCharacter != null && quoteCharacter.trim().length() == 1) { @@ -118,14 +140,20 @@ public class SeparatorBasedImporter extends TabularImportingParserBase { final LineNumberReader lnReader = new LineNumberReader(reader); TableDataReader dataReader = new TableDataReader() { + boolean usedColumnNames = false; @Override public List getNextRowOfCells() throws IOException { + if (columnNames != null && !usedColumnNames) { + usedColumnNames = true; + return columnNames; + } else { String line = lnReader.readLine(); if (line == null) { return null; } else { return getCells(line, parser, lnReader); } + } } }; diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html index c7c3ba283..701cd3d59 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html @@ -24,6 +24,8 @@ + +
diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js index 0cc8637ee..f209762d7 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js @@ -117,6 +117,11 @@ Refine.SeparatorBasedParserUI.prototype.getOptions = function() { options.storeBlankCellsAsNulls = this._optionContainerElmts.storeBlankCellsAsNullsCheckbox[0].checked; options.includeFileSources = this._optionContainerElmts.includeFileSourcesCheckbox[0].checked; + + var columnNames = this._optionContainerElmts.columnNamesInput.val(); + if (columnNames != undefined && columnNames != null && columnNames != '') { + options.columnNames = columnNames.split(","); + } return options; }; @@ -136,6 +141,10 @@ Refine.SeparatorBasedParserUI.prototype._initialize = function() { $('#or-import-tabs').html($.i18n._('core-index-parser')["tabs"]); $('#or-import-custom').html($.i18n._('core-index-parser')["custom"]); $('#or-import-escape').html($.i18n._('core-index-parser')["escape"]); + $('#or-import-columnNames').html($.i18n._('core-index-parser')["column-names-label"] + ':'); + $('#or-import-optional').html($.i18n._('core-index-parser')["column-names-optional"]); + + self._optionContainerElmts.columnNamesInput.prop('disabled', true); $('#or-import-ignore').text($.i18n._('core-index-parser')["ignore-first"]); $('#or-import-lines').text($.i18n._('core-index-parser')["lines-beg"]); @@ -160,6 +169,18 @@ Refine.SeparatorBasedParserUI.prototype._initialize = function() { }); }); + this._optionContainerElmts.headerLinesCheckbox.on("click", function() { + if ($(this).is(':checked')) { + var isDisabled = $('textbox').prop('disabled'); + if (!isDisabled) { + self._optionContainerElmts.columnNamesInput.prop('disabled', true); + self._optionContainerElmts.columnNamesInput.val(''); + } + } else { + self._optionContainerElmts.columnNamesInput.prop('disabled', false); + } + }); + var columnSeparatorValue = (this._config.separator == ",") ? 'comma' : ((this._config.separator == "\\t") ? 'tab' : 'custom'); this._optionContainer.find( From de30b34e67add1c6e7e042139fbe81738202ec0e Mon Sep 17 00:00:00 2001 From: xseris Date: Wed, 12 Sep 2018 15:54:55 +0200 Subject: [PATCH 02/18] commit missing translation file --- main/webapp/modules/core/langs/translation-en.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index 0f4a0b88e..0fca7b5d5 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -155,7 +155,9 @@ "escape": "Escape special characters with \\", "use-quote": "Use character", "quote-delimits-cells": "to enclose cells containing column separators", - "click-xml": "Click on the first XML element corresponding to the first record to load." + "click-xml": "Click on the first XML element corresponding to the first record to load.", + "column-names-label": "Column names", + "column-names-optional":"comma separated" }, "core-dialogs": { "cluster-edit": "Cluster & Edit column", From 81e8e4c2f62f82ea04ed1184f9f18db0adc19611 Mon Sep 17 00:00:00 2001 From: xseris Date: Thu, 13 Sep 2018 11:25:07 +0200 Subject: [PATCH 03/18] Fix visualization. Auto refresh preview on input change. --- .../index/parser-interfaces/separator-based-parser-ui.html | 5 ++++- .../index/parser-interfaces/separator-based-parser-ui.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html index 701cd3d59..82f674024 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html @@ -24,7 +24,10 @@ - +
+
+ +
diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js index f209762d7..3d46b929d 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js @@ -227,6 +227,7 @@ Refine.SeparatorBasedParserUI.prototype._initialize = function() { }; this._optionContainer.find("input").bind("change", onChange); this._optionContainer.find("select").bind("change", onChange); + this._optionContainerElmts.columnNamesInput.bind("keyup",onChange); }; Refine.SeparatorBasedParserUI.prototype._scheduleUpdatePreview = function() { From f288bc653e420e2d7f4b3cd443a65dc6dcce439e Mon Sep 17 00:00:00 2001 From: xseris Date: Thu, 13 Sep 2018 14:45:38 +0200 Subject: [PATCH 04/18] Added test, changed visualization --- .../importers/SeparatorBasedImporter.java | 2 +- .../tests/importers/TsvCsvImporterTests.java | 49 +++++++++++++++---- .../separator-based-parser-ui.html | 18 +++---- .../separator-based-parser-ui.js | 21 ++++++-- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/main/src/com/google/refine/importers/SeparatorBasedImporter.java b/main/src/com/google/refine/importers/SeparatorBasedImporter.java index 0ec95b394..2c483019e 100644 --- a/main/src/com/google/refine/importers/SeparatorBasedImporter.java +++ b/main/src/com/google/refine/importers/SeparatorBasedImporter.java @@ -114,7 +114,7 @@ public class SeparatorBasedImporter extends TabularImportingParserBase { } } - if (retrievedColumnNames.size() > 0) { + if (!retrievedColumnNames.isEmpty()) { JSONUtilities.safePut(options, "headerLines", 1); } else { retrievedColumnNames = null; diff --git a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java index 5d714ad98..7893cefa2 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java @@ -514,6 +514,27 @@ public class TsvCsvImporterTests extends ImporterTest { Assert.assertEquals(project.rows.get(0).cells.get(2).value, "data3"); } + @Test(dataProvider = "CSV-TSV-AutoDetermine") + public void readCustomColumnNames(String sep){ + //create input + String inputSeparator = sep == null ? "\t" : sep; + String input = "data1" + inputSeparator + "data2" + inputSeparator + "data3\n"; + + try { + prepareOptions(sep, -1, 0, 0, 0, false, false,"\"","col1,col2,col3"); + parseOneFile(SUT, new StringReader(input)); + } catch (Exception e) { + Assert.fail("Exception during file parse",e); + } + Assert.assertEquals(project.columnModel.columns.size(), 3); + Assert.assertEquals(project.columnModel.columns.get(0).getName(), "col1"); + Assert.assertEquals(project.columnModel.columns.get(1).getName(), "col2"); + Assert.assertEquals(project.columnModel.columns.get(2).getName(), "col3"); + Assert.assertEquals(project.rows.get(0).cells.get(0).value, "data1"); + Assert.assertEquals(project.rows.get(0).cells.get(1).value, "data2"); + Assert.assertEquals(project.rows.get(0).cells.get(2).value, "data3"); + } + //---------------------read tests------------------------ @Test public void readCsvWithProperties() { @@ -579,17 +600,24 @@ public class TsvCsvImporterTests extends ImporterTest { protected void prepareOptions( String sep, int limit, int skip, int ignoreLines, int headerLines, boolean guessValueType, boolean ignoreQuotes, String quoteCharacter) { - - whenGetStringOption("separator", options, sep); - whenGetStringOption("quoteCharacter", options, quoteCharacter); - whenGetIntegerOption("limit", options, limit); - whenGetIntegerOption("skipDataLines", options, skip); - whenGetIntegerOption("ignoreLines", options, ignoreLines); - whenGetIntegerOption("headerLines", options, headerLines); - whenGetBooleanOption("guessCellValueTypes", options, guessValueType); - whenGetBooleanOption("processQuotes", options, !ignoreQuotes); - whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + prepareOptions(sep, limit, skip, ignoreLines, headerLines, guessValueType, ignoreQuotes, "\"",""); } + + protected void prepareOptions( + String sep, int limit, int skip, int ignoreLines, + int headerLines, boolean guessValueType, boolean ignoreQuotes, String quoteCharacter, String columnNames) { + + whenGetStringOption("separator", options, sep); + whenGetStringOption("quoteCharacter", options, quoteCharacter); + whenGetIntegerOption("limit", options, limit); + whenGetIntegerOption("skipDataLines", options, skip); + whenGetIntegerOption("ignoreLines", options, ignoreLines); + whenGetIntegerOption("headerLines", options, headerLines); + whenGetBooleanOption("guessCellValueTypes", options, guessValueType); + whenGetBooleanOption("processQuotes", options, !ignoreQuotes); + whenGetBooleanOption("storeBlankCellsAsNulls", options, true); + whenGetStringOption("columnNames", options, columnNames); + } private void verifyOptions() { try { @@ -601,6 +629,7 @@ public class TsvCsvImporterTests extends ImporterTest { verify(options, times(1)).getBoolean("guessCellValueTypes"); verify(options, times(1)).getBoolean("processQuotes"); verify(options, times(1)).getBoolean("storeBlankCellsAsNulls"); + verify(options, times(1)).getBoolean("columnNames"); } catch (JSONException e) { Assert.fail("JSON exception",e); } diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html index 82f674024..14f04d834 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html @@ -1,4 +1,4 @@ -
+
+
@@ -24,12 +24,7 @@ -
-
- - - -
@@ -60,9 +55,14 @@
-   +
+ + + + +
-
+ diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js index 3d46b929d..099a597ac 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.js @@ -118,9 +118,11 @@ Refine.SeparatorBasedParserUI.prototype.getOptions = function() { options.storeBlankCellsAsNulls = this._optionContainerElmts.storeBlankCellsAsNullsCheckbox[0].checked; options.includeFileSources = this._optionContainerElmts.includeFileSourcesCheckbox[0].checked; - var columnNames = this._optionContainerElmts.columnNamesInput.val(); - if (columnNames != undefined && columnNames != null && columnNames != '') { - options.columnNames = columnNames.split(","); + if (this._optionContainerElmts.columnNamesCheckbox[0].checked) { + var columnNames = this._optionContainerElmts.columnNamesInput.val(); + if (columnNames != undefined && columnNames != null && columnNames != '') { + options.columnNames = columnNames.split(","); + } } return options; @@ -174,10 +176,23 @@ Refine.SeparatorBasedParserUI.prototype._initialize = function() { var isDisabled = $('textbox').prop('disabled'); if (!isDisabled) { self._optionContainerElmts.columnNamesInput.prop('disabled', true); + self._optionContainerElmts.columnNamesCheckbox.prop("checked", false); self._optionContainerElmts.columnNamesInput.val(''); } } else { self._optionContainerElmts.columnNamesInput.prop('disabled', false); + self._optionContainerElmts.columnNamesCheckbox.prop("checked", true); + } + }); + + this._optionContainerElmts.columnNamesCheckbox.on("click", function() { + if ($(this).is(':checked')) { + self._optionContainerElmts.headerLinesCheckbox.prop("checked", false); + self._optionContainerElmts.columnNamesInput.prop('disabled', false); + } else { + self._optionContainerElmts.headerLinesCheckbox.prop("checked", true); + self._optionContainerElmts.columnNamesInput.val(''); + self._optionContainerElmts.columnNamesInput.prop('disabled', true); } }); From 31c70be20a1910e3e14237585ca10dda8be87d22 Mon Sep 17 00:00:00 2001 From: xseris Date: Thu, 13 Sep 2018 15:12:52 +0200 Subject: [PATCH 05/18] Fixed importing test --- .../refine/tests/importers/TsvCsvImporterTests.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java index 7893cefa2..21d29db68 100644 --- a/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java +++ b/main/tests/server/src/com/google/refine/tests/importers/TsvCsvImporterTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verify; import java.io.StringReader; +import org.json.JSONArray; import org.json.JSONException; import org.slf4j.LoggerFactory; import org.testng.Assert; @@ -521,7 +522,7 @@ public class TsvCsvImporterTests extends ImporterTest { String input = "data1" + inputSeparator + "data2" + inputSeparator + "data3\n"; try { - prepareOptions(sep, -1, 0, 0, 0, false, false,"\"","col1,col2,col3"); + prepareOptions(sep, -1, 0, 0, 1, false, false,"\"","[col1,col2,col3]"); parseOneFile(SUT, new StringReader(input)); } catch (Exception e) { Assert.fail("Exception during file parse",e); @@ -600,7 +601,8 @@ public class TsvCsvImporterTests extends ImporterTest { protected void prepareOptions( String sep, int limit, int skip, int ignoreLines, int headerLines, boolean guessValueType, boolean ignoreQuotes, String quoteCharacter) { - prepareOptions(sep, limit, skip, ignoreLines, headerLines, guessValueType, ignoreQuotes, "\"",""); + + prepareOptions(sep, limit, skip, ignoreLines, headerLines, guessValueType, ignoreQuotes, quoteCharacter,"[]"); } protected void prepareOptions( @@ -616,7 +618,7 @@ public class TsvCsvImporterTests extends ImporterTest { whenGetBooleanOption("guessCellValueTypes", options, guessValueType); whenGetBooleanOption("processQuotes", options, !ignoreQuotes); whenGetBooleanOption("storeBlankCellsAsNulls", options, true); - whenGetStringOption("columnNames", options, columnNames); + whenGetArrayOption("columnNames", options, new JSONArray(columnNames)); } private void verifyOptions() { @@ -629,7 +631,7 @@ public class TsvCsvImporterTests extends ImporterTest { verify(options, times(1)).getBoolean("guessCellValueTypes"); verify(options, times(1)).getBoolean("processQuotes"); verify(options, times(1)).getBoolean("storeBlankCellsAsNulls"); - verify(options, times(1)).getBoolean("columnNames"); + verify(options, times(1)).getJSONArray("columnNames"); } catch (JSONException e) { Assert.fail("JSON exception",e); } From 280650867e3aa2171b55adad5c5856147aae8e54 Mon Sep 17 00:00:00 2001 From: xseris Date: Thu, 13 Sep 2018 16:25:04 +0200 Subject: [PATCH 06/18] move "comma separated" near "Column values" --- main/webapp/modules/core/langs/translation-en.json | 2 +- .../index/parser-interfaces/separator-based-parser-ui.html | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index 0fca7b5d5..d72a4c0be 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -156,7 +156,7 @@ "use-quote": "Use character", "quote-delimits-cells": "to enclose cells containing column separators", "click-xml": "Click on the first XML element corresponding to the first record to load.", - "column-names-label": "Column names", + "column-names-label": "Column names (comma separated)", "column-names-optional":"comma separated" }, "core-dialogs": { diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html index 14f04d834..f7e2efdda 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html @@ -59,7 +59,6 @@ -
From f04b5ef0489ff54449541f30fa4192f97bc54ceb Mon Sep 17 00:00:00 2001 From: xseris Date: Thu, 13 Sep 2018 17:10:04 +0200 Subject: [PATCH 07/18] moved input box below label --- .../index/parser-interfaces/separator-based-parser-ui.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html index f7e2efdda..addb4cdd9 100644 --- a/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html +++ b/main/webapp/modules/core/scripts/index/parser-interfaces/separator-based-parser-ui.html @@ -56,9 +56,10 @@
- - - + + +
+
From d034ddb9a7a5f279c08b254decc3b4a244e2ff66 Mon Sep 17 00:00:00 2001 From: Jacky Date: Mon, 24 Sep 2018 20:38:25 -0400 Subject: [PATCH 08/18] force the encoding to UTF8 --- main/webapp/modules/core/MOD-INF/controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js index 55ca73bcd..44c58d272 100644 --- a/main/webapp/modules/core/MOD-INF/controller.js +++ b/main/webapp/modules/core/MOD-INF/controller.js @@ -585,7 +585,7 @@ function process(path, request, response) { var urlConnection = url.openConnection(); input = new Packages.java.io.BufferedReader( - new Packages.java.io.InputStreamReader(urlConnection.getInputStream())); + new Packages.java.io.InputStreamReader(urlConnection.getInputStream(), "UTF8")); output.write("/* ===== "); output.write(qualifiedPath.fullPath); From 534fc5e7d6b86ab2d5e5d8a382fc73c2fc38b68e Mon Sep 17 00:00:00 2001 From: Isao Matsunami Date: Mon, 24 Sep 2018 17:44:35 +0000 Subject: [PATCH 09/18] Translated using Weblate (Japanese) Currently translated at 100.0% (696 of 696 strings) Translation: OpenRefine/Translations Translate-URL: https://hosted.weblate.org/projects/openrefine/translations/ja/ --- main/webapp/modules/core/langs/translation-jp.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/main/webapp/modules/core/langs/translation-jp.json b/main/webapp/modules/core/langs/translation-jp.json index b5961f442..a4f87622f 100644 --- a/main/webapp/modules/core/langs/translation-jp.json +++ b/main/webapp/modules/core/langs/translation-jp.json @@ -110,7 +110,7 @@ "subject": "題名", "warning-proj-name": "プロジェクト名を指定してください。", "warning-rename": "プロジェクトのリネームに失敗しました:", - "edit-tags-desc": "タグを編集(空白とコンマで分割):", + "edit-tags-desc": "タグを編集(空白とカンマで分割):", "del-title": "プロジェクトを削除", "edit-tags": "プロジェクトタグを編集", "edit-meta-data": "文書情報", @@ -156,7 +156,9 @@ "lines-beg": "行分(先頭から)", "preserve-empty": "空文字を保存", "rows-data": "行分", - "load-at-most": "最大読み込み行数" + "load-at-most": "最大読み込み行数", + "column-names-label": "カラム名(カンマ切り)", + "column-names-optional": "カンマ切り" }, "core-dialogs": { "help": "ヘルプ", From b6e9ace597a2a07eb41b5baf213ec6b690b9ab80 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 25 Sep 2018 19:37:40 +0100 Subject: [PATCH 10/18] Temporary fix for HTTP 500 errors from Wikibase. Closes #1746 --- .../org/openrefine/wikidata/editing/EditBatchProcessor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java b/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java index b4afae964..e9822ee23 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java @@ -192,7 +192,8 @@ public class EditBatchProcessor { logger.info("Requesting documents"); currentDocs = null; int retries = 3; - while (currentDocs == null && retries > 0) { + // TODO: remove currentDocs.isEmpty() once https://github.com/Wikidata/Wikidata-Toolkit/issues/402 is solved + while ((currentDocs == null || currentDocs.isEmpty()) && retries > 0) { try { currentDocs = fetcher.getEntityDocuments(qidsToFetch); } catch (MediaWikiApiErrorException e) { From 2a68e761cbb5b0d687558d0a11a33cd570fc3086 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 2 Oct 2018 12:31:59 +0100 Subject: [PATCH 11/18] Implementation of UseValuesAsIdentifiers operation and command --- .../ReconUseValuesAsIdentifiersCommand.java | 31 ++++ .../ReconUseValuesAsIdentifiersOperation.java | 157 ++++++++++++++++++ .../webapp/modules/core/MOD-INF/controller.js | 2 + 3 files changed, 190 insertions(+) create mode 100644 main/src/com/google/refine/commands/recon/ReconUseValuesAsIdentifiersCommand.java create mode 100644 main/src/com/google/refine/operations/recon/ReconUseValuesAsIdentifiersOperation.java diff --git a/main/src/com/google/refine/commands/recon/ReconUseValuesAsIdentifiersCommand.java b/main/src/com/google/refine/commands/recon/ReconUseValuesAsIdentifiersCommand.java new file mode 100644 index 000000000..cece2bddb --- /dev/null +++ b/main/src/com/google/refine/commands/recon/ReconUseValuesAsIdentifiersCommand.java @@ -0,0 +1,31 @@ +package com.google.refine.commands.recon; + +import javax.servlet.http.HttpServletRequest; + +import org.json.JSONObject; + +import com.google.refine.browsing.EngineConfig; +import com.google.refine.commands.EngineDependentCommand; +import com.google.refine.model.AbstractOperation; +import com.google.refine.model.Project; +import com.google.refine.model.ReconCandidate; +import com.google.refine.model.recon.StandardReconConfig; +import com.google.refine.operations.recon.ReconMatchSpecificTopicOperation; +import com.google.refine.operations.recon.ReconUseValuesAsIdentifiersOperation; + +public class ReconUseValuesAsIdentifiersCommand extends EngineDependentCommand { + @Override + protected AbstractOperation createOperation(Project project, + HttpServletRequest request, EngineConfig engineConfig) throws Exception { + + String columnName = request.getParameter("columnName"); + + return new ReconUseValuesAsIdentifiersOperation( + engineConfig, + columnName, + request.getParameter("service"), + request.getParameter("identifierSpace"), + request.getParameter("schemaSpace") + ); + } +} diff --git a/main/src/com/google/refine/operations/recon/ReconUseValuesAsIdentifiersOperation.java b/main/src/com/google/refine/operations/recon/ReconUseValuesAsIdentifiersOperation.java new file mode 100644 index 000000000..0e6f65d92 --- /dev/null +++ b/main/src/com/google/refine/operations/recon/ReconUseValuesAsIdentifiersOperation.java @@ -0,0 +1,157 @@ +package com.google.refine.operations.recon; + +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONWriter; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import com.google.refine.browsing.EngineConfig; +import com.google.refine.browsing.RowVisitor; +import com.google.refine.expr.ExpressionUtils; +import com.google.refine.history.Change; +import com.google.refine.model.Cell; +import com.google.refine.model.Column; +import com.google.refine.model.Project; +import com.google.refine.model.Recon; +import com.google.refine.model.Recon.Judgment; +import com.google.refine.model.ReconCandidate; +import com.google.refine.model.Row; +import com.google.refine.model.changes.CellChange; +import com.google.refine.model.changes.ReconChange; +import com.google.refine.model.recon.StandardReconConfig; +import com.google.refine.operations.EngineDependentMassCellOperation; +import com.google.refine.operations.OperationRegistry; + +public class ReconUseValuesAsIdentifiersOperation extends EngineDependentMassCellOperation { + + @JsonProperty("identifierSpace") + protected String identifierSpace; + @JsonProperty("schemaSpace") + protected String schemaSpace; + @JsonProperty("service") + protected String service; + + @JsonIgnore + protected StandardReconConfig reconConfig; + + public ReconUseValuesAsIdentifiersOperation( + EngineConfig engineConfig, + String columnName, + String service, + String identifierSpace, + String schemaSpace) { + super(engineConfig, columnName, false); + this.service = service; + this.identifierSpace = identifierSpace; + this.schemaSpace = schemaSpace; + this.reconConfig = new StandardReconConfig(service, identifierSpace, schemaSpace, null, null, true, Collections.emptyList()); + } + + static public ReconUseValuesAsIdentifiersOperation reconstruct(JSONObject obj) throws Exception { + JSONObject engineConfig = obj.getJSONObject("engineConfig"); + return new ReconUseValuesAsIdentifiersOperation( + EngineConfig.reconstruct(engineConfig), + obj.getString("columnName"), + obj.getString("service"), + obj.getString("identifierSpace"), + obj.getString("schemaSpace") + ); + } + + @Override + public void write(JSONWriter writer, Properties options) + throws JSONException { + writer.object(); + writer.key("op"); writer.value(OperationRegistry.s_opClassToName.get(this.getClass())); + writer.key("description"); writer.value(getBriefDescription(null)); + writer.key("engineConfig"); getEngineConfig().write(writer, options); + writer.key("columnName"); writer.value(_columnName); + writer.key("service"); writer.value(service); + writer.key("schemaSpace"); writer.value(schemaSpace); + writer.key("identifierSpace"); writer.value(identifierSpace); + writer.endObject(); + } + + @Override + public String getBriefDescription(Project project) { + return "Use values as reconciliation identifiers in column " + _columnName; + } + + @Override + protected RowVisitor createRowVisitor(Project project, List cellChanges, long historyEntryID) + throws Exception { + Column column = project.columnModel.getColumnByName(_columnName); + + return new RowVisitor() { + int cellIndex; + List cellChanges; + long historyEntryID; + + public RowVisitor init(int cellIndex, List cellChanges, long historyEntryID) { + this.cellIndex = cellIndex; + this.cellChanges = cellChanges; + this.historyEntryID = historyEntryID; + return this; + } + + @Override + public void start(Project project) { + // nothing to do + } + + @Override + public void end(Project project) { + // nothing to do + } + + @Override + public boolean visit(Project project, int rowIndex, Row row) { + Cell cell = row.getCell(cellIndex); + if (cell != null && ExpressionUtils.isNonBlankData(cell.value)) { + String id = cell.value.toString(); + + ReconCandidate match = new ReconCandidate(id, id, new String[0], 100); + Recon newRecon = reconConfig.createNewRecon(historyEntryID); + newRecon.match = match; + newRecon.candidates = Collections.singletonList(match); + newRecon.matchRank = -1; + newRecon.judgment = Judgment.Matched; + newRecon.judgmentAction = "mass"; + newRecon.judgmentBatchSize = 1; + + Cell newCell = new Cell( + cell.value, + newRecon + ); + + CellChange cellChange = new CellChange(rowIndex, cellIndex, cell, newCell); + cellChanges.add(cellChange); + } + return false; + } + }.init(column.getCellIndex(), cellChanges, historyEntryID); + } + + @Override + protected String createDescription(Column column, List cellChanges) { + return "Use values as reconciliation identifiers for "+ cellChanges.size() + + " cells in column " + column.getName(); + } + + @Override + protected Change createChange(Project project, Column column, List cellChanges) { + return new ReconChange( + cellChanges, + _columnName, + reconConfig, + null + ); + } + +} diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js index 55ca73bcd..6c0d531e3 100644 --- a/main/webapp/modules/core/MOD-INF/controller.js +++ b/main/webapp/modules/core/MOD-INF/controller.js @@ -127,6 +127,7 @@ function registerCommands() { RS.registerCommand(module, "recon-clear-one-cell", new Packages.com.google.refine.commands.recon.ReconClearOneCellCommand()); RS.registerCommand(module, "recon-clear-similar-cells", new Packages.com.google.refine.commands.recon.ReconClearSimilarCellsCommand()); RS.registerCommand(module, "recon-copy-across-columns", new Packages.com.google.refine.commands.recon.ReconCopyAcrossColumnsCommand()); + RS.registerCommand(module, "recon-use-values-as-identifiers", new Packages.com.google.refine.commands.recon.ReconUseValuesAsIdentifiersCommand()); RS.registerCommand(module, "preview-extend-data", new Packages.com.google.refine.commands.recon.PreviewExtendDataCommand()); RS.registerCommand(module, "extend-data", new Packages.com.google.refine.commands.recon.ExtendDataCommand()); @@ -190,6 +191,7 @@ function registerOperations() { OR.registerOperation(module, "recon-clear-similar-cells", Packages.com.google.refine.operations.recon.ReconClearSimilarCellsOperation); OR.registerOperation(module, "recon-copy-across-columns", Packages.com.google.refine.operations.recon.ReconCopyAcrossColumnsOperation); OR.registerOperation(module, "extend-reconciled-data", Packages.com.google.refine.operations.recon.ExtendDataOperation); + OR.registerOperation(module, "recon-use-values-as-identifiers", Packages.com.google.refine.operations.recon.ReconUseValuesAsIdentifiersOperation); } function registerImporting() { From 5f551370b386a9d3c483996871252e13e11cda42 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 2 Oct 2018 12:32:30 +0100 Subject: [PATCH 12/18] Tests for UseValuesAsIdentifiers operation --- .../recon/ReconUseValuesAsIdsOperation.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 main/tests/server/src/com/google/refine/tests/operations/recon/ReconUseValuesAsIdsOperation.java diff --git a/main/tests/server/src/com/google/refine/tests/operations/recon/ReconUseValuesAsIdsOperation.java b/main/tests/server/src/com/google/refine/tests/operations/recon/ReconUseValuesAsIdsOperation.java new file mode 100644 index 000000000..2b11c86a3 --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/operations/recon/ReconUseValuesAsIdsOperation.java @@ -0,0 +1,58 @@ +package com.google.refine.tests.operations.recon; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.util.Properties; + +import org.json.JSONException; +import org.json.JSONObject; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; + +import com.google.refine.model.Project; +import com.google.refine.model.recon.StandardReconConfig; +import com.google.refine.operations.OperationRegistry; +import com.google.refine.operations.recon.ReconUseValuesAsIdentifiersOperation; +import com.google.refine.tests.RefineTest; +import com.google.refine.tests.util.TestUtils; + + +public class ReconUseValuesAsIdsOperation extends RefineTest { + String json = "{" + + "\"op\":\"core/recon-use-values-as-identifiers\"," + + "\"description\":\"Use values as reconciliation identifiers in column ids\"," + + "\"columnName\":\"ids\"," + + "\"engineConfig\":{\"mode\":\"row-based\",\"facets\":[]}," + + "\"service\":\"http://localhost:8080/api\"," + + "\"identifierSpace\":\"http://test.org/entities\"," + + "\"schemaSpace\":\"http://test.org/schema\"" + + "}"; + + @BeforeSuite + public void registerOperation() { + OperationRegistry.registerOperation(getCoreModule(), "recon-use-values-as-identifiers", ReconUseValuesAsIdentifiersOperation.class); + } + + @Test + public void serializeReconUseValuesAsIdentifiersOperation() throws JSONException, Exception { + TestUtils.isSerializedTo(ReconUseValuesAsIdentifiersOperation.reconstruct(new JSONObject(json)), json); + } + + @Test + public void testUseValuesAsIds() throws JSONException, Exception { + Project project = createCSVProject("ids,v\n" + + "Q343,hello\n" + + ",world\n" + + "Q31,test"); + ReconUseValuesAsIdentifiersOperation op = ReconUseValuesAsIdentifiersOperation.reconstruct(new JSONObject(json)); + op.createProcess(project, new Properties()).performImmediate(); + + assertEquals("Q343", project.rows.get(0).cells.get(0).recon.match.id); + assertEquals("http://test.org/entities", project.rows.get(0).cells.get(0).recon.identifierSpace); + assertNull(project.rows.get(1).cells.get(0)); + assertEquals("Q31", project.rows.get(2).cells.get(0).recon.match.id); + assertEquals(2, project.columnModel.columns.get(0).getReconStats().matchedTopics); + assertEquals("http://test.org/schema", ((StandardReconConfig)project.columnModel.columns.get(0).getReconConfig()).schemaSpace); + } +} From 610a8b31007279862fbd72b93d0f6d5452382f72 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 2 Oct 2018 12:32:46 +0100 Subject: [PATCH 13/18] UI for UseValuesAsIdentifiers operation --- .../modules/core/langs/translation-en.json | 6 +- .../views/data-table/menu-reconcile.js | 61 +++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index dffddfe4d..3cfd081ed 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -674,7 +674,11 @@ "rows": "rows", "records": "records", "show": "Show", - "hide": "Hide" + "hide": "Hide", + "use-values-as-identifiers": "Use values as identifiers", + "use-values-as-identifiers2": "Mark cells as reconciled with their values as identifiers", + "choose-reconciliation-service": "Choose a reconciliation service", + "choose-reconciliation-service-alert": "Please choose a reconciliation service first." }, "core-buttons": { "cancel": "Cancel", diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js b/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js index 3c20e1d57..1f20d7510 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js @@ -126,6 +126,61 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { input.focus().data("suggest").textchange(); }; + var doUseValuesAsIdentifiers = function() { + var frame = DialogSystem.createDialog(); + frame.width("400px"); + + var header = $('
').addClass("dialog-header").text($.i18n._('core-views')["use-values-as-identifiers"]).appendTo(frame); + var body = $('
').addClass("dialog-body").appendTo(frame); + var footer = $('
').addClass("dialog-footer").appendTo(frame); + + $('

').text($.i18n._('core-views')["choose-reconciliation-service"]).appendTo(body); + var select = $('').appendTo(body); + var services = ReconciliationManager.getAllServices(); + for (var i = 0; i < services.length; i++) { + var service = services[i]; + console.log(service); + $('').attr('value', service.url) + .text(service.name) + .appendTo(select); + }; + + $('').text($.i18n._('core-buttons')["cancel"]).click(function() { + DialogSystem.dismissUntil(level - 1); + }).appendTo(footer); + $('').html($.i18n._('core-buttons')["ok"]).click(function() { + + var service = select.val(); + var identifierSpace = null; + var schemaSpace = null; + for(var i = 0; i < services.length; i++) { + if(services[i].url === service) { + identifierSpace = services[i].identifierSpace; + schemaSpace = services[i].schemaSpace; + } + } + if (identifierSpace === null) { + alert($.i18n._('core-views')["choose-reconciliation-service-alert"]); + } else { + Refine.postCoreProcess( + "recon-use-values-as-identifiers", + { + columnName: column.name, + service: service, + identifierSpace: identifierSpace, + schemaSpace: schemaSpace + }, + null, + { cellsChanged: true, columnStatsChanged: true } + ); + } + DialogSystem.dismissUntil(level - 1); + }).appendTo(footer); + + var level = DialogSystem.showDialog(frame); + } + + var doCopyAcrossColumns = function() { var frame = $(DOM.loadHTML("core", "scripts/views/data-table/copy-recon-across-columns-dialog.html")); var elmts = DOM.bind(frame); @@ -402,6 +457,12 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { label: $.i18n._('core-views')["copy-recon"], tooltip: $.i18n._('core-views')["copy-recon2"], click: doCopyAcrossColumns + }, + { + id: "core/use-values-as-identifiers", + label: $.i18n._('core-views')["use-values-as-identifiers"], + tooltip: $.i18n._('core-views')['use-values-as-identifiers2'], + click: doUseValuesAsIdentifiers } ]); }); From 630f119f1a05a037ad23f43809ca7b5b7e67ab8e Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Tue, 2 Oct 2018 10:39:16 +0000 Subject: [PATCH 14/18] Translated using Weblate (French) Currently translated at 99.2% (691 of 696 strings) Translation: OpenRefine/Translations Translate-URL: https://hosted.weblate.org/projects/openrefine/translations/fr/ --- main/webapp/modules/core/langs/translation-fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/webapp/modules/core/langs/translation-fr.json b/main/webapp/modules/core/langs/translation-fr.json index 571b310af..955153edc 100644 --- a/main/webapp/modules/core/langs/translation-fr.json +++ b/main/webapp/modules/core/langs/translation-fr.json @@ -406,7 +406,7 @@ "all": "Tous les encodages", "years-ago": "années avant", "week-ago": "semaine dernière", - "working": "En fonctionnement", + "working": "Veuillez patienter", "encoding": "Encodage", "months-ago": "mois avant", "yesterday": "hier", @@ -430,7 +430,7 @@ "match-identical": "Apparier toutes les cellules identiques", "join-cells": "Joindre les cellules multivaluées", "actions": "Actions", - "search-match": "Chercher les correspondances", + "search-match": "Chercher une correspondance", "filtered-cell": "Apparier toutes les cellules filtrées avec...", "bounded-log-facet": "Facette logarithmique de limite 1", "remove-col": "Supprimer cette colonne", From 8e7f45a20aabf34323c34488917600d19077f6d7 Mon Sep 17 00:00:00 2001 From: ssantos Date: Sun, 30 Sep 2018 07:36:19 +0000 Subject: [PATCH 15/18] Translated using Weblate (German) Currently translated at 100.0% (696 of 696 strings) Translation: OpenRefine/Translations Translate-URL: https://hosted.weblate.org/projects/openrefine/translations/de/ --- main/webapp/modules/core/langs/translation-de.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/main/webapp/modules/core/langs/translation-de.json b/main/webapp/modules/core/langs/translation-de.json index 5240bdec6..ca6ed8553 100644 --- a/main/webapp/modules/core/langs/translation-de.json +++ b/main/webapp/modules/core/langs/translation-de.json @@ -155,7 +155,9 @@ "lines-beg": "Zeile(n) am Dateianfang", "preserve-empty": "Leere Zeichenfolgen bewahren", "rows-data": "Datenzeile(n)", - "load-at-most": "Maximale Last" + "load-at-most": "Maximale Last", + "column-names-label": "Spaltennamen (durch Kommata getrennt)", + "column-names-optional": "Kommagetrennt" }, "core-dialogs": { "help": "Hilfe", From 61928a671b6bd7630ba993a5fa35bf6a4e7baf3c Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Fri, 5 Oct 2018 10:27:09 +0100 Subject: [PATCH 16/18] Codacy cleanup --- .../modules/core/scripts/views/data-table/menu-reconcile.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js b/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js index 1f20d7510..399b6309c 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-reconcile.js @@ -139,11 +139,10 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var services = ReconciliationManager.getAllServices(); for (var i = 0; i < services.length; i++) { var service = services[i]; - console.log(service); $('').attr('value', service.url) .text(service.name) .appendTo(select); - }; + } $('').text($.i18n._('core-buttons')["cancel"]).click(function() { DialogSystem.dismissUntil(level - 1); @@ -178,7 +177,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { }).appendTo(footer); var level = DialogSystem.showDialog(frame); - } + }; var doCopyAcrossColumns = function() { From 7ed8c8b7e70b0224663e5364e3988bdad0480169 Mon Sep 17 00:00:00 2001 From: Luca Martinelli Date: Tue, 9 Oct 2018 19:45:53 +0000 Subject: [PATCH 17/18] Added translation using Weblate (Italian) --- extensions/wikidata/module/langs/translation-it.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 extensions/wikidata/module/langs/translation-it.json diff --git a/extensions/wikidata/module/langs/translation-it.json b/extensions/wikidata/module/langs/translation-it.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/extensions/wikidata/module/langs/translation-it.json @@ -0,0 +1 @@ +{} From 22abbe795076d48d593ae76195a1a7b305192eb4 Mon Sep 17 00:00:00 2001 From: Luca Martinelli Date: Tue, 9 Oct 2018 19:46:48 +0000 Subject: [PATCH 18/18] Translated using Weblate (Italian) Currently translated at 29.8% (43 of 144 strings) Translation: OpenRefine/wikidata Translate-URL: https://hosted.weblate.org/projects/openrefine/wikidata/it/ --- .../wikidata/module/langs/translation-it.json | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/extensions/wikidata/module/langs/translation-it.json b/extensions/wikidata/module/langs/translation-it.json index 0967ef424..3b039bfb1 100644 --- a/extensions/wikidata/module/langs/translation-it.json +++ b/extensions/wikidata/module/langs/translation-it.json @@ -1 +1,50 @@ -{} +{ + "wikidata-extension": { + "edit-wikidata-schema": "Modifica lo schema per Wikidata", + "manage-wikidata-account": "Gestisci il tuo account su Wikidata", + "perform-edits-on-wikidata": "Carica le tue modifiche su Wikidata", + "export-to-qs": "Esporta verso QuickStatements", + "quickstatements-export-name": "QuickStatements" + }, + "wikidata-schema": { + "dialog-explanation": "Lo schema per Wikidata ti permette di trasformare i dati nella tua tabella in modifiche pronte per Wikidata. Puoi trascinare i nomi delle colonne nei box di testo appropriati: in questo modo, per ogni riga verranno generate delle modifiche su Wikidata con i valori presenti.", + "preview-explanation": "Questa pagina mostra solo i primi risultati (su {nb_edits}) delle modifiche che verranno effettuate su Wikidata. Puoi usare le faccette per controllare le singole modifiche sugli elementi.", + "schema-tab-header": "Schema", + "warnings-tab-header": "Problemi", + "edits-preview-tab-header": "Anteprima", + "statements-header": "Dichiarazioni", + "terms-header": "Termini", + "empty-statements": "Nessuna dichiarazione generata", + "empty-terms": "Nessuna etichetta, descrizione o alias aggiunto", + "add-item-button": "aggiungi elemento", + "add-term": "aggiungi termine", + "remove": "rimuovi", + "add-statement": "aggiungi dichiarazione", + "add-value": "aggiungi valore", + "add-qualifier": "aggiungi qualificatore", + "add-reference": "aggiungi fonte", + "add-reference-snak": "aggiungi", + "property-placeholder": "proprietà", + "nb-references": " note", + "remove-column": "rimuovi colonna", + "label": "Etichetta", + "description": "Descrizione", + "alias": "Alias", + "item-or-reconciled-column": "digita l'elemento o trascina una colonna riconciliata", + "amount": "quantità", + "unit": "unità", + "full-url": "URL completo (compreso http:// o https://)", + "tabular-data-with-prefix": "nome del file (compreso \"Data:\")", + "commons-media": "nome del file", + "math-expression": "espressione matematica", + "geoshape-with-prefix": "nome del file (compreso \"Data:\")", + "datatype-not-supported-yet": "Ci dispiace, questo tipo di dato non è ancora supportato.", + "invalid-schema-warning-issues": "Il tuo schema è incompleto, per favore correggilo per vedere gli errori.", + "invalid-schema-warning-preview": "Il tuo schema è incompleto, per favore correggilo per vedere l'anteprima.", + "discard-button": "Non salvare le modifiche", + "save-button": "Salva lo schema", + "close-button": "Chiudi", + "unsaved-changes-alt": "Non hai salvato alcune modifiche al tuo schema Wikidata.", + "save-schema-alt": "Salva lo schema. Non verrà ancora effettuata alcuna modifica su Wikidata." + } +}