Added in csv restore

This commit is contained in:
Henry Whitaker
2020-06-17 18:22:06 +01:00
parent 771e9824ff
commit 02659e7968
3 changed files with 143 additions and 39 deletions

View File

@@ -5,6 +5,7 @@ namespace App\Helpers;
use App\Speedtest; use App\Speedtest;
use DateTime; use DateTime;
use Exception; use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
class BackupHelper { class BackupHelper {
@@ -42,8 +43,9 @@ class BackupHelper {
return $name; return $name;
} }
public static function restore($array) public static function restore($array, $format)
{ {
if($format == 'json') {
foreach($array as $test) { foreach($array as $test) {
try { try {
$st = Speedtest::create([ $st = Speedtest::create([
@@ -56,5 +58,34 @@ class BackupHelper {
continue; continue;
} }
} }
return true;
} else if($format == 'csv') {
$csv = explode(PHP_EOL, $array);
$headers = 'id,ping,download,upload,created_at,updated_at';
if($csv[0] != $headers) {
Log::error('Incorrect CSV format');
return false;
}
unset($csv[0]);
$csv = array_values($csv);
for($i = 0; $i < sizeof($csv); $i++) {
$e = explode(',', $csv[$i]);
try {
$st = Speedtest::create([
'ping' => $e[1],
'download' => $e[2],
'upload' => $e[3],
'created_at' => substr($e[4], 1, -1),
]);
} catch(Exception $e) {
Log::error($e);
continue;
}
}
return true;
}
} }
} }

View File

@@ -28,7 +28,8 @@ class BackupController extends Controller
public function restore(Request $request) public function restore(Request $request)
{ {
$rule = [ $rule = [
'data' => [ 'required', 'array' ], 'data' => [ 'required' ],
'format' => [ 'required', 'in:json,csv' ]
]; ];
$validator = Validator::make($request->all(), $rule); $validator = Validator::make($request->all(), $rule);
@@ -39,10 +40,14 @@ class BackupController extends Controller
], 422); ], 422);
} }
BackupHelper::restore($request->data); if(BackupHelper::restore($request->data, $request->format) != false) {
return response()->json([ return response()->json([
'method' => 'restore data from backup', 'method' => 'restore data from backup',
], 200); ], 200);
} else {
return response()->json([
'method' => 'incorrect backup format',
], 422);
}
} }
} }

View File

@@ -1,8 +1,9 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { Button, Modal, Form, Tooltip, OverlayTrigger } from 'react-bootstrap'; import { Button, Modal, Form, Tooltip, OverlayTrigger, Dropdown, DropdownButton } from 'react-bootstrap';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import Axios from 'axios'; import Axios from 'axios';
import CSVFileValidator from 'csv-file-validator';
export default class Restore extends Component { export default class Restore extends Component {
constructor(props) { constructor(props) {
@@ -12,13 +13,15 @@ export default class Restore extends Component {
show: false, show: false,
data: null, data: null,
uploadReady: false, uploadReady: false,
filename: 'Upload your backup JSON' filename: 'Upload your backup',
format: 'json'
}; };
} }
showModal = () => { showModal = (format) => {
this.setState({ this.setState({
show: true show: true,
format: format
}); });
} }
@@ -28,13 +31,74 @@ export default class Restore extends Component {
}); });
} }
readFile = (e) => { readFile = (e, format) => {
var file = e.target.files[0]; var file = e.target.files[0];
var reader = new FileReader(); var reader = new FileReader();
reader.readAsText(file, 'UTF-8'); reader.readAsText(file, 'UTF-8');
reader.onload = function(evt) { reader.onload = function(evt) {
try {
var data = evt.target.result.trim(); var data = evt.target.result.trim();
if(format == 'csv') {
var csv = data.substr(45);
var config = {
headers: [
{
name: "id",
inputName: 'id',
required: false,
},
{
name: "ping",
inputName: 'ping',
required: true,
requiredError: function (headerName, rowNumber, columnNumber) {
return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column`
}
},
{
name: "upload",
inputName: 'upload',
required: true,
requiredError: function (headerName, rowNumber, columnNumber) {
return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column`
}
},
{
name: "download",
inputName: 'download',
required: true,
requiredError: function (headerName, rowNumber, columnNumber) {
return `${headerName} is required in the ${rowNumber} row / ${columnNumber} column`
}
},
{
name: "created_at",
inputName: 'created_at',
required: false,
},
{
name: "updated_at",
inputName: 'updated_at',
required: false,
}
]
};
CSVFileValidator(csv, config)
.then((e) => {
if(e.inValidMessages.length > 0) {
toast.error('Your upload file is not valid ' + format.toUpperCase());
} else {
this.setState({
data: data,
uploadReady: true,
filename: file.name
});
}
})
.catch((e) => {
toast.error('Your upload file is not valid ' + format.toUpperCase());
})
} else {
try {
var data = JSON.parse(data); var data = JSON.parse(data);
this.setState({ this.setState({
data: data, data: data,
@@ -43,7 +107,8 @@ export default class Restore extends Component {
}); });
} catch(e) { } catch(e) {
console.log(e); console.log(e);
toast.error('Your upload file is not valid JSON'); toast.error('Your upload file is not valid ' + format.toUpperCase());
}
} }
}.bind(this) }.bind(this)
reader.onerror = function (evt) { reader.onerror = function (evt) {
@@ -52,7 +117,7 @@ export default class Restore extends Component {
} }
uploadFile = () => { uploadFile = () => {
var data = { data: this.state.data }; var data = { data: this.state.data, format: this.state.format };
var url = 'api/restore'; var url = 'api/restore';
Axios.post(url, data) Axios.post(url, data)
@@ -62,7 +127,7 @@ export default class Restore extends Component {
show: false, show: false,
data: null, data: null,
uploadReady: false, uploadReady: false,
filename: 'Upload your backup JSON' filename: 'Upload your backup'
}); });
}) })
.catch((err) => { .catch((err) => {
@@ -77,21 +142,24 @@ export default class Restore extends Component {
return ( return (
<> <>
<Button variant="secondary" className="mx-2" onClick={this.showModal}>Restore</Button> <DropdownButton variant="secondary" title="Restore" className="m-2 d-inline-block">
<Dropdown.Item href="#" onClick={() => { this.showModal('json') }}>JSON</Dropdown.Item>
<Dropdown.Item href="#" onClick={() => { this.showModal('csv') }}>CSV</Dropdown.Item>
</DropdownButton>
<Modal show={show} onHide={this.hideModal} animation={true}> <Modal show={show} onHide={this.hideModal} animation={true}>
<Modal.Header closeButton> <Modal.Header closeButton>
<Modal.Title>Restore from a backup</Modal.Title> <Modal.Title>Restore from a backup</Modal.Title>
</Modal.Header> </Modal.Header>
<Modal.Body> <Modal.Body>
<p>Upload your JSON backup file here:</p> <p>Upload your {this.state.format.toUpperCase()} backup file here:</p>
<Form.File <Form.File
id="restoreFileInput" id="restoreFileInput"
label="Upload JSON file" label={"Upload " + this.state.format.toUpperCase() + " file"}
className="mb-3" className="mb-3"
custom custom
> >
<Form.File.Input onChange={this.readFile} /> <Form.File.Input onChange={(e) => { this.readFile(e, this.state.format) }} />
<Form.File.Label data-browse="Choose file"> <Form.File.Label data-browse="Choose file">
{filename} {filename}
</Form.File.Label> </Form.File.Label>