Skip to content

Commit 539fadd

Browse files
committed
feat: download Word Picture results as CSV
1 parent 54bee61 commit 539fadd

File tree

6 files changed

+67
-2
lines changed

6 files changed

+67
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
## [Unreleased]
44

5-
## Fixed
5+
### Added
6+
7+
- Download Word Picture results as CSV [#200](https://github.com/spraakbanken/korp-frontend/issues/200)
8+
9+
### Fixed
610

711
- KWIC download sometimes fails because row.structs is optional [#492](https://github.com/spraakbanken/korp-frontend/issues/492)
812

app/scripts/components/wordpicture/results-word-picture.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "@/components/util/json_button"
99
import "@/components/util/korp-error"
1010
import "@/components/wordpicture/word-picture"
1111
import { StoreService } from "@/services/store"
12+
import { CsvType, downloadCsvFile } from "@/csv"
1213

1314
type ResultsWordPictureController = IController & {
1415
isActive: boolean
@@ -19,6 +20,7 @@ type ResultsWordPictureController = IController & {
1920
type ResultsWordPictureScope = IScope & {
2021
activated: boolean
2122
data?: WordPicture
23+
downloadOption: CsvType | ""
2224
error?: string
2325
limit: string // Number as string to work with <select ng-model>
2426
limitOptions: number[]
@@ -75,6 +77,12 @@ angular.module("korpApp").component("resultsWordPicture", {
7577
</help-box>
7678
7779
<div class="mt-4 flex gap-4 justify-end">
80+
<select ng-show="!$ctrl.loading && data && !warning && !error" ng-model="downloadOption">
81+
<option value="">{{ "download" | loc:$root.lang }}</option>
82+
<option value="comma">{{ "csv_comma" | loc:$root.lang }}</option>
83+
<option value="tab">{{ "csv_tab" | loc:$root.lang }}</option>
84+
</select>
85+
7886
<json-button
7987
ng-if="!$ctrl.loading && data && !warning && !error"
8088
endpoint="relations"
@@ -94,6 +102,7 @@ angular.module("korpApp").component("resultsWordPicture", {
94102
function ($scope: ResultsWordPictureScope, $timeout: ITimeoutService, store: StoreService) {
95103
const $ctrl = this as ResultsWordPictureController
96104
$scope.activated = false
105+
$scope.downloadOption = ""
97106
$scope.limit = String(LIMITS[0])
98107
$scope.limitOptions = [...LIMITS]
99108
$scope.proxy = new RelationsProxy()
@@ -185,6 +194,19 @@ angular.module("korpApp").component("resultsWordPicture", {
185194
})
186195
.finally(() => $timeout(() => $ctrl.setProgress(false, 0)))
187196
}
197+
198+
// Create and download CSV file when the download selector is used
199+
$scope.$watch("downloadOption", () => {
200+
// Skip if empty (at init or if the label option is selected)
201+
if (!$scope.downloadOption) return
202+
203+
if (!$scope.data) throw new Error("Word picture data missing")
204+
const rows = $scope.data.generateCsv()
205+
downloadCsvFile("word-picture", rows, $scope.downloadOption)
206+
207+
// Reset to the label option
208+
$scope.downloadOption = ""
209+
})
188210
},
189211
],
190212
})

app/scripts/csv.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import CSV from "comma-separated-values/csv"
2+
import { downloadFile } from "./util"
3+
4+
export type CsvType = keyof typeof CSV_TYPES
5+
6+
export const CSV_TYPES = {
7+
comma: { delimiter: ",", ext: "csv", mime: "text/csv" },
8+
semi: { delimiter: ";", ext: "csv", mime: "text/csv" },
9+
tab: { delimiter: "\t", ext: "tsv", mime: "text/tab-separated-values" },
10+
}
11+
12+
/** Create a CSV file and trigger a download */
13+
export function downloadCsvFile(name: string, rows: Iterable<string[]>, type: CsvType) {
14+
const { delimiter, ext, mime } = CSV_TYPES[type]
15+
const csv = CSV.encode([...rows], { delimiter })
16+
downloadFile(csv, `korp-${name}.${ext}`, mime)
17+
}

app/scripts/word-picture.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,5 +117,19 @@ export class WordPicture {
117117
}
118118

119119
/** Get a string for the params that identify a word picture column */
120-
getColumnId = (pos: string, rel: string, reverse: boolean) => `${pos}${reverse ? "+" : "-"}${rel}`
120+
getColumnId = (pos: string, rel: string, reverse: boolean) => `${pos}${reverse ? "+" : "-"}${rel}`;
121+
122+
/** Create listing of full data, suitable for CSV export. */
123+
*generateCsv(): Generator<string[]> {
124+
const fields: (keyof MatchedRelation)[] = ["head", "headpos", "dep", "deppos", "rel", "depextra", "freq", "mi"]
125+
// Header row
126+
yield fields
127+
128+
// Data rows
129+
for (const key of Object.keys(this.items)) {
130+
for (const relation of this.items[key]) {
131+
yield fields.map((field) => String(relation[field] || ""))
132+
}
133+
}
134+
}
121135
}

app/translations/locale-eng.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@
341341
"statstable_gen_export": "Generate export",
342342
"statstable_download": "Download",
343343

344+
"csv_comma": "Comma-separated values (CSV)",
345+
"csv_semi": "Semicolon-separated values (CSV)",
346+
"csv_tab": "Tab-separated values (TSV)",
347+
344348
"util_decimalseparator": ".",
345349
"util_numbergroupseparator": ",",
346350

app/translations/locale-swe.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,10 @@
342342
"statstable_gen_export": "Generera export",
343343
"statstable_download": "Ladda ner",
344344

345+
"csv_comma": "Kommaseparerade värden (CSV)",
346+
"csv_semi": "Semikolonseparerade värden (CSV)",
347+
"csv_tab": "Tabbseparerade värden (TSV)",
348+
345349
"util_decimalseparator": ",",
346350
"util_numbergroupseparator": "&nbsp;",
347351

0 commit comments

Comments
 (0)