Skip to content

Commit

Permalink
added import from hevyapp
Browse files Browse the repository at this point in the history
  • Loading branch information
bandinopla committed Feb 12, 2024
1 parent 3bc70c8 commit 9d137ed
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 165 deletions.
3 changes: 3 additions & 0 deletions public/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
- 2.23.0 : 2024-02-12
+ Requested by @Tribble_Fun : Added option to import a cvs backup from hevyapp.com

- 2.22.0 : 2024-02-10
+ Added, in the settings, the option to import the backup file from weightxreps.net
+ Requested by @Indobesar : added the posibility to import a backup file from StrongApp
Expand Down
Binary file added public/heavyapp-logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/componentes/importer/fixRPE.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* When importing RPE values, other apps might allow diferent RPE values, but hee in weightxreps we only allow
* RPE from 6 to 10 and only .5 as "in between"
*
* @param {number} rpeValue
* @returns {number} the RPE in the valid range WXR logs accept
*/
export function fixRPE( rpeValue )
{
if( isNaN(rpeValue) || rpeValue<6 || rpeValue>10 )
{
return 0;
}

return Math.round(rpeValue * 2) / 2;
}
171 changes: 171 additions & 0 deletions src/componentes/importer/import-from-csv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { ImportFromWXR } from "./import-from-wxr"
import Joi from "joi";



/**
* Config for a CVS improter
*
* @typedef {Object} CVSParserConfig
* @property {bool} askWeightUnitToUser if we should ask the user for the weight unit used in the file
* @property {( firstRow:any, assumeUseKg:boolean )=>Object} startState Create the start state that will be used as initial state.
* @property { (state:Object, row:any)=>void} processRow Process a row from the CVS
* @property {Boolean} canContinueInCaseOfError if it can continue in case of error
* @property {(state:Object)=>string} stateAsLog turns the state into a weightxreps log
* @property {Joi.ObjectSchema<any>} schema Schema for a row
* @property {boolean} verifyEachRow if true, every row will be verified against the schema, else, only the first one.
*/


/**
* Handles the importing of the CVS and uses the config to delegate the implementation of the details.
*
* @param {CVSParserConfig} config details on how to parse the cvs
* @param {File} file the CVS file
* @param {(status:string)=>void} informStatus Send informative text to this function so the user knows wtf is going on.
* @returns {Promise<string>} The result is a workout log as if it was typed in the lovely workout editor of this site.
*/
const formatCVS = async (config, file, informStatus) => {

var usekg = true;

if( config.askWeightUnitToUser === true )
{
informStatus("Are the weights are in kilograms?");

if( !window.confirm("WEIGHT UNIT\n\nThe weights in the file are in (kg)Kilograms? (If you cancel then (lbs)Pounds will be assumed)\n\nAccept = KG\nCancel = LB"))
{
usekg = false;
}
}

informStatus("Prepearing to parse cvs");

const Papa = await import(/* webpackChunkName: "papaparse" */"papaparse");

return new Promise(( resolve, reject )=>{


informStatus("Parsing starts...");

let log = "";
let rowsDone = 0;
let abortedError;
let ignoreErrors;
let userWasPrompted = false;
var state;
let errorsIgnored = 0;

Papa.parse(file, {
delimiter:",",
header:true,
encoding:"utf-8",
skipEmptyLines:"greedy",

chunk: function(results, parser )
{
for (let i = 0; i < results.data.length; i++)
{
const row = results.data[i];

try
{
if( rowsDone==0 || config.verifyEachRow )
{
//
// verify each row to make sure the schema matches what we expect...
//
let validationResult = config.schema.validate(row, {allowUnknown:true, presence:"required"});
if( validationResult.error ) //validate each row....
{
console.error(validationResult.error);
throw new Error("Unexpected row format. Bad .csv mabe...");
}
}

if(!state)
{
state = config.startState(row, usekg);
}

config.processRow( state, row );
}
catch(e)
{
if( config.canContinueInCaseOfError && !userWasPrompted )
{
userWasPrompted = true;

if( window.confirm("WHAT TO DO?\n\nThere was an error parsing some portion(s) of the file. Do you wan't to ignore it and continue tyring to import the file?\n\nAccept = IGNORE ERRORS/ CONTINUE\nCancel = ABORT") )
{
ignoreErrors = true;
}
}

if( ignoreErrors )
{
//nothing...
errorsIgnored++;
}
else
{
abortedError = e;
parser.abort();
return;
}
}

};

rowsDone+=results.data.length;
informStatus(`Parsed ${rowsDone} rows...`);
},

complete: ()=>{

if( abortedError )
{
reject(`The file you attempt to import has errors, can't proceed :( Details: ${abortedError.message}`);
return;
}

try
{
log = config.stateAsLog(state);
}
catch(e)
{
reject( e.message );
return;
}

if( !log || log=="" )
{
reject("Nothing was imported, no data was found in the file.");
return;
}

informStatus("Parsing complete...");

if( errorsIgnored>0 )
{
alert(`The cvs was parsed! But we skipped (${errorsIgnored}) rows with errors.\n %${ ((rowsDone-errorsIgnored)/rowsDone*100).toFixed(1) } of the cvs was parsed successfully.`);
}

resolve( log );
},
error: err => reject(`Error parsing cvs --> ${err.message}`)
});

});
}


/**
* @param {{ config:CVSParserConfig, fileInputLabel:string }} param0
* @returns
*/
export const ImportFromCVS = ({ config, fileInputLabel, fileInputFileExtensions })=>{

return <ImportFromWXR formatFile={ formatCVS.bind(null,config) } fileInputLabel={fileInputLabel} fileInputFileExtensions=".csv"/>
}
Loading

0 comments on commit 9d137ed

Please sign in to comment.