Skip to content
This repository has been archived by the owner on Jan 4, 2024. It is now read-only.

Auto backup #128

Merged
merged 4 commits into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 56 additions & 10 deletions src/components/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ExpansionPanelSummary from "@material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "@material-ui/core/ExpansionPanelDetails";
import Typography from "@material-ui/core/Typography";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import * as auto_export from "../util/auto_export";

const numberFormat = require("../util/getNumberFormat")
const { dialog, getCurrentWindow } = require('electron').remote;
Expand Down Expand Up @@ -47,7 +48,8 @@ class SettingsModal extends React.Component {
langCodeValue: "",
langCode: "",
langVersion: "",
folderPath: ""
folderPath: "",
backupFrequency: ""
},
refSetting: {
bibleName: "",
Expand Down Expand Up @@ -116,10 +118,14 @@ class SettingsModal extends React.Component {
settingData.langCode = doc.targetLang;
settingData.langCodeValue = doc.targetLang;
settingData.langVersion = doc.targetVersion;
settingData.folderPath = doc.targetPath;
}, (err) => {
settingData.folderPath = doc.targetPath;
settingData.backupFrequency = (doc.backupFrequency) === undefined ? "daily" : doc.backupFrequency;
if (settingData.langCodeValue && settingData.langVersion && settingData.backupFrequency !== "none") {
auto_export.initializeBackUp();
}
}, (err) => {
// console.log(err);
});
});
}

loadReference = () => {
Expand Down Expand Up @@ -241,14 +247,15 @@ class SettingsModal extends React.Component {

saveSetting = () => {
if (this.target_setting() === false) return;
const currentTrans = AutographaStore.currentTrans;
const {langCode, langVersion, folderPath} = this.state.settingData;
const currentTrans = AutographaStore.currentTrans;
const {langCode, langVersion, folderPath, backupFrequency} = this.state.settingData;
const settingData = {
_id: 'targetBible',
targetLang: langCode.toLowerCase(),
targetVersion: langVersion,
targetPath: folderPath,
langScript: AutographaStore.scriptDirection.toUpperCase()
langScript: AutographaStore.scriptDirection.toUpperCase(),
backupFrequency: backupFrequency
}
db.get('targetBible').then((doc) => {
settingData._rev = doc._rev;
Expand All @@ -261,7 +268,11 @@ class SettingsModal extends React.Component {
}, (err) => {
swal(currentTrans["dynamic-msg-trans-data"], currentTrans["dynamic-msg-went-wrong"], "success");
});
});
});

if (langCode && langVersion && backupFrequency !== "none") {
auto_export.initializeBackUp();
}
}

openFileDialogSettingData = (event) => {
Expand Down Expand Up @@ -752,6 +763,9 @@ class SettingsModal extends React.Component {

onChangeScriptDir = (value) => {
AutographaStore.scriptDirection = value;
}
onChangeBackupSetting = (value) => {
this.setState({ settingData: { ...this.state.settingData, backupFrequency: value} });
}
onChangeRefScriptDir = (value) => {
AutographaStore.refScriptDirection = value;
Expand Down Expand Up @@ -945,7 +959,7 @@ class SettingsModal extends React.Component {

let closeSetting = () => AutographaStore.showModalSettings = false
const { show } = this.props;
const { langCodeValue, langVersion, folderPath } = this.state.settingData;
const { langCodeValue, langVersion, folderPath, backupFrequency } = this.state.settingData;
const { bibleName, refVersion, refLangCode, refFolderPath } = this.state.refSetting;

const listCode = this.state._listArray;
Expand Down Expand Up @@ -1057,7 +1071,7 @@ class SettingsModal extends React.Component {
}
</FormattedMessage>
</div>
<div style={{"display": "flex"}} className="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<div style={{"display": "flex", padding: "20px 0 0 0"}} className="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<label
style={{"marginTop": "-24px", "fontSize": "14px"}}
className="mdl-textfield__label"
Expand All @@ -1083,6 +1097,38 @@ class SettingsModal extends React.Component {
/>
</RadioButtonGroup>
</div>
<div style={{"display": "flex"}} className="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<label
style={{"marginTop": "-24px", "fontSize": "14px"}}
className="mdl-textfield__label"
id="label-autobackup"
>
<FormattedMessage id="label-auto-backup" />
</label>
<RadioButtonGroup
valueSelected={backupFrequency}
name="autobackup"
style={{display: "flex", marginBottom:"6%", marginTop: "8px"}}
onChange={(event, value) => this.onChangeBackupSetting(value)}
>
<RadioButton
value="none"
label={<FormattedMessage id="label-none" />}
style={{width: "70%"}}
/>
<RadioButton
value="daily"
label={<FormattedMessage id="label-daily" />}
style={{width: "70%", marginLeft: "25px"}}
/>
<RadioButton
value="weekly"
label={<FormattedMessage id="label-weekly" />}
style={{width: "70%", marginLeft: "25px"}}
/>
</RadioButtonGroup>
</div>

<FormattedMessage id="btn-save" >
{ (message)=>
<RaisedButton
Expand Down
4 changes: 4 additions & 0 deletions src/translations/ar.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export default {
"label-pwd" : "كلمه السر",
"label-credentials": "شهاداته",
"label-no-project" : "لا توجد مشاريع لإظهارها.",
"label-auto-backup": "لصناعة السيارات في النسخ الاحتياطي",
"label-none": "لا يوجد",
"label-daily": "اليومي",
"label-weekly": "أسبوعي",
"placeholder-search-text" : "البحث عن النص",
"placeholder-replace-text" : "إستبدال",
"placeholder-path-to-destination" : "مسار إلى مجلد الوجهة",
Expand Down
4 changes: 4 additions & 0 deletions src/translations/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ export default {
"label-incomplete-verses" : "Possible Incomplete Verses",
"label-multiple-spaces" : "Verses with Multiple Spaces",
"label-statistic-heading": "Statistics for the Book of",
"label-auto-backup": "Auto-backup",
"label-none": "None",
"label-daily": "Daily",
"label-weekly": "Weekly",
"export-html": "HTML",
"export-usfm": "USFM",
"export-html-1-column": "1-Column HTML",
Expand Down
4 changes: 4 additions & 0 deletions src/translations/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export default {
"label-unjoin-this-verse": "Desunir este verso",
"label-join-to-preceding-verse": "Únete al verso anterior",
"label-joint-with-the-preceding-verse(s)" : "----- Conjunta con los versos anteriores -----",
"label-auto-backup": "copia de seguridad automática",
"label-none": "Ninguno",
"label-daily": "Diario",
"label-weekly": "Semanal",
"placeholder-search-text" : "Buscar texto",
"placeholder-replace-text" : "Reemplazo",
"placeholder-path-to-destination" : "Ruta a la carpeta de destino",
Expand Down
4 changes: 4 additions & 0 deletions src/translations/hi.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export default {
"label-unjoin-this-verse": "आयत को अलग करें",
"label-join-to-preceding-verse": "पिछले आयत से जोड़े",
"label-joint-with-the-preceding-verse(s)" : "----- पिछले आयत(तों) के साथ जोड़े -----",
"label-auto-backup": "ऑटो बैकअप",
"label-none": "नहीं",
"label-daily": "दैनिक",
"label-weekly": "साप्ताहिक",
"placeholder-search-text" : "विषय ढूंढें",
"placeholder-replace-text" : "प्रतिस्थापन",
"placeholder-path-to-destination" : "लक्षित फोल्डर की ओर मार्ग",
Expand Down
4 changes: 4 additions & 0 deletions src/translations/pt.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export default {
"label-unjoin-this-verse": "Desunir este verso",
"label-join-to-preceding-verse": "Junte-se ao verso anterior",
"label-joint-with-the-preceding-verse(s)" : "----- Juntar-se ao (s) versículo (s) -----",
"label-auto-backup": "backup automático",
"label-none": "Nenhum",
"label-daily": "Diariamente",
"label-weekly": "Semanal",
"placeholder-search-text" : "Buscar Texto",
"placeholder-replace-text" : "Substituição",
"placeholder-path-to-destination" : "Localização da pasta de destino",
Expand Down
178 changes: 178 additions & 0 deletions src/util/auto_export.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import AutographaStore from "../components/AutographaStore";
const path = require("path");
const fs = require("fs");
const constants = require("../util/constants");
const db = require(`${__dirname}/data-provider`).targetDb();

export const initializeBackUp = async () => {
try {
// Initial Backup call
backUp();
const delay = 60000 * 60; // 60 minutes/1 hour
setInterval(async () => {
backUp();
}, delay);
} catch(e) {
console.log(e);
}
}

async function backUp() {
let targetLangDoc = await db.get('targetBible');
const outputPath = targetLangDoc.targetPath;
const directory = path.join(Array.isArray(outputPath) ? outputPath[0] : outputPath,"auto-backup");
const nameElements = [
targetLangDoc && targetLangDoc.targetLang,
targetLangDoc && targetLangDoc.targetVersion
].filter(Boolean);
const foldername = nameElements.join("_");
const dirPath = path.join(directory, foldername);
if (!fs.existsSync(dirPath)){
fs.mkdirSync(dirPath, { recursive: true });
}
await fs.readdir(dirPath, (err, folders) => {
if (folders.length >= 1) {
let folderName = (folders[(folders.length)-1]).split('_');
var d = new Date();
let timeStamp = [ d && getTimeStamp(d) ].filter(Boolean);
let currentTime = timeStamp[0].split('_');
let days = calculateDays(currentTime,folderName);
if (days >= 1 && (targetLangDoc.backupFrequency).toLowerCase() === "daily") {
buildFilePath(new Date(), dirPath);
folderHandling (folders, dirPath);
} else if (days >= 7 && (targetLangDoc.backupFrequency).toLowerCase() === "weekly") {
buildFilePath(new Date(), dirPath);
folderHandling (folders, dirPath);
} else {
return null;
}

} else {
buildFilePath(new Date(), dirPath);
}
});
}

async function exportAllBooks(dir) {
let doc = await db.get('targetBible');
constants.bookCodeList.forEach(async(value, index) => {
let book={};
book.bookNumber = (index+1).toString();
book.bookName = AutographaStore.editBookNamesMode ? AutographaStore.translatedBookNames[index] : constants.booksList[index];
book.bookCode = value;
book.outputPath = doc.targetPath;
await toUsfm(book, dir);
})
}

export const toUsfm = async (book, dir) => {
const usfmDoc = await toUsfmDoc(book, false);
const filename = (book.bookCode) + '.usfm';
const filePath = path.join(dir, filename);
writeUsfm(usfmDoc, filePath);
};

async function toUsfmDoc(book, returnNullForEmptyBook=false) {
try {
const usfmContent = [];
var isEmpty = true;
usfmContent.push('\\id ' + book.bookCode);
usfmContent.push('\\mt ' + book.bookName);
const doc = await db.get(book.bookNumber);
for (const chapter of doc.chapters) {
usfmContent.push('\n\\c ' + chapter.chapter);
usfmContent.push('\\p');
let i = 0;
let verseNumber;
let verses;
for (const verse of chapter.verses) {
i = i + 1;
if (i < (chapter.verses).length && chapter.verses[i].joint_verse) {
// Finding out the join verses and get their verse number(s)
verseNumber = chapter.verses[i].joint_verse + "-" + chapter.verses[i].verse_number;
verses = chapter.verses[(chapter.verses[i].joint_verse)-1].verse;
continue;
} else {
if (verseNumber) {
// Push join verse number (1-3) and content.
let newVerse = addMtag(verses);
usfmContent.push('\\v ' + verseNumber + ' ' + newVerse);
verseNumber = undefined;
verses = undefined;
} else {
// Push verse number and content.
let newVerse = addMtag(verse.verse);
usfmContent.push('\\v ' + verse.verse_number + ' ' + newVerse);
}
isEmpty = isEmpty && !verse.verse;
}
}
}
return (returnNullForEmptyBook && isEmpty)
? null
: usfmContent.join('\n');
} catch(err) {
console.log(err);
}
}

function addMtag(verses) {
let newVerse = verses;
if(verses.indexOf('\n') !== -1 ){
newVerse = verses.trim().replace(new RegExp(/[\n\r]/, 'gu'), '\n\\m ')
verses = newVerse
}
return newVerse;
}

async function buildFilePath(date, dirPath) {
const dateElement = [ date && getTimeStamp(date) ].filter(Boolean);
const dir = path.join(dirPath, dateElement[0]);
if (!fs.existsSync(dir)){
fs.mkdirSync(dir, { recursive: true });
}
exportAllBooks(dir);
}

function folderHandling (folders, dirPath) {
if (folders.length >= 5) {
var files = fs.readdirSync(path.join(dirPath, folders[0]));
files.forEach(file => {
fs.unlinkSync(path.join(dirPath, folders[0], file));
});
fs.rmdir(path.join(dirPath, folders[0]), function(err, result) {
if(err) console.log('error', err);
});
}
}

function writeUsfm(usfmDoc, filePath) {
if (usfmDoc && filePath) {
fs.writeFileSync(filePath, usfmDoc, 'utf8', function(err, result) {
if(err) console.log('error', err);
});
}
}

function getTimeStamp(date) {
var months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
var year = date.getFullYear(),
month = ((date.getMonth() + 1) < 10 ? '0' : '') + (date.getMonth() + 1),
day = (date.getDate() < 10 ? '0' : '') + date.getDate(),
hour = (date.getHours() < 10 ? '0' : '') + date.getHours(),
minute = (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(),
second = (date.getSeconds() < 10 ? '0' : '') + date.getSeconds();

return (months[parseInt(month)-1] + " " + day + "-" + year.toString() + "_" + hour + ":" + minute + ":" + second).toString();
}

function calculateDays(currDate, preDate)
{
var currTime = new Date(currDate[0] + " " + currDate[1]);
var preTime = new Date(preDate[0] + " " + preDate[1]);
var difference = Math.abs(currTime.getTime() - preTime.getTime()) / 1000;
// var hour = Math.floor(difference / 3600) % 24;
// var minutes = Math.floor(difference / 60) % 60;
var days = Math.floor(difference / 86400);
return days;
}