forked from opencaching/okapi
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
issue opencaching#601 upload fieldnotes
- Loading branch information
Showing
3 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
okapi/services/draftlogs/upload_fieldnotes_file/WebService.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
<?php | ||
|
||
namespace okapi\services\draftlogs\upload_fieldnotes_file; | ||
|
||
use okapi\core\Exception\InvalidParam; | ||
use okapi\core\Exception\ParamMissing; | ||
use okapi\core\Db; | ||
use okapi\core\Okapi; | ||
use okapi\core\OkapiServiceRunner; | ||
use okapi\core\Request\OkapiInternalRequest; | ||
use okapi\core\Request\OkapiRequest; | ||
use okapi\services\logs\LogsCommon; | ||
use okapi\Settings; | ||
|
||
class WebService | ||
{ | ||
public static function options() | ||
{ | ||
return array( | ||
'min_auth_level' => 3 | ||
); | ||
} | ||
|
||
public static function call(OkapiRequest $request) | ||
{ | ||
$result = array( | ||
'success' => false // if the installation doesn't support it | ||
); | ||
|
||
if (Settings::get('OC_BRANCH') == 'oc.de') | ||
{ | ||
|
||
$field_notes = $request->get_parameter('field_notes'); | ||
if (!$field_notes) throw new ParamMissing('field_notes'); | ||
|
||
$notes = self::parse_notes($field_notes); | ||
if ($notes['success'] === false) throw new InvalidParam('field_notes', "Input data not recognized."); | ||
|
||
foreach ($notes['records'] as $n) | ||
{ | ||
$geocache = OkapiServiceRunner::call( | ||
'services/caches/geocache', | ||
new OkapiInternalRequest($request->consumer, $request->token, array( | ||
'cache_code' => $n['code'], | ||
'fields' => 'internal_id' | ||
)) | ||
); | ||
$user_id = $request->token->user_id; | ||
$geocache_id = $geocache['internal_id']; | ||
$type = Okapi::logtypename2id($n['type']); | ||
$text = $n['log']; | ||
$dateString = strtotime($n['date']); | ||
if ($dateString === false) | ||
{ | ||
throw new InvalidParam('Date field in logi record', "Input data not recognized."); | ||
} | ||
else | ||
{ | ||
$date = date("Y-m-d H:i:s", $dateString); | ||
} | ||
Db::query(" | ||
insert into field_note ( | ||
user_id, geocache_id, type, date, text | ||
) values ( | ||
'".Db::escape_string($user_id)."', | ||
'".Db::escape_string($geocache_id)."', | ||
'".Db::escape_string($type)."', | ||
'".Db::escape_string($date)."', | ||
'".Db::escape_string($text)."' | ||
) | ||
"); | ||
|
||
} | ||
$result = array( | ||
'success' => true, | ||
'totalRecords' => $notes['totalRecords'], | ||
'processedRecords' => $notes['processedRecords'] | ||
); | ||
//$result = json_encode($notes, JSON_PRETTY_PRINT); // debug | ||
} | ||
return Okapi::formatted_response($request, $result); | ||
} | ||
|
||
// ------------------------------------------------------------------ | ||
|
||
private static function parse_notes($field_notes) | ||
{ | ||
$decoded_field_notes = base64_decode($field_notes, true); | ||
if ($decoded_field_notes === false) return false; | ||
|
||
$multiline = self::fieldNotesTxtArea2Array($decoded_field_notes); | ||
$submittable_logtype_names = Okapi::get_submittable_logtype_names(); | ||
$records = []; | ||
$totalRecords = 0; | ||
$processedRecords = 0; | ||
|
||
foreach ($multiline as $line) { | ||
$totalRecords++; | ||
$line = trim($line); | ||
$fields = self::CSVtoArray($line); | ||
|
||
$code = $fields[0]; | ||
$date = $fields[1]; | ||
$type = $fields[2]; | ||
|
||
if (!in_array($type, $submittable_logtype_names)) continue; | ||
|
||
$log = nl2br($fields[3]); | ||
|
||
$records[] = [ | ||
'code' => $code, | ||
'date' => $date, | ||
'type' => $type, | ||
'log' => $log, | ||
]; | ||
$processedRecords++; | ||
} | ||
return ['success' => true, 'records' => $records, 'totalRecords' => $totalRecords, 'processedRecords' => $processedRecords]; | ||
} | ||
|
||
|
||
// ------------------------------------------------------------------ | ||
|
||
private static function fieldNotesTxtArea2Array($fieldnotes) | ||
{ | ||
$output = []; | ||
$buffer = ''; | ||
$start = true; | ||
|
||
$lines = explode("\n", $fieldnotes); | ||
$lines = array_filter($lines); // Drop empty lines | ||
|
||
foreach ($lines as $line) { | ||
if ($start) { | ||
$buffer = $line; | ||
$start = false; | ||
} else { | ||
if (strpos($line, 'OC') !== 0) { | ||
$buffer .= "\n" . $line; | ||
} else { | ||
$output[] = trim($buffer); | ||
$buffer = $line; | ||
} | ||
} | ||
} | ||
|
||
if (!$start) { | ||
$output[] = trim($buffer); | ||
} | ||
|
||
return $output; | ||
} | ||
|
||
// ------------------------------------------------------------------ | ||
|
||
private static function CSVtoArray($text) | ||
{ | ||
$ret = ['']; | ||
$i = 0; | ||
$p = ''; | ||
$s = true; | ||
|
||
foreach (str_split($text) as $l) { | ||
if ('"' === $l) { | ||
$s = !$s; | ||
if ('"' === $p) { | ||
$ret[$i] .= '"'; | ||
$l = '-'; | ||
} elseif ('' === $p) { | ||
$l = '-'; | ||
} | ||
} elseif ($s && ',' === $l) { | ||
$l = $ret[++$i] = ''; | ||
} else { | ||
$ret[$i] .= $l; | ||
} | ||
$p = $l; | ||
} | ||
|
||
return $ret; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<xml> | ||
<brief>Upload Fieldnotes</brief> | ||
<issue-id>630</issue-id> | ||
<desc> | ||
<p>Upload a set of one or more fieldnotes records.</p> | ||
</desc> | ||
<req name='field_notes' infotags='ocde-specific'> | ||
<p>This method allows you to upload a series of fieldnotes in the CSV format. | ||
Fieldnotes are draft versions of log entries. Once uploaded, users will be able | ||
to review, edit, and submit them via the Opencaching site.</p> | ||
|
||
<p>Fieldnotes do not have a CSV header in the file that might describe the columns.</p> | ||
<p>Each record describes a geocache log consisting of four fields:</p> | ||
<ol> | ||
<li>Geocache Code</li> | ||
<li>Date</li> | ||
<li>Log Type</li> | ||
<li>Log Text</li> | ||
</ol> | ||
<p>The first three fields are string entities that don't have line control characters in them, | ||
the Log Text field is different and a bit difficult as it may spread over muliple lines | ||
and it may contain quote characters. In order to preserve the structure | ||
of the records, the <i>field_notes</i> parameter must be passed as a | ||
<b>base64 encoded utf8 string</b>. UTF-16LE, UTF-16BE with or without | ||
BOM are not supported. | ||
</p> | ||
<p>The second field <i>Date</i> should be in ISO 8601 format (currently any format | ||
acceptable by PHP's strtotime function also will do, but most of them don't handle | ||
time zones properly, try to use ISO 8601!).</p> | ||
<p>Since the log type is passed as a string, its value must match the | ||
values supported by the platform (case sensitive!):</p> | ||
<pre> | ||
[ | ||
"Found it", | ||
"Didn't find it", | ||
"Comment", | ||
"Attended", | ||
"Will attend", | ||
"Archived", | ||
"Ready to search", | ||
"Temporarily unavailable" | ||
] | ||
</pre> | ||
<p>Note: This service method is not supported on all installations</p> | ||
</req> | ||
<common-format-params/> | ||
<returns> | ||
<p>A dictionary of the following structure:</p> | ||
<ul> | ||
<li>success - true</li> | ||
<li>totalRecords - number of records in <i>field_notes</i></li> | ||
<li>processedRecords - number of records inserted into the database</li> | ||
</ul> | ||
<p>processedRecords may be less than totalRecords (it may even be zero) and that | ||
is the case for the following reason: Fieldnotes files are created from | ||
Geocaching client applications on Smartphones. These applications support multiple | ||
geocaching platforms from which opencaching is only one of them. Conseqently the | ||
Fieldnotes file is a "hybrid file" which contains records for multiple different | ||
platforms. For instance for geocaching.com logs the records start with <b>GC....</b> | ||
while of opencaching the log records start with <b>OC....</b>. The client application | ||
uploads one and the same Fielnotes file to all platforms and it is the platform's | ||
task to filter out what matches their objects. Geocaching.com will discard "OC logs" | ||
and opencaching must discard "GC logs" in that file.</p> | ||
<p>In addition, in that hybrid file there will be <i>Log Type</i>, a string that | ||
inevitably has a different definition for the various platforms. For instance, what | ||
is called a "Write note" log on geocaching.com is recognized s "Comments" on | ||
opencaching platforms. Consequently fieldnotes records which have a log type which | ||
is not understood by the opencaching platform will be discarded without notice.</p> | ||
<p>It is the responsibility of the client application to assign the correct log type | ||
string when the offline log is created</p> | ||
</returns> | ||
</xml> |