aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-08 21:15:20 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-05-08 21:15:20 +0000
commit97f0b089da0d9c4fe8aa2532b5f61d2615f6bf65 (patch)
treec3de814058820576d1af42f000958599b6b5dcd6
parentdaeec365ffb755c955382a5ece4a1235b9958c35 (diff)
rebaseline_server JSON: pass category values as values, not keys
BUG=skia:1758 NOTRY=True R=borenet@google.com Author: epoger@google.com Review URL: https://codereview.chromium.org/270413002 git-svn-id: http://skia.googlecode.com/svn/trunk@14658 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--gm/rebaseline_server/column.py25
-rwxr-xr-xgm/rebaseline_server/compare_to_expectations.py2
-rw-r--r--gm/rebaseline_server/imagepairset.py3
-rwxr-xr-xgm/rebaseline_server/imagepairset_test.py4
-rwxr-xr-xgm/rebaseline_server/results.py2
-rw-r--r--gm/rebaseline_server/static/constants.js14
-rw-r--r--gm/rebaseline_server/static/loader.js32
-rw-r--r--gm/rebaseline_server/static/view.html20
-rw-r--r--gm/rebaseline_server/testdata/outputs/expected/compare_configs_test.CompareConfigsTest.test_gm/gm.json93
-rw-r--r--gm/rebaseline_server/testdata/outputs/expected/compare_rendered_pictures_test.CompareRenderedPicturesTest.test_endToEnd/compare_rendered_pictures.json63
-rw-r--r--gm/rebaseline_server/testdata/outputs/expected/compare_to_expectations_test.CompareToExpectationsTest.test_gm/gm.json113
11 files changed, 263 insertions, 108 deletions
diff --git a/gm/rebaseline_server/column.py b/gm/rebaseline_server/column.py
index d8d119d130..26597c37d5 100644
--- a/gm/rebaseline_server/column.py
+++ b/gm/rebaseline_server/column.py
@@ -11,11 +11,12 @@ ColumnHeaderFactory class (see class docstring for details)
# Keys used within dictionary representation of each column header.
# NOTE: Keep these in sync with static/constants.js
-KEY__HEADER_TEXT = 'headerText'
-KEY__HEADER_URL = 'headerUrl'
-KEY__IS_FILTERABLE = 'isFilterable'
-KEY__IS_SORTABLE = 'isSortable'
-KEY__VALUES_AND_COUNTS = 'valuesAndCounts'
+KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
+KEY__EXTRACOLUMNHEADERS__HEADER_TEXT = 'headerText'
+KEY__EXTRACOLUMNHEADERS__HEADER_URL = 'headerUrl'
+KEY__EXTRACOLUMNHEADERS__IS_FILTERABLE = 'isFilterable'
+KEY__EXTRACOLUMNHEADERS__IS_SORTABLE = 'isSortable'
+KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS = 'valuesAndCounts'
class ColumnHeaderFactory(object):
@@ -46,7 +47,8 @@ class ColumnHeaderFactory(object):
"""Creates the header for this column, in dictionary form.
Creates the header for this column in dictionary form, as needed when
- constructing the JSON representation. Uses the KEY__* constants as keys.
+ constructing the JSON representation. Uses the KEY__EXTRACOLUMNHEADERS__*
+ constants as keys.
Args:
values_and_counts_dict: dictionary mapping each possible column value
@@ -54,12 +56,13 @@ class ColumnHeaderFactory(object):
None if this information is not available.
"""
asdict = {
- KEY__HEADER_TEXT: self._header_text,
- KEY__IS_FILTERABLE: self._is_filterable,
- KEY__IS_SORTABLE: self._is_sortable,
+ KEY__EXTRACOLUMNHEADERS__HEADER_TEXT: self._header_text,
+ KEY__EXTRACOLUMNHEADERS__IS_FILTERABLE: self._is_filterable,
+ KEY__EXTRACOLUMNHEADERS__IS_SORTABLE: self._is_sortable,
}
if self._header_url:
- asdict[KEY__HEADER_URL] = self._header_url
+ asdict[KEY__EXTRACOLUMNHEADERS__HEADER_URL] = self._header_url
if self._include_values_and_counts and values_and_counts_dict:
- asdict[KEY__VALUES_AND_COUNTS] = values_and_counts_dict
+ asdict[KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS] = sorted(
+ values_and_counts_dict.items())
return asdict
diff --git a/gm/rebaseline_server/compare_to_expectations.py b/gm/rebaseline_server/compare_to_expectations.py
index ab05f6f51b..cddca55d2a 100755
--- a/gm/rebaseline_server/compare_to_expectations.py
+++ b/gm/rebaseline_server/compare_to_expectations.py
@@ -316,6 +316,8 @@ class ExpectationComparisons(results.BaseComparisons):
# categories recorded within the gm_actuals AT ALL, and
# instead evaluate the result_type ourselves based on what
# we see in expectations vs actual checksum?
+ # See related http://skbug.com/2514 ('rebaseline_server: apply
+ # ignored-tests.txt within rebaseline_server, not just on the bots')
if expected_image_relative_url == actual_image_relative_url:
updated_result_type = results.KEY__RESULT_TYPE__SUCCEEDED
else:
diff --git a/gm/rebaseline_server/imagepairset.py b/gm/rebaseline_server/imagepairset.py
index 04aea90342..880987703d 100644
--- a/gm/rebaseline_server/imagepairset.py
+++ b/gm/rebaseline_server/imagepairset.py
@@ -17,7 +17,6 @@ import column
# Keys used within dictionary representation of ImagePairSet.
# NOTE: Keep these in sync with static/constants.js
-KEY__EXTRACOLUMNHEADERS = 'extraColumnHeaders'
KEY__IMAGEPAIRS = 'imagePairs'
KEY__IMAGESETS = 'imageSets'
KEY__IMAGESETS__FIELD__BASE_URL = 'baseUrl'
@@ -141,7 +140,7 @@ class ImagePairSet(object):
key_description = KEY__IMAGESETS__FIELD__DESCRIPTION
key_base_url = KEY__IMAGESETS__FIELD__BASE_URL
return {
- KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
+ column.KEY__EXTRACOLUMNHEADERS: self._column_headers_as_dict(),
KEY__IMAGEPAIRS: self._image_pair_dicts,
KEY__IMAGESETS: {
KEY__IMAGESETS__SET__IMAGE_A: {
diff --git a/gm/rebaseline_server/imagepairset_test.py b/gm/rebaseline_server/imagepairset_test.py
index c046ec7c7c..d7aeb64fea 100755
--- a/gm/rebaseline_server/imagepairset_test.py
+++ b/gm/rebaseline_server/imagepairset_test.py
@@ -91,9 +91,7 @@ class ImagePairSetTest(unittest.TestCase):
'headerText': 'builder',
'isFilterable': True,
'isSortable': True,
- 'valuesAndCounts': {
- 'MyBuilder': 3
- },
+ 'valuesAndCounts': [('MyBuilder', 3)],
},
'test': {
'headerText': 'which GM test',
diff --git a/gm/rebaseline_server/results.py b/gm/rebaseline_server/results.py
index 461e7463a6..254b0a3c84 100755
--- a/gm/rebaseline_server/results.py
+++ b/gm/rebaseline_server/results.py
@@ -31,7 +31,7 @@ import imagepairset
# Keys used to link an image to a particular GM test.
# NOTE: Keep these in sync with static/constants.js
-REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 2
+REBASELINE_SERVER_SCHEMA_VERSION_NUMBER = 3
KEY__EXPECTATIONS__BUGS = gm_json.JSONKEY_EXPECTEDRESULTS_BUGS
KEY__EXPECTATIONS__IGNOREFAILURE = gm_json.JSONKEY_EXPECTEDRESULTS_IGNOREFAILURE
KEY__EXPECTATIONS__REVIEWED = gm_json.JSONKEY_EXPECTEDRESULTS_REVIEWED
diff --git a/gm/rebaseline_server/static/constants.js b/gm/rebaseline_server/static/constants.js
index 87a07752a3..55dddbf749 100644
--- a/gm/rebaseline_server/static/constants.js
+++ b/gm/rebaseline_server/static/constants.js
@@ -9,11 +9,12 @@ var module = angular.module(
module.constant('constants', (function() {
return {
// NOTE: Keep these in sync with ../column.py
- KEY__HEADER_TEXT: 'headerText',
- KEY__HEADER_URL: 'headerUrl',
- KEY__IS_FILTERABLE: 'isFilterable',
- KEY__IS_SORTABLE: 'isSortable',
- KEY__VALUES_AND_COUNTS: 'valuesAndCounts',
+ KEY__EXTRACOLUMNHEADERS: 'extraColumnHeaders',
+ KEY__EXTRACOLUMNHEADERS__HEADER_TEXT: 'headerText',
+ KEY__EXTRACOLUMNHEADERS__HEADER_URL: 'headerUrl',
+ KEY__EXTRACOLUMNHEADERS__IS_FILTERABLE: 'isFilterable',
+ KEY__EXTRACOLUMNHEADERS__IS_SORTABLE: 'isSortable',
+ KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS: 'valuesAndCounts',
// NOTE: Keep these in sync with ../imagediffdb.py
KEY__DIFFERENCE_DATA__MAX_DIFF_PER_CHANNEL: 'maxDiffPerChannel',
@@ -31,7 +32,6 @@ module.constant('constants', (function() {
KEY__IS_DIFFERENT: 'isDifferent',
// NOTE: Keep these in sync with ../imagepairset.py
- KEY__EXTRACOLUMNHEADERS: 'extraColumnHeaders',
KEY__IMAGEPAIRS: 'imagePairs',
KEY__IMAGESETS: 'imageSets',
KEY__IMAGESETS__FIELD__BASE_URL: 'baseUrl',
@@ -42,7 +42,7 @@ module.constant('constants', (function() {
KEY__IMAGESETS__SET__WHITEDIFFS: 'whiteDiffs',
// NOTE: Keep these in sync with ../results.py
- REBASELINE_SERVER_SCHEMA_VERSION_NUMBER: 2,
+ REBASELINE_SERVER_SCHEMA_VERSION_NUMBER: 3,
KEY__EXPECTATIONS__BUGS: 'bugs',
KEY__EXPECTATIONS__IGNOREFAILURE: 'ignore-failure',
KEY__EXPECTATIONS__REVIEWED: 'reviewed-by-human',
diff --git a/gm/rebaseline_server/static/loader.js b/gm/rebaseline_server/static/loader.js
index dc33bd0a93..f5a4f7bf4d 100644
--- a/gm/rebaseline_server/static/loader.js
+++ b/gm/rebaseline_server/static/loader.js
@@ -148,13 +148,15 @@ Loader.controller(
constants.KEY__RESULT_TYPE__NOCOMPARISON] = true;
$scope.hiddenResultTypes[
constants.KEY__RESULT_TYPE__SUCCEEDED] = true;
- $scope.allResultTypes = Object.keys(
+ $scope.allResultTypes = $scope.columnSliceOf2DArray(
$scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE]
- [constants.KEY__VALUES_AND_COUNTS]);
+ [constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS],
+ 0);
$scope.hiddenConfigs = {};
- $scope.allConfigs = Object.keys(
+ $scope.allConfigs = $scope.columnSliceOf2DArray(
$scope.extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG]
- [constants.KEY__VALUES_AND_COUNTS]);
+ [constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS],
+ 0);
// Associative array of partial string matches per category.
$scope.categoryValueMatch = {};
@@ -744,6 +746,28 @@ Loader.controller(
//
/**
+ * Returns a single "column slice" of a 2D array.
+ *
+ * For example, if array is:
+ * [[A0, A1],
+ * [B0, B1],
+ * [C0, C1]]
+ * and index is 0, this this will return:
+ * [A0, B0, C0]
+ *
+ * @param array a Javascript Array
+ * @param column (numeric): index within each row array
+ */
+ $scope.columnSliceOf2DArray = function(array, column) {
+ var slice = [];
+ var numRows = array.length;
+ for (var row = 0; row < numRows; row++) {
+ slice.push(array[row][column]);
+ }
+ return slice;
+ }
+
+ /**
* Returns a human-readable (in local time zone) time string for a
* particular moment in time.
*
diff --git a/gm/rebaseline_server/static/view.html b/gm/rebaseline_server/static/view.html
index bbe1f210f3..b2f3fba4f3 100644
--- a/gm/rebaseline_server/static/view.html
+++ b/gm/rebaseline_server/static/view.html
@@ -68,13 +68,13 @@
<tr valign="top">
<td>
resultType<br>
- <label ng-repeat="(resultType, count) in extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE][constants.KEY__VALUES_AND_COUNTS] track by $index">
+ <label ng-repeat="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMN__RESULT_TYPE][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]">
<input type="checkbox"
name="resultTypes"
- value="{{resultType}}"
- ng-checked="!isValueInSet(resultType, hiddenResultTypes)"
- ng-click="toggleValueInSet(resultType, hiddenResultTypes); setUpdatesPending(true)">
- {{resultType}} ({{count}})<br>
+ value="{{valueAndCount[0]}}"
+ ng-checked="!isValueInSet(valueAndCount[0], hiddenResultTypes)"
+ ng-click="toggleValueInSet(valueAndCount[0], hiddenResultTypes); setUpdatesPending(true)">
+ {{valueAndCount[0]}} ({{valueAndCount[1]}})<br>
</label>
<button ng-click="hiddenResultTypes = {}; updateResults()">
all
@@ -100,13 +100,13 @@
</td>
<td>
config<br>
- <label ng-repeat="(config, count) in extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG][constants.KEY__VALUES_AND_COUNTS] track by $index">
+ <label ng-repeat="valueAndCount in extraColumnHeaders[constants.KEY__EXTRACOLUMN__CONFIG][constants.KEY__EXTRACOLUMNHEADERS__VALUES_AND_COUNTS]">
<input type="checkbox"
name="configs"
- value="{{config}}"
- ng-checked="!isValueInSet(config, hiddenConfigs)"
- ng-click="toggleValueInSet(config, hiddenConfigs); setUpdatesPending(true)">
- {{config}} ({{count}})<br>
+ value="{{valueAndCount[0]}}"
+ ng-checked="!isValueInSet(valueAndCount[0], hiddenConfigs)"
+ ng-click="toggleValueInSet(valueAndCount[0], hiddenConfigs); setUpdatesPending(true)">
+ {{valueAndCount[0]}} ({{valueAndCount[1]}})<br>
</label>
<button ng-click="hiddenConfigs = {}; updateResults()">
all
diff --git a/gm/rebaseline_server/testdata/outputs/expected/compare_configs_test.CompareConfigsTest.test_gm/gm.json b/gm/rebaseline_server/testdata/outputs/expected/compare_configs_test.CompareConfigsTest.test_gm/gm.json
index aabbdbf859..6d63113f37 100644
--- a/gm/rebaseline_server/testdata/outputs/expected/compare_configs_test.CompareConfigsTest.test_gm/gm.json
+++ b/gm/rebaseline_server/testdata/outputs/expected/compare_configs_test.CompareConfigsTest.test_gm/gm.json
@@ -4,51 +4,96 @@
"headerText": "builder",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "Test-Android-GalaxyNexus-SGX540-Arm7-Release": 7,
- "Test-Builder-We-Have-No-Expectations-File-For": 3,
- "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug": 3
- }
+ "valuesAndCounts": [
+ [
+ "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ 7
+ ],
+ [
+ "Test-Builder-We-Have-No-Expectations-File-For",
+ 3
+ ],
+ [
+ "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ 3
+ ]
+ ]
},
"config": {
"headerText": "config",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "TODO": 13
- }
+ "valuesAndCounts": [
+ [
+ "TODO",
+ 13
+ ]
+ ]
},
"resultType": {
"headerText": "resultType",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "failed": 4,
- "no-comparison": 7,
- "succeeded": 2
- }
+ "valuesAndCounts": [
+ [
+ "failed",
+ 4
+ ],
+ [
+ "no-comparison",
+ 7
+ ],
+ [
+ "succeeded",
+ 2
+ ]
+ ]
},
"test": {
"headerText": "test",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "3x3bitmaprect": 3,
- "aaclip": 1,
- "bigblurs": 3,
- "bitmapsource": 1,
- "displacement": 2,
- "filterbitmap_checkerboard_192_192": 1,
- "filterbitmap_checkerboard_32_2": 1,
- "texdata": 1
- }
+ "valuesAndCounts": [
+ [
+ "3x3bitmaprect",
+ 3
+ ],
+ [
+ "aaclip",
+ 1
+ ],
+ [
+ "bigblurs",
+ 3
+ ],
+ [
+ "bitmapsource",
+ 1
+ ],
+ [
+ "displacement",
+ 2
+ ],
+ [
+ "filterbitmap_checkerboard_192_192",
+ 1
+ ],
+ [
+ "filterbitmap_checkerboard_32_2",
+ 1
+ ],
+ [
+ "texdata",
+ 1
+ ]
+ ]
}
},
"header": {
"dataHash": "-7043844904261310530",
"isEditable": false,
"isExported": true,
- "schemaVersion": 2,
+ "schemaVersion": 3,
"timeNextUpdateAvailable": null,
"timeUpdated": 12345678,
"type": "all"
diff --git a/gm/rebaseline_server/testdata/outputs/expected/compare_rendered_pictures_test.CompareRenderedPicturesTest.test_endToEnd/compare_rendered_pictures.json b/gm/rebaseline_server/testdata/outputs/expected/compare_rendered_pictures_test.CompareRenderedPicturesTest.test_endToEnd/compare_rendered_pictures.json
index fbe48c088d..464b1d4e94 100644
--- a/gm/rebaseline_server/testdata/outputs/expected/compare_rendered_pictures_test.CompareRenderedPicturesTest.test_endToEnd/compare_rendered_pictures.json
+++ b/gm/rebaseline_server/testdata/outputs/expected/compare_rendered_pictures_test.CompareRenderedPicturesTest.test_endToEnd/compare_rendered_pictures.json
@@ -4,45 +4,72 @@
"headerText": "builder",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "TODO": 4
- }
+ "valuesAndCounts": [
+ [
+ "TODO",
+ 4
+ ]
+ ]
},
"config": {
"headerText": "config",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "whole-image": 4
- }
+ "valuesAndCounts": [
+ [
+ "whole-image",
+ 4
+ ]
+ ]
},
"resultType": {
"headerText": "resultType",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "failed": 1,
- "no-comparison": 2,
- "succeeded": 1
- }
+ "valuesAndCounts": [
+ [
+ "failed",
+ 1
+ ],
+ [
+ "no-comparison",
+ 2
+ ],
+ [
+ "succeeded",
+ 1
+ ]
+ ]
},
"test": {
"headerText": "test",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "changed.skp": 1,
- "only-in-after.skp": 1,
- "only-in-before.skp": 1,
- "unchanged.skp": 1
- }
+ "valuesAndCounts": [
+ [
+ "changed.skp",
+ 1
+ ],
+ [
+ "only-in-after.skp",
+ 1
+ ],
+ [
+ "only-in-before.skp",
+ 1
+ ],
+ [
+ "unchanged.skp",
+ 1
+ ]
+ ]
}
},
"header": {
"dataHash": "-595743736412687673",
"isEditable": false,
"isExported": true,
- "schemaVersion": 2,
+ "schemaVersion": 3,
"timeNextUpdateAvailable": null,
"timeUpdated": 12345678,
"type": "all"
diff --git a/gm/rebaseline_server/testdata/outputs/expected/compare_to_expectations_test.CompareToExpectationsTest.test_gm/gm.json b/gm/rebaseline_server/testdata/outputs/expected/compare_to_expectations_test.CompareToExpectationsTest.test_gm/gm.json
index f8774689fa..4f4eef7bf8 100644
--- a/gm/rebaseline_server/testdata/outputs/expected/compare_to_expectations_test.CompareToExpectationsTest.test_gm/gm.json
+++ b/gm/rebaseline_server/testdata/outputs/expected/compare_to_expectations_test.CompareToExpectationsTest.test_gm/gm.json
@@ -4,55 +4,112 @@
"headerText": "builder",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "Test-Android-GalaxyNexus-SGX540-Arm7-Release": 13,
- "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug": 15
- }
+ "valuesAndCounts": [
+ [
+ "Test-Android-GalaxyNexus-SGX540-Arm7-Release",
+ 13
+ ],
+ [
+ "Test-Mac10.7-MacMini4.1-GeForce320M-x86_64-Debug",
+ 15
+ ]
+ ]
},
"config": {
"headerText": "config",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "565": 9,
- "8888": 9,
- "gpu": 4,
- "pdf-mac": 3,
- "pdf-poppler": 3
- }
+ "valuesAndCounts": [
+ [
+ "565",
+ 9
+ ],
+ [
+ "8888",
+ 9
+ ],
+ [
+ "gpu",
+ 4
+ ],
+ [
+ "pdf-mac",
+ 3
+ ],
+ [
+ "pdf-poppler",
+ 3
+ ]
+ ]
},
"resultType": {
"headerText": "resultType",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "failed": 1,
- "failure-ignored": 4,
- "no-comparison": 9,
- "succeeded": 14
- }
+ "valuesAndCounts": [
+ [
+ "failed",
+ 1
+ ],
+ [
+ "failure-ignored",
+ 4
+ ],
+ [
+ "no-comparison",
+ 9
+ ],
+ [
+ "succeeded",
+ 14
+ ]
+ ]
},
"test": {
"headerText": "test",
"isFilterable": true,
"isSortable": true,
- "valuesAndCounts": {
- "3x3bitmaprect": 7,
- "aaclip": 2,
- "bigblurs": 7,
- "bitmapsource": 2,
- "displacement": 5,
- "filterbitmap_checkerboard_192_192": 2,
- "filterbitmap_checkerboard_32_2": 2,
- "texdata": 1
- }
+ "valuesAndCounts": [
+ [
+ "3x3bitmaprect",
+ 7
+ ],
+ [
+ "aaclip",
+ 2
+ ],
+ [
+ "bigblurs",
+ 7
+ ],
+ [
+ "bitmapsource",
+ 2
+ ],
+ [
+ "displacement",
+ 5
+ ],
+ [
+ "filterbitmap_checkerboard_192_192",
+ 2
+ ],
+ [
+ "filterbitmap_checkerboard_32_2",
+ 2
+ ],
+ [
+ "texdata",
+ 1
+ ]
+ ]
}
},
"header": {
"dataHash": "2775016045957284034",
"isEditable": false,
"isExported": true,
- "schemaVersion": 2,
+ "schemaVersion": 3,
"timeNextUpdateAvailable": null,
"timeUpdated": 12345678,
"type": "all"