function viewUpload() { $form = new SessionUploadForm(); $view = Core_View::factory('sessionsfileupload'); $view->UploadStatusMsg = ""; $view->UploadStatus = "Error"; if ($form->validate()) { $timer = new Benchmark_Timer(); $timer->start(); $upload = $form->getSubmitValue('upload'); $timer->setMarker('Decode Sessions - Start'); exec('/usr/local/bin/fitdecode -s ' . $upload['tmp_name'], $xml_session); $xml_session = implode("\n", $xml_session); $sessions = parseSessions($xml_session); $timer->setMarker('Decode Sessions - End'); /* There should only be one session */ if (is_array($sessions)) { $session = $sessions[0]; unset($sessions); } $db = Zend_Registry::get('db'); $db->beginTransaction(); try { $api = new Module_Sessions_API(); /* Insert the session data into the database */ $api->createSessionFull($session->start_time, 'E1', 'Untitled', $session->total_timer_time, $session->total_distance, $session->total_calories, $session->avg_heart_rate, $session->max_heart_rate, $session->avg_speed, $session->max_speed, $session->total_ascent, $session->total_descent, ''); /* Find the seconds since epoch so we can do simple maths */ $ftime = strptime($session->start_time, '%FT%T%z'); $session_epoch = mktime($ftime['tm_hour'], $ftime['tm_min'], $ftime['tm_sec'], 1, $ftime['tm_yday'] + 1, $ftime['tm_year'] + 1900); $session_timestamp = $session->start_time; unset($session); unset($sessions); $timer->setMarker('Decode Records - Start'); exec('/usr/local/bin/fitdecode -r ' . $upload['tmp_name'], $xml_records); $xml_records = implode("\n", $xml_records); $records_input = parseRecords($xml_records, $session_epoch); $timer->setMarker('Decode Records - End'); if (is_array($records_input)) { $record_prev = $records_input[0]; } /* Get the array of records, removing duplicates */ $records = array(); foreach ($records_input as $record) { if (!isset($record_last) || $record_last->interval != $record->interval) { $records[] = $record; } $record_last = $record; } unset($records_input); unset($record_last); $UserAPI = Module_UserManagement_API::getInstance(); $user = $UserAPI->getUser(); /* Add the matching data points */ foreach ($records as $record) { /* Skip duplicates, they will cause issues in graphs */ if (!isset($record->power)) { $record->power = $api->getPower($record->gradient, $record->temperature, $record->altitude, $record->speed, $record->speed - $record_prev->speed, $record->interval - $record_prev->interval, $user['rider_weight'], $user['bike_weight']); } $record_prev = $record; } unset($user); unset($UserAPI); $timer->setMarker('Record insertion - start'); $api->insertAllSessionData($session_timestamp, $records); /* Insert all the data */ $timer->setMarker('Record insertion - end'); /* Calculate the climbs */ $climbs = $api->getClimbCategories(); $timer->setMarker('Climb - Start'); $min_climb = $climbs[0]; /* 500m with an average gradient of more than 3% (cat 5)*/ /* Find the points that have a distance of 500m */ $window_distance = 0; $window_altitude = 0; $cat = -1; $climb_num = 1; $num_records = count($records); $num_climbs = count($climbs); for ($front = 0, $back = 0; $front < $num_records; $front++) { $window_distance += $records[$front]->delta_distance * 1000; $window_altitude += $records[$front]->delta_altitude; if ($window_distance > $min_climb['min_distance']) { $window_gradient = $window_altitude / $window_distance * 100; /* Check if we have found the start of a climb */ if ($cat == -1 && $window_gradient >= $climbs[$cat + 1]['min_gradient']) { $cat++; /* Go through and find the minimum height */ $min = $back; for ($i = $back; $i < $front; $i++) { if ($records[$i]->altitude <= $records[$min]->altitude) { $min = $i; } } $climb['bottom'] = $records[$min]->interval; $climb['min_altitude'] = $records[$min]->altitude; } /* Check if we have finished the climb */ if ($cat != -1 && $window_gradient < $climbs[$cat]['min_gradient']) { /* Need to go back and find the maximum altitude */ $max = $back; for ($i = $back; $i < $front; $i++) { if ($records[$i]->altitude > $records[$max]->altitude) { $max = $i; } } $climb['top'] = $records[$max]->interval; $climb['max_altitude'] = $records[$max]->altitude; /* Get the max gradient */ $climb['gradient_max'] = $records[$min]->gradient; for ($i = $min; $i <= $max; $i++) { if ($climb['gradient_max'] < $records[$i]->gradient) { $climb['gradient_max'] = $records[$i]->gradient; } } /* Tally the totals */ $climb['total_climbed'] = 0; for ($i = $min + 1; $i <= $max; $i++) { $climb['total_climbed'] += $records[$i]->delta_altitude; } $climb['total_distance'] = round($records[$max]->distance - $records[$min]->distance, 2); $climb['gradient_avg'] = round($climb['total_climbed'] / ($climb['total_distance'] * 1000) * 100, 2); /* Find the category of the climb */ $cat = -1; while ($cat + 1 < $num_climbs && $climb['gradient_avg'] >= $climbs[$cat + 1]['min_gradient'] && $climb['total_distance'] * 1000 >= $climbs[$cat + 1]['min_distance'] && $climb['total_climbed'] >= $climbs[$cat + 1]['min_height']) { $cat++; } $climb['cat'] = $cat; if ($cat != -1) { /* Store it into the database */ $api->insertClimb($session_timestamp, $climb_num++, $climb['bottom'], $climb['top'], $climb['gradient_avg'], $climb['gradient_max'], $climb['total_distance'], $climb['total_climbed'], $climb['min_altitude'], $climb['max_altitude']); /* Start search for the next climb */ $front = $max; $back = $max; $window_distance = 0; $window_altitude = 0; } else { /* It was a false climb, either not steep enough, * too short, and the window just masked this * Keep searching for the next climb */ } $cat = -1; } /* Move the back of the window up */ while ($window_distance > $min_climb['min_distance'] && $back < $num_records) { $window_distance -= $records[$back]->delta_distance * 1000; $window_altitude -= $records[$back]->delta_altitude; $back++; } } } $timer->setMarker('Climb - End'); /* * Bikes * userid * name * description * type, TT or Road * weight * picture? * Assign a bike to an exercise session at creation time? */ unset($records); $timer->setMarker('Laps - Start'); exec('/usr/local/bin/fitdecode -l ' . $upload['tmp_name'], $xml_laps); $xml_laps = implode("\n", $xml_laps); $laps = parseLaps($xml_laps); $timer->setMarker('Laps - End'); $lap_num = 1; foreach ($laps as $lap) { $ftime = strptime($lap->start_time, '%FT%T%z'); $start_epoch = mktime($ftime['tm_hour'], $ftime['tm_min'], $ftime['tm_sec'], 1, $ftime['tm_yday'] + 1, $ftime['tm_year'] + 1900); $lap_start = $start_epoch - $session_epoch; $api->insertLap($session_timestamp, $lap_num, $lap_start, $lap->start_position_lat, $lap->start_position_long, $lap->total_timer_time, $lap->total_elapsed_time, $lap->total_calories, $lap->avg_heart_rate, $lap->max_heart_rate, $lap->avg_speed, $lap->max_speed, $lap->total_ascent, $lap->total_descent, $lap->total_distance); $lap_num++; } //$timer->display(); $db->commit(); $plans = Module_Plans_API::getInstance(); $view->planned = $plans->getClosestPlan($session_timestamp); $view->session_timestamp = $session_timestamp; $view->UploadStatusMsg = "Is this session the planned exercise session on at ere"; $view->UploadStatus = "Success"; } catch (Exception $e) { $db->rollback(); $view->UploadStatusMsg = "Failed to upload"; $view->UploadStatus = "Error"; echo $e->getMessage(); } $timer->display(); } $view->addForm($form); $view->subTemplate = 'genericForm.tpl'; echo $view->render(); }
function scifinderToRefbase($sourceText, $importRecordsRadio, $importRecordNumbersArray) { global $alnum, $alpha, $cntrl, $dash, $digit, $graph, $lower, $print, $punct, $space, $upper, $word, $patternModifiers; // defined in 'transtab_unicode_charset.inc.php' and 'transtab_latin1_charset.inc.php' global $errors; global $showSource; // The SciFinder format uses variable-length field label names, which makes it // impossible to match field labels using regular expressions with perl-style // look-behinds (such as '(?<=...)'). This poses a problem when specifying an // appropriate regex pattern for variable '$dataDelimiter'. Therefore, we'll // preprocess the '$sourceText' so that delimiters between field labels and // field data can be easily matched. $sourceText = preg_replace("/^(FIELD [^:\r\n]+):/m", "\\1__dataDelimiter__", $sourceText); // replace the first colon (":"), which separates a field label from its data, with a custom string ("__dataDelimiter__") // Define regular expression patterns that will facilitate parsing of SciFinder data: // (patterns must be specified as perl-style regular expression, without the leading & trailing slashes, if not stated otherwise) // Pattern by which the input text will be split into individual records: $recordDelimiter = "\\s*(START_RECORD[\r\n]+|[\r\n]+END_RECORD)\\s*"; // Pattern by which records will be split into individual fields: $fieldDelimiter = "[\r\n]+FIELD *"; // Pattern by which fields will be split into their field label (tag) and field data: $dataDelimiter = " *__dataDelimiter__ *"; // Pattern by which multiple persons are separated within the author, editor or series editor fields of the source data: // (Notes: - name standardization occurs after multiple author fields have been merged by '; ' // - the split pattern must be specified as perl-style regular expression (including the leading & trailing // slashes) and may include mode modifiers (such as '/.../i' to perform a case insensitive match)) $personDelimiter = "/ *; */"; // Pattern by which a person's family name is separated from the given name (or initials): // (the split pattern must be specified as perl-style regular expression (including the leading & trailing // slashes) and may include mode modifiers (such as '/.../i' to perform a case insensitive match)) $familyNameGivenNameDelimiter = "/ *, */"; // Specifies whether the person's family name comes first within a person's name // ('true' means that the family name is followed by the given name (or initials), 'false' means that the person's family name comes *after* the given name (or initials)) $familyNameFirst = true; // Specifies whether a person's full given name(s) shall be shortened to initial(s): // (Notes: - if set to 'true', given names will be abbreviated and initials will get normalized (meaning removal of extra whitespace, adding of dots between initials, etc) // - if set to 'false', given names (and any initials) are taken as is // - in your database, you should stick to either fully written given names OR initials; if you mix these, records won't get sorted correctly on citation output) $shortenGivenNames = true; // Specifies whether fields whose contents are entirely in upper case shall be transformed to title case ('true') or not ('false'): $transformCase = true; // Preprocessor actions: // Defines search & replace 'actions' that will be applied to each record's raw source data if the pattern in the corresponding 'match' element is matched: // (If you don't want to perform any preprocessor actions, specify an empty array, like: '$preprocessorActionsArray = array();'. // Note that, in this case, the search patterns MUST include the leading & trailing slashes -- which is done to allow for mode modifiers such as 'imsxU'.) // "/Search Pattern/" => "Replace Pattern" $preprocessorActionsArray = array(); // Postprocessor actions: // Defines search & replace 'actions' that will be applied to all those refbase fields that are listed in the corresponding 'fields' element: // (If you don't want to perform any search and replace actions, specify an empty array, like: '$postprocessorActionsArray = array();'. // Note that, in this case, the search patterns MUST include the leading & trailing slashes -- which is done to allow for mode modifiers such as 'imsxU'.) // "/Search Pattern/" => "Replace Pattern" $postprocessorActionsArray = array(array('fields' => array("year"), 'actions' => array("/^.*?(\\d{4}).*/" => "\\1", "/^\\D+\$/" => "")), array('fields' => array("pages"), 'actions' => array("/(\\d+ *pp?)\\./" => "\\1")), array('fields' => array("title", "address"), 'actions' => array("/[,.;:!] *\$/" => "", "/,(?! )/" => ", ")), array('fields' => array("abstract"), 'actions' => array('/\\\\"/' => '"', "/ *\\[on SciFinder \\(R\\)\\]\$/" => "")), array('fields' => array("language"), 'actions' => array("/^[{$lower}{$punct} ]+(?=[{$upper}][{$lower}]+)/{$patternModifiers}" => "", "/language unavailable/" => "", "/[{$punct}] *\$/{$patternModifiers}" => "")), array('fields' => array("notes"), 'actions' => array("/^Can (\\d+)/" => "CAN:\\1", "/^(\\d+)/" => "CAN:\\1"))); // This array lists patterns which match all SciFinder tags that must occur within a record to be recognized as valid SciFinder record: // (Array keys must contain the tag name as it should be displayed to the user; as is the case with search & replace actions, // the search patterns MUST include the leading & trailing slashes.) // "tag display name" => "tag search pattern" $requiredTagsArray = array("Document Type" => "/^FIELD Document Type/m"); // This array matches SciFinder tags with their corresponding refbase fields: // (fields that are unsupported in either SciFinder or refbase are commented out) // "SciFinder tag" => "refbase field" // SciFinder tag name (comment) $tagsToRefbaseFieldsArray = array("Document Type" => "type", "Author" => "author", "Corporate Source" => "address", "Title" => "title", "Publication Year" => "year", "Publication Date" => "year", "Journal Title" => "publication", "Volume" => "volume", "Issue" => "issue", "Page" => "pages", "Internat.Standard Doc. Number" => array("Book, Section" => "isbn", "Book, Edited" => "isbn", "Book" => "isbn", "Dissertation" => "isbn", "Dissertation/Thesis" => "isbn", "Other" => "issn"), "Language" => "language", "Index Terms" => "keywords", "Abstract" => "abstract", "URL" => "url", "Chemical Abstracts Number(CAN)" => "notes"); // This array lists all SciFinder tags that may occur multiple times: $tagsMultipleArray = array("Publication Year", "Publication Date"); // This array matches SciFinder reference types with their corresponding refbase types: // (SciFinder types that are currently not supported in refbase will be taken as is but will get // prefixed with an "Unsupported: " label; '#fallback#' in comments indicates a type mapping that // is not a perfect match but as close as currently possible) // (NOTE: the commented reference types are NOT from SciFinder but are remains from the 'refworksToRefbase()' function!) // "SciFinder type" => "refbase type" // name of SciFinder reference type (comment) $referenceTypesToRefbaseTypesArray = array("Book(;.*)?" => "Book Whole", "Journal(;.*)?" => "Journal Article", "Report(;.*)?" => "Report", "Preprint" => "Manuscript"); // Other SciFinder Document Types which I've encountered so far: // "General Review" => "" // General Review // "Online Computer File" => "" // Online Computer File // ----------------------------------------- // Split input text into individual records: $recordArray = splitSourceText($sourceText, $recordDelimiter, false); // split on the "START_RECORD"/"END_RECORD" tags that delimit every SciFinder record // Validate all records that shall be imported: list($errors, $importRecordNumbersRecognizedFormatArray, $importRecordNumbersNotRecognizedFormatArray) = validateRecords($recordArray, $requiredTagsArray, $importRecordsRadio, $importRecordNumbersArray, $errors); // Parse all records that shall be imported: list($parsedRecordsArray, $recordsCount) = parseRecords($recordArray, "SciFinder", $importRecordNumbersRecognizedFormatArray, $tagsToRefbaseFieldsArray, $tagsMultipleArray, $referenceTypesToRefbaseTypesArray, $fieldDelimiter, $dataDelimiter, $personDelimiter, $familyNameGivenNameDelimiter, $familyNameFirst, $shortenGivenNames, $transformCase, $postprocessorActionsArray, $preprocessorActionsArray); // Build refbase import array: $importDataArray = buildImportArray("refbase", "1.0", "http://refbase.net/import/scifinder/", "Matthias Steffens", "*****@*****.**", array('prefix_call_number' => "true"), $parsedRecordsArray); // 'records' - array of record(s) (with each record being a sub-array of fields) return array($importDataArray, $recordsCount, $importRecordNumbersRecognizedFormatArray, $importRecordNumbersNotRecognizedFormatArray, $errors); }