Skip to content

Commit 9bda9e2

Browse files
committed
issue #601 upload fieldnotes
1 parent 34975e3 commit 9bda9e2

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed

okapi/core/OkapiServiceRunner.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class OkapiServiceRunner
4242
'services/caches/formatters/gpx',
4343
'services/caches/formatters/garmin',
4444
'services/caches/formatters/ggz',
45+
'services/fieldnotes/upload',
4546
'services/caches/map/tile',
4647
'services/logs/capabilities',
4748
'services/logs/delete',
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace okapi\services\fieldnotes\upload;
4+
5+
use okapi\core\Exception\InvalidParam;
6+
use okapi\core\Exception\ParamMissing;
7+
use okapi\core\Db;
8+
use okapi\core\Okapi;
9+
use okapi\core\OkapiServiceRunner;
10+
use okapi\core\Request\OkapiInternalRequest;
11+
use okapi\core\Request\OkapiRequest;
12+
use okapi\services\logs\LogsCommon;
13+
use okapi\Settings;
14+
15+
class WebService
16+
{
17+
public static function options()
18+
{
19+
return array(
20+
'min_auth_level' => 3
21+
);
22+
}
23+
24+
public static function call(OkapiRequest $request)
25+
{
26+
$result = array(
27+
'success' => false // if the installation doesn't support it
28+
);
29+
30+
if (Settings::get('OC_BRANCH') == 'oc.de')
31+
{
32+
33+
$field_notes = $request->get_parameter('field_notes');
34+
if (!$field_notes) throw new ParamMissing('field_notes');
35+
36+
$notes = self::parse_notes($field_notes);
37+
if ($notes['success'] === false) throw new InvalidParam('field_notes', "Input data not recognized.");
38+
39+
foreach ($notes['records'] as $n)
40+
{
41+
$geocache = OkapiServiceRunner::call(
42+
'services/caches/geocache',
43+
new OkapiInternalRequest($request->consumer, $request->token, array(
44+
'cache_code' => $n['code'],
45+
'fields' => 'internal_id'
46+
))
47+
);
48+
$user_id = $request->token->user_id;
49+
$geocache_id = $geocache['internal_id'];
50+
$type = Okapi::logtypename2id($n['type']);
51+
$date = date("Y-m-d H:i:s", strtotime($n['date']));
52+
$text = $n['log'];
53+
54+
Db::query("
55+
insert into field_note (
56+
user_id, geocache_id, type, date, text
57+
) values (
58+
'".Db::escape_string($user_id)."',
59+
'".Db::escape_string($geocache_id)."',
60+
'".Db::escape_string($type)."',
61+
'".Db::escape_string($date)."',
62+
'".Db::escape_string($text)."'
63+
)
64+
");
65+
66+
}
67+
$result = array(
68+
'success' => true,
69+
'totalRecords' => $notes['totalRecords'],
70+
'processedRecords' => $notes['processedRecords']
71+
);
72+
//$result = json_encode($notes, JSON_PRETTY_PRINT); // debug
73+
}
74+
return Okapi::formatted_response($request, $result);
75+
}
76+
77+
// ------------------------------------------------------------------
78+
79+
private static function parse_notes($field_notes)
80+
{
81+
$decoded_field_notes = base64_decode($field_notes, true);
82+
if ($decoded_field_notes === false) return false;
83+
84+
$multiline = self::fieldNotesTxtArea2Array($decoded_field_notes);
85+
$submittable_logtype_names = Okapi::get_submittable_logtype_names();
86+
$records = [];
87+
$totalRecords = 0;
88+
$processedRecords = 0;
89+
90+
foreach ($multiline as $line) {
91+
$totalRecords++;
92+
$line = trim($line);
93+
$fields = self::CSVtoArray($line);
94+
95+
$code = $fields[0];
96+
$date = $fields[1];
97+
$type = $fields[2];
98+
99+
if (!in_array($type, $submittable_logtype_names)) continue;
100+
101+
$log = nl2br($fields[3]);
102+
103+
$records[] = [
104+
'code' => $code,
105+
'date' => $date,
106+
'type' => $type,
107+
'log' => $log,
108+
];
109+
$processedRecords++;
110+
}
111+
return ['success' => true, 'records' => $records, 'totalRecords' => $totalRecords, 'processedRecords' => $processedRecords];
112+
}
113+
114+
115+
// ------------------------------------------------------------------
116+
117+
private static function fieldNotesTxtArea2Array($fieldnotes)
118+
{
119+
$output = [];
120+
$buffer = '';
121+
$start = true;
122+
123+
$lines = explode("\n", $fieldnotes);
124+
$lines = array_filter($lines); // Drop empty lines
125+
126+
foreach ($lines as $line) {
127+
if ($start) {
128+
$buffer = $line;
129+
$start = false;
130+
} else {
131+
if (strpos($line, 'OC') !== 0) {
132+
$buffer .= "\n" . $line;
133+
} else {
134+
$output[] = trim($buffer);
135+
$buffer = $line;
136+
}
137+
}
138+
}
139+
140+
if (!$start) {
141+
$output[] = trim($buffer);
142+
}
143+
144+
return $output;
145+
}
146+
147+
// ------------------------------------------------------------------
148+
149+
private static function CSVtoArray($text)
150+
{
151+
$ret = [''];
152+
$i = 0;
153+
$p = '';
154+
$s = true;
155+
156+
foreach (str_split($text) as $l) {
157+
if ('"' === $l) {
158+
$s = !$s;
159+
if ('"' === $p) {
160+
$ret[$i] .= '"';
161+
$l = '-';
162+
} elseif ('' === $p) {
163+
$l = '-';
164+
}
165+
} elseif ($s && ',' === $l) {
166+
$l = $ret[++$i] = '';
167+
} else {
168+
$ret[$i] .= $l;
169+
}
170+
$p = $l;
171+
}
172+
173+
return $ret;
174+
}
175+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<xml>
2+
<brief>Upload Fieldnotes</brief>
3+
<issue-id>630</issue-id>
4+
<desc>
5+
<p>Upload a set of one or more fieldnotes records.</p>
6+
</desc>
7+
<req name='field_notes'>
8+
<p>Fieldnotes consist of one or more records in CSV format. Each
9+
record comprises a geocache log consisting of four fields:</p>
10+
<ul>
11+
<li>Geocache Code</li>
12+
<li>Date</li>
13+
<li>Log Type</li>
14+
<li>Log Text</li>
15+
</ul>
16+
<p>The first three fields are simple entities, the Log Text field is
17+
different and a bit difficult as it may spread over muliple lines
18+
and it may contain quote characters. In order to preserve the structure
19+
of the records, the <i>field_notes</i> parameter must be passed as a
20+
<b>base64 encoded utf8 string</b>. UTF-16LE, UTF-16BE with or without
21+
BOM are not supported.
22+
</p>
23+
<p>Since the log type is passed as a string, its value must match the
24+
values supported by the platform (case sensitive!):
25+
<pre>
26+
[
27+
"Found it",
28+
"Didn't find it",
29+
"Comment",
30+
"Attended",
31+
"Will attend",
32+
"Archived",
33+
"Ready to search",
34+
"Temporarily unavailable"
35+
]
36+
</pre>
37+
</p>
38+
<p>Note: This service method is not supported on all installations</p>
39+
</req>
40+
<common-format-params/>
41+
<returns>
42+
<p>A dictionary of the following structure:</p>
43+
<ul>
44+
<li>success - true</li>
45+
<li>totalRecords - number of records in <i>field_notes</i></li>
46+
<li>processedRecords - number of records inserted into the database</li>
47+
</ul>
48+
</returns>
49+
</xml>

0 commit comments

Comments
 (0)