Skip to content

Commit

Permalink
replace import/export format with JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
zhenghaoz committed Nov 12, 2024
1 parent 7837d69 commit 6e30641
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 749 deletions.
48 changes: 15 additions & 33 deletions src/views/Advance.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,35 +21,25 @@
<d-row>
<d-col sm="12" md="2">
<a :href="'/api/bulk/users'">
<d-button class="advance_button" outline
>&nbsp;&nbsp;&nbsp;Export
Users&nbsp;&nbsp;&nbsp;</d-button
>
<d-button class="advance_button" outline>&nbsp;&nbsp;&nbsp;Export
Users&nbsp;&nbsp;&nbsp;</d-button>
</a>
</d-col>
<d-col sm="12" md="10">
<label
>Export users into a csv file. The format is "user_id,
labels".</label
>
<label>Export users into JSON lines.</label>
</d-col>
</d-row>
</d-list-group-item>
<d-list-group-item class="p-3">
<d-row>
<d-col sm="12" md="2">
<a :href="'/api/bulk/items'">
<d-button class="advance_button" outline
>&nbsp;&nbsp;&nbsp;Export
Items&nbsp;&nbsp;&nbsp;</d-button
>
<d-button class="advance_button" outline>&nbsp;&nbsp;&nbsp;Export
Items&nbsp;&nbsp;&nbsp;</d-button>
</a>
</d-col>
<d-col sm="12" md="10">
<label
>Export items into a csv file. The format is "item_id, is_hidden, categories,
timestamp, labels, description".</label
>
<label>Export items into JSON lines.</label>
</d-col>
</d-row>
</d-list-group-item>
Expand All @@ -61,10 +51,7 @@
</a>
</d-col>
<d-col sm="12" md="10">
<label
>Export feedbacks into a csv file. The format is
"feedback_type, user_id, item_id, timestamp".</label
>
<label>Export feedbacks into JSON lines.</label>
</d-col>
</d-row>
</d-list-group-item>
Expand All @@ -86,10 +73,8 @@
<d-row>
<d-col sm="12" md="2">
<router-link :to="{ name: 'import_users' }">
<d-button outline
>&nbsp;&nbsp;&nbsp;Import
Users&nbsp;&nbsp;&nbsp;</d-button
>
<d-button outline>&nbsp;&nbsp;&nbsp;Import
Users&nbsp;&nbsp;&nbsp;</d-button>
</router-link>
</d-col>
<d-col sm="12" md="10">
Expand All @@ -101,10 +86,8 @@
<d-row>
<d-col sm="12" md="2">
<router-link :to="{ name: 'import_items' }">
<d-button outline
>&nbsp;&nbsp;&nbsp;Import
Items&nbsp;&nbsp;&nbsp;</d-button
>
<d-button outline>&nbsp;&nbsp;&nbsp;Import
Items&nbsp;&nbsp;&nbsp;</d-button>
</router-link>
</d-col>
<d-col sm="12" md="10">
Expand Down Expand Up @@ -141,16 +124,15 @@
<d-list-group-item class="p-3">
<d-row>
<d-col sm="12" md="2">
<d-button outline theme="danger" @click.native="handleClick"
>&nbsp;Purge
Database&nbsp;</d-button
>
<d-button outline theme="danger" @click.native="handleClick">&nbsp;Purge
Database&nbsp;</d-button>
<d-modal v-if="showDialog" @close="handleClose" centered>
<d-modal-header>
<d-modal-title>Are you absolutely sure?</d-modal-title>
</d-modal-header>
<d-modal-body>
<label>This action <span style="color: red; font-weight: bold">cannot</span> be undone. This will permanently:</label>
<label>This action <span style="color: red; font-weight: bold">cannot</span> be undone. This
will permanently:</label>
<d-checkbox v-model="checkList" value="delete_users">Delete all users.</d-checkbox>
<d-checkbox v-model="checkList" value="delete_items">Delete all items.</d-checkbox>
<d-checkbox v-model="checkList" value="delete_feedback">Delete all feedbacks.</d-checkbox>
Expand Down
233 changes: 25 additions & 208 deletions src/views/ImportFeedback.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,165 +7,46 @@
<h3 class="page-title">Import Feedback</h3>
</div>
</div>
<d-alert
:theme="alertTheme"
:show="timeUntilDismissed"
dismissible
@alert-dismissed="timeUntilDismissed = 0"
@alert-dismiss-countdown="handleTimeChange"
>{{ alertText }}</d-alert
>
<d-alert :theme="alertTheme" :show="timeUntilDismissed" dismissible @alert-dismissed="timeUntilDismissed = 0"
@alert-dismiss-countdown="handleTimeChange">{{ alertText }}</d-alert>
<div class="row">
<div class="col">
<div class="card card-small mb-4">
<div class="card-header">
<d-form validated>
<d-form validated @submit.prevent="uploadFile">
<d-form-row>
<d-col md="9" class="form-group">
<d-col md="10" class="form-group">
<strong class="text-muted d-block mb-2">File</strong>
<div class="custom-file mb-3">
<input
type="file"
class="custom-file-input"
id="csvFile"
@change="loadFile"
required
/>
<input type="file" class="custom-file-input" id="csvFile" @change="loadFile" required />
<label class="custom-file-label" for="customFile2">{{
fileName
}}</label>
<d-form-invalid-feedback
>Upload local csv file.</d-form-invalid-feedback
>
}}</label>
<d-form-invalid-feedback>Upload local csv file.</d-form-invalid-feedback>
</div>
</d-col>
<d-col md="3" class="form-group">
<label for="fieldSeparator">Field Separator</label>
<d-input
id="fieldSeparator"
type="text"
v-model="fieldSeparator"
required
/>
<d-form-invalid-feedback
>Field separator is required!</d-form-invalid-feedback
>
</d-col>
</d-form-row>
</d-form>
<d-form @submit.prevent="uploadFile">
<d-form-row>
<d-col md="3">
<strong class="text-muted d-block mb-2"
>Field Matching</strong
>
<d-input-group prepend="FeedbackType" class="mb-3">
<d-select v-model="feedbackTypeColIdx">
<option
v-for="(name, idx) in previewHeader"
:key="idx"
:value="idx"
>
{{ name }}
</option>
</d-select>
</d-input-group>
</d-col>
<d-col md="3">
<strong class="text-muted d-block mb-2">&nbsp;</strong>
<d-input-group prepend="UserId" class="mb-3">
<d-select v-model="userIdColIdx">
<option
v-for="(name, idx) in previewHeader"
:key="idx"
:value="idx"
>
{{ name }}
</option>
</d-select>
</d-input-group>
</d-col>
<d-col md="3">
<strong class="text-muted d-block mb-2">&nbsp;</strong>
<d-input-group prepend="ItemId" class="mb-3">
<d-select v-model="itemIdColIdx">
<option
v-for="(name, idx) in previewHeader"
:key="idx"
:value="idx"
>
{{ name }}
</option>
</d-select>
</d-input-group>
</d-col>
<d-col md="3">
<strong class="text-muted d-block mb-2">&nbsp;</strong>
<d-input-group prepend="Timestamp" class="mb-3">
<d-select v-model="timestampColIdx">
<option
v-for="(name, idx) in previewHeader"
:key="idx"
:value="idx"
>
{{ name }}
</option>
</d-select>
</d-input-group>
</d-col>
<d-col md="10" class="form-group">
<d-checkbox v-model="hasHeader"
>The first line is the header.</d-checkbox
>
</d-col>
<d-col md="2"> <d-button>Comfirm Import</d-button></d-col>
<d-col md="2" class="form-group"><strong class="text-muted d-block mb-2">&nbsp;</strong><d-button
outline>Comfirm Import</d-button></d-col>
</d-form-row>
</d-form>
</div>
<div class="progress" v-if="progressShow">
<div
class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
style="width: 100%"
></div>
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" aria-valuenow="75"
aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
</div>
<div class="card-body p-0 pb-3">
<table class="table mb-0">
<thead class="bg-light">
<tr>
<th
scope="col"
class="border-0"
v-for="(name, idx) in previewHeader"
:key="idx"
>
{{ mappedColumNames[idx]
}}<span class="text-semibold">({{ name }})</span>
<th scope="col" class="border-0" v-for="columnName in columnNames" :key="columnName">
{{ columnName }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(line, row_idx) in previewBody" :key="row_idx">
<td v-for="(value, col_idx) in line" :key="col_idx">
<div v-if="mappedColumNames[col_idx] == 'Labels'">
<d-badge
outline
theme="primary"
v-for="(label, idx) in splitLabels(
value,
labelSeparator
)"
:key="idx"
>
{{ label }}
</d-badge>
</div>
<div v-else>
{{ value }}
</div>
<tr v-for="(row, row_idx) in rows" :key="row_idx">
<td v-for="columnName in columnNames" :key="columnName">
{{ row[columnName] }}
</td>
</tr>
</tbody>
Expand All @@ -179,76 +60,27 @@

<script>
import moment from 'moment';
import utils from '@/utils/index';
const axios = require('axios');
export default {
data() {
return {
fieldSeparator: ',',
fileName: 'Choose file...',
hasHeader: true,
text: '',
feedbackTypeColIdx: 0,
userIdColIdx: 1,
itemIdColIdx: 2,
timestampColIdx: 3,
progressShow: false,
alertTheme: 'danger',
alertText: null,
duration: 5,
timeUntilDismissed: 0,
columnNames: [
'FeedbackType',
'UserId',
'ItemId',
'Timestamp',
],
rows: [],
};
},
computed: {
mappedColumNames() {
const header = this.previewHeader;
const names = [];
for (let i = 0; i < header.length; i += 1) {
if (i === this.feedbackTypeColIdx) {
names.push('FeedbackType');
} else if (i === this.userIdColIdx) {
names.push('UserId');
} else if (i === this.itemIdColIdx) {
names.push('ItemId');
} else if (i === this.timestampColIdx) {
names.push('Timestamp');
} else {
names.push('');
}
}
return names;
},
previewHeader() {
return this.previewTable[0];
},
previewBody() {
return this.previewTable.slice(1);
},
previewTable() {
if (this.fieldSeparator.length === 0) {
return [];
}
let numCols = 0;
// split fields
const table = utils
.parseLines(this.text, this.fieldSeparator)
.slice(0, -1);
table.forEach((fields) => {
numCols = Math.max(numCols, fields.length);
});
// add header
if (!this.hasHeader) {
const header = [];
for (let i = 0; i < numCols; i += 1) {
header.push(i.toString());
}
table.unshift(header);
}
return table;
},
},
methods: {
loadFile(event) {
const file = event.target.files[0];
Expand All @@ -259,6 +91,7 @@ export default {
reader.readAsText(file.slice(0, 1024));
reader.onload = (e) => {
this.text = e.target.result;
this.rows = e.target.result.split('\n').slice(0, -1).map((line) => JSON.parse(line));
};
},
format_date_time(timestamp) {
Expand Down Expand Up @@ -289,23 +122,7 @@ export default {
},
uploadFile() {
const formData = new FormData();
// 1. field separator must be a single character
if (this.fieldSeparator.length !== 1) {
this.showDanger('field separator must be a single character');
return;
}
formData.append('sep', this.fieldSeparator);
formData.append('has-header', this.hasHeader);
formData.append(
'format',
utils.mapFields('fuit', [
this.feedbackTypeColIdx,
this.userIdColIdx,
this.itemIdColIdx,
this.timestampColIdx,
]),
);
// 2. file must be chosen
// file must be chosen
const csvfile = document.querySelector('#csvFile');
if (csvfile.files.length === 0) {
this.showDanger('file must be chosen');
Expand Down
Loading

0 comments on commit 6e30641

Please sign in to comment.