Skip to content
Snippets Groups Projects
Commit 2883aaa8 authored by Julia Friesel's avatar Julia Friesel
Browse files

Merge branch '114-add-import-button' into 'master'

Resolve "Add Import button"

Closes #114

See merge request bloodyhealth/drip!48
parents 18c5fef5 4b227d82
No related branches found
No related tags found
No related merge requests found
...@@ -137,6 +137,8 @@ android { ...@@ -137,6 +137,8 @@ android {
} }
dependencies { dependencies {
compile project(':react-native-fs')
compile project(':react-native-document-picker')
compile project(':react-native-share') compile project(':react-native-share')
compile project(':realm') compile project(':realm')
compile project(':react-native-svg') compile project(':react-native-svg')
......
...@@ -3,6 +3,8 @@ package com.drip; ...@@ -3,6 +3,8 @@ package com.drip;
import android.app.Application; import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.rnfs.RNFSPackage;
import com.reactnativedocumentpicker.ReactNativeDocumentPicker;
import cl.json.RNSharePackage; import cl.json.RNSharePackage;
import cl.json.ShareApplication; import cl.json.ShareApplication;
import io.realm.react.RealmReactPackage; import io.realm.react.RealmReactPackage;
...@@ -27,6 +29,8 @@ public class MainApplication extends Application implements ReactApplication, Sh ...@@ -27,6 +29,8 @@ public class MainApplication extends Application implements ReactApplication, Sh
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new RNFSPackage(),
new ReactNativeDocumentPicker(),
new RNSharePackage(), new RNSharePackage(),
new RealmReactPackage(), new RealmReactPackage(),
new SvgPackage() new SvgPackage()
......
rootProject.name = 'drip' rootProject.name = 'drip'
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-document-picker'
project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android')
include ':react-native-share' include ':react-native-share'
project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android') project(':react-native-share').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-share/android')
include ':realm' include ':realm'
......
export const settings = { export const settings = {
errors: { shared: {
noData: 'There is no data to export', cancel: 'Cancel',
couldNotConvert: 'Could not convert data to CSV', errorTitle: 'Error',
problemSharing: 'There was a problem sharing the data export file' successTitle: 'Success'
}, },
exportTitle: 'My Drip data export', export: {
exportSubject: 'My Drip data export', errors: {
buttonLabel: 'Export data' noData: 'There is no data to export',
couldNotConvert: 'Could not convert data to CSV',
problemSharing: 'There was a problem sharing the data export file'
},
title: 'My Drip data export',
subject: 'My Drip data export',
button: 'Export data',
},
import: {
button: 'Import data',
title: 'Keep existing data?',
message: `There are two options for the import:
1. Keep existing cycle days and replace only the ones in the import file.
2. Delete all existing cycle days and import cycle days from file.`,
replaceOption: 'Import and replace',
deleteOption: 'Import and delete existing',
errors: {
couldNotOpenFile: 'Could not open file',
postFix: 'No data was imported or changed'
},
success: {
message: 'Data successfully imported'
}
}
} }
\ No newline at end of file
...@@ -7,9 +7,12 @@ import { ...@@ -7,9 +7,12 @@ import {
} from 'react-native' } from 'react-native'
import Share from 'react-native-share' import Share from 'react-native-share'
import getDataAsCsvDataUri from '../lib/export-to-csv' import { DocumentPicker, DocumentPickerUtil } from 'react-native-document-picker'
import rnfs from 'react-native-fs'
import styles from '../styles/index' import styles from '../styles/index'
import { settings as labels } from './labels' import { settings as labels } from './labels'
import getDataAsCsvDataUri from '../lib/import-export/export-to-csv'
import importCsv from '../lib/import-export/import-from-csv'
export default class Settings extends Component { export default class Settings extends Component {
render() { render() {
...@@ -18,36 +21,100 @@ export default class Settings extends Component { ...@@ -18,36 +21,100 @@ export default class Settings extends Component {
<View style={styles.homeButtons}> <View style={styles.homeButtons}>
<View style={styles.homeButton}> <View style={styles.homeButton}>
<Button <Button
onPress={async () => { onPress={ openShareDialogAndExport }
let data title={labels.export.button}>
try { </Button>
data = getDataAsCsvDataUri() </View>
if (!data) { <View style={styles.homeButton}>
return Alert.alert(labels.errors.noData) <Button
} title={labels.import.button}
} catch (err) { onPress={ openImportDialogAndImport }>
console.error(err)
return Alert.alert(labels.errors.couldNotConvert)
}
try {
await Share.open({
title: labels.exportTitle,
url: data,
subject: labels.exportSubject,
type: 'text/csv',
showAppsToView: true
})
} catch (err) {
console.error(err)
return Alert.alert(labels.errors.problemSharing)
}
}}
title={labels.buttonLabel}>
</Button> </Button>
</View> </View>
</View> </View>
</ScrollView> </ScrollView>
) )
} }
}
async function openShareDialogAndExport() {
let data
try {
data = getDataAsCsvDataUri()
if (!data) {
return alertError(labels.errors.noData)
}
} catch (err) {
console.error(err)
return alertError(labels.errors.couldNotConvert)
}
try {
await Share.open({
title: labels.export.title,
url: data,
subject: labels.export.subject,
type: 'text/csv',
showAppsToView: true
})
} catch (err) {
console.error(err)
return alertError(labels.export.errors.problemSharing)
}
}
function openImportDialogAndImport() {
Alert.alert(
labels.import.title,
labels.import.message,
[{
text: labels.import.replaceOption,
onPress: () => getFileContentAndImport({ deleteExisting: false })
}, {
text: labels.import.deleteOption,
onPress: () => getFileContentAndImport({ deleteExisting: true })
}, {
text: labels.shared.cancel, style: 'cancel', onPress: () => { }
}]
)
}
async function getFileContentAndImport({ deleteExisting }) {
let fileInfo
try {
fileInfo = await new Promise((resolve, reject) => {
DocumentPicker.show({
filetype: [DocumentPickerUtil.allFiles()],
}, (err, res) => {
if (err) return reject(err)
resolve(res)
})
})
} catch (err) {
// because cancelling also triggers an error, we do nothing here
return
}
let fileContent
try {
fileContent = await rnfs.readFile(fileInfo.uri, 'utf8')
} catch (err) {
return importError(labels.import.errors.couldNotOpenFile)
}
try {
await importCsv(fileContent, deleteExisting)
Alert.alert(labels.import.success.title, labels.import.success.message)
} catch(err) {
importError(err.message)
}
}
function alertError(msg) {
Alert.alert(labels.shared.errorTitle, msg)
}
function importError(msg) {
const postFixed = `${msg}\n\n${labels.import.errors.postFix}`
alertError(postFixed)
} }
\ No newline at end of file
import Realm from 'realm' import Realm from 'realm'
import { LocalDate } from 'js-joda' import { LocalDate } from 'js-joda'
import { import {
cycleWithTempAndNoMucusShift, cycleWithTempAndNoMucusShift,
cycleWithFhm, cycleWithFhm,
...@@ -196,24 +197,37 @@ function getPreviousTemperature(cycleDay) { ...@@ -196,24 +197,37 @@ function getPreviousTemperature(cycleDay) {
return winner.temperature.value return winner.temperature.value
} }
function getColumnNamesForCsv() { const schema = db.schema.reduce((acc, curr) => {
return getPrefixedKeys('CycleDay') acc[curr.name] = curr.properties
return acc
function getPrefixedKeys(schemaName, prefix) { }, {})
const schema = db.schema.find(x => x.name === schemaName).properties
return Object.keys(schema).reduce((acc, key) => { function tryToCreateCycleDay(day, i) {
const prefixedKey = prefix ? [prefix, key].join('.') : key try {
const childSchemaName = schema[key].objectType db.create('CycleDay', day)
if (!childSchemaName) { } catch (err) {
acc.push(prefixedKey) const msg = `Line ${i + 1}(${day.date}): ${err.message}`
return acc throw new Error(msg)
}
acc.push(...getPrefixedKeys(childSchemaName, prefixedKey))
return acc
}, [])
} }
} }
function tryToImportWithDelete(cycleDays) {
db.write(() => {
db.delete(db.objects('CycleDay'))
cycleDays.forEach(tryToCreateCycleDay)
})
}
function tryToImportWithoutDelete(cycleDays) {
db.write(() => {
cycleDays.forEach((day, i) => {
const existing = getCycleDay(day.date)
if (existing) db.delete(existing)
tryToCreateCycleDay(day, i)
})
})
}
export { export {
saveSymptom, saveSymptom,
getOrCreateCycleDay, getOrCreateCycleDay,
...@@ -224,5 +238,7 @@ export { ...@@ -224,5 +238,7 @@ export {
deleteAll, deleteAll,
getPreviousTemperature, getPreviousTemperature,
getCycleDay, getCycleDay,
getColumnNamesForCsv schema,
tryToImportWithDelete,
tryToImportWithoutDelete
} }
...@@ -43,6 +43,8 @@ ...@@ -43,6 +43,8 @@
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AEBF0735214455AAEDF56D5 /* libc++.tbd */; }; 62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 9AEBF0735214455AAEDF56D5 /* libc++.tbd */; };
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C8B91E0A747B3883A0D56 /* libz.tbd */; }; D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = CD8C8B91E0A747B3883A0D56 /* libz.tbd */; };
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */; }; 26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */; };
29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */; };
17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84CCEBD3B2C44758853BC941 /* libRNFS.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
...@@ -357,6 +359,10 @@ ...@@ -357,6 +359,10 @@
CD8C8B91E0A747B3883A0D56 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; }; CD8C8B91E0A747B3883A0D56 /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */ = {isa = PBXFileReference; name = "RNShare.xcodeproj"; path = "../node_modules/react-native-share/ios/RNShare.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; 4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */ = {isa = PBXFileReference; name = "RNShare.xcodeproj"; path = "../node_modules/react-native-share/ios/RNShare.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */ = {isa = PBXFileReference; name = "libRNShare.a"; path = "libRNShare.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; A8B59389C2FC4F19BD30ABC3 /* libRNShare.a */ = {isa = PBXFileReference; name = "libRNShare.a"; path = "libRNShare.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */ = {isa = PBXFileReference; name = "RNDocumentPicker.xcodeproj"; path = "../node_modules/react-native-document-picker/ios/RNDocumentPicker.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
D211D71BE5A8436A978770A9 /* libRNDocumentPicker.a */ = {isa = PBXFileReference; name = "libRNDocumentPicker.a"; path = "libRNDocumentPicker.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */ = {isa = PBXFileReference; name = "RNFS.xcodeproj"; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
84CCEBD3B2C44758853BC941 /* libRNFS.a */ = {isa = PBXFileReference; name = "libRNFS.a"; path = "libRNFS.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -390,6 +396,8 @@ ...@@ -390,6 +396,8 @@
62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */, 62F2A4645AC84CDC9506FF27 /* libc++.tbd in Frameworks */,
D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */, D91133DCE120440893E2FD2E /* libz.tbd in Frameworks */,
26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */, 26DC04B498C64CE5AAA0C4F8 /* libRNShare.a in Frameworks */,
29DF0CCC1AEA4C92BCA0BCCD /* libRNDocumentPicker.a in Frameworks */,
17AD822C42A44BADA96BD860 /* libRNFS.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -582,6 +590,8 @@ ...@@ -582,6 +590,8 @@
8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */, 8316A5AD64274E6FBA6C9FFE /* RNSVG.xcodeproj */,
7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */, 7F6C9FA9B66B453CA602B334 /* RealmReact.xcodeproj */,
4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */, 4E6AB77B55F2491487B6124E /* RNShare.xcodeproj */,
1F05FE29622E4F21AF70C2B7 /* RNDocumentPicker.xcodeproj */,
49089E09BFCF4F3DB209B6E9 /* RNFS.xcodeproj */,
); );
name = Libraries; name = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1209,12 +1219,16 @@ ...@@ -1209,12 +1219,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Debug; name = Debug;
...@@ -1236,12 +1250,16 @@ ...@@ -1236,12 +1250,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Release; name = Release;
...@@ -1266,6 +1284,8 @@ ...@@ -1266,6 +1284,8 @@
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Debug; name = Debug;
...@@ -1289,6 +1309,8 @@ ...@@ -1289,6 +1309,8 @@
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Release; name = Release;
...@@ -1319,12 +1341,16 @@ ...@@ -1319,12 +1341,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Debug; name = Debug;
...@@ -1355,12 +1381,16 @@ ...@@ -1355,12 +1381,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Release; name = Release;
...@@ -1390,12 +1420,16 @@ ...@@ -1390,12 +1420,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Debug; name = Debug;
...@@ -1425,12 +1459,16 @@ ...@@ -1425,12 +1459,16 @@
LIBRARY_SEARCH_PATHS = ( LIBRARY_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"\"$(SRCROOT)/$(TARGET_NAME)\"", "\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
"\"$(SRCROOT)/$(TARGET_NAME)\"",
); );
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"$(SRCROOT)/../node_modules/react-native-svg/ios/**", "$(SRCROOT)/../node_modules/react-native-svg/ios/**",
"$(SRCROOT)/../node_modules/realm/src/**", "$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-share/ios", "$(SRCROOT)/../node_modules/react-native-share/ios",
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
"$(SRCROOT)/../node_modules/react-native-fs/**",
); );
}; };
name = Release; name = Release;
......
import objectPath from 'object-path' import objectPath from 'object-path'
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'
import { cycleDaysSortedByDate } from '../../db'
import { getColumnNamesForCsv, cycleDaysSortedByDate } from '../db' import getColumnNamesForCsv from './get-csv-column-names'
export default function makeDataURI() { export default function makeDataURI() {
if (!cycleDaysSortedByDate.length) return null if (!cycleDaysSortedByDate.length) return null
......
import { schema } from '../../db'
export default function getColumnNamesForCsv() {
return getPrefixedKeys('CycleDay')
function getPrefixedKeys(schemaName, prefix) {
const model = schema[schemaName]
return Object.keys(model).reduce((acc, key) => {
const prefixedKey = prefix ? [prefix, key].join('.') : key
const childSchemaName = model[key].objectType
if (!childSchemaName) {
acc.push(prefixedKey)
return acc
}
acc.push(...getPrefixedKeys(childSchemaName, prefixedKey))
return acc
}, [])
}
}
\ No newline at end of file
import csvParser from 'csvtojson'
import isObject from 'isobject'
import { schema, tryToImportWithDelete, tryToImportWithoutDelete } from '../../db'
import getColumnNamesForCsv from './get-csv-column-names'
export default async function importCsv(csv, deleteFirst) {
const parseFuncs = {
bool: val => {
if (val.toLowerCase() === 'true') return true
if (val.toLowerCase() === 'false') return false
return val
},
int: parseNumberIfPossible,
float: parseNumberIfPossible,
double: parseNumberIfPossible,
string: val => val
}
function parseNumberIfPossible(val) {
// Number and parseFloat catch different cases of weirdness,
// so we test them both
if (isNaN(Number(val)) || isNaN(parseFloat(val))) return val
return Number(val)
}
const config = {
ignoreEmpty: true,
colParser: getColumnNamesForCsv().reduce((acc, colName) => {
const path = colName.split('.')
const dbType = getDbType(schema.CycleDay, path)
acc[colName] = item => {
if (item === '') return null
return parseFuncs[dbType](item)
}
return acc
}, {})
}
const cycleDays = await csvParser(config)
.fromString(csv)
.on('header', validateHeaders)
//remove symptoms where all fields are null
putNullForEmptySymptoms(cycleDays)
if (deleteFirst) {
tryToImportWithDelete(cycleDays)
} else {
tryToImportWithoutDelete(cycleDays)
}
}
function validateHeaders(headers) {
const expectedHeaders = getColumnNamesForCsv()
if (!headers.every(header => {
return expectedHeaders.indexOf(header) > -1
})) {
const msg = `Expected CSV column titles to be ${expectedHeaders.join()}`
throw new Error(msg)
}
}
function putNullForEmptySymptoms(data) {
data.forEach(replaceWithNullIfAllPropertiesAreNull)
function replaceWithNullIfAllPropertiesAreNull(obj) {
Object.keys(obj).forEach((key) => {
if (!isObject(obj[key])) return
if (Object.values(obj[key]).every(val => val === null)) {
obj[key] = null
return
}
replaceWithNullIfAllPropertiesAreNull(obj[key])
})
}
}
function getDbType(modelProperties, path) {
if (path.length === 1) return modelProperties[path[0]].type
const modelName = modelProperties[path[0]].objectType
return getDbType(schema[modelName], path.slice(1))
}
\ No newline at end of file
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment