Skip to content

Commit 2d546a1

Browse files
committed
issue #601 upload fieldnotes
1 parent 0462c38 commit 2d546a1

File tree

3 files changed

+218
-0
lines changed

3 files changed

+218
-0
lines changed

okapi/core/OkapiServiceRunner.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class OkapiServiceRunner
4343
'services/caches/formatters/gpx',
4444
'services/caches/formatters/garmin',
4545
'services/caches/formatters/ggz',
46+
'services/fieldnotes/upload',
4647
'services/caches/map/tile',
4748
'services/logs/capabilities',
4849
'services/logs/delete',
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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 === false) throw new InvalidParam('field_notes', "Input data not recognized.");
38+
39+
foreach ($notes 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+
);
70+
//$result = json_encode($notes, JSON_PRETTY_PRINT); // debug
71+
}
72+
return Okapi::formatted_response($request, $result);
73+
}
74+
75+
// ------------------------------------------------------------------
76+
77+
private static function parse_notes($field_notes)
78+
{
79+
$decoded_field_notes = base64_decode($field_notes, true);
80+
if ($decoded_field_notes === false) return false;
81+
82+
$multiline = self::fieldNotesTxtArea2Array($decoded_field_notes);
83+
84+
$parsed_records = [];
85+
$submittable_logtype_names = Okapi::get_submittable_logtype_names();
86+
87+
foreach ($multiline as $line) {
88+
$line = trim($line);
89+
$fields = self::CSVtoArray($line);
90+
91+
$code = $fields[0];
92+
$date = $fields[1];
93+
$type = $fields[2];
94+
if (!in_array($type, $submittable_logtype_names))
95+
throw new InvalidParam('logtype', "'$type' in not a valid logtype code.");
96+
97+
$log = nl2br($fields[3]);
98+
99+
$parsed_records[] = [
100+
'code' => $code,
101+
'date' => $date,
102+
'type' => $type,
103+
'log' => $log,
104+
];
105+
}
106+
return $parsed_records;
107+
}
108+
109+
110+
// ------------------------------------------------------------------
111+
112+
private static function fieldNotesTxtArea2Array($fieldnotes)
113+
{
114+
$output = [];
115+
$buffer = '';
116+
$start = true;
117+
118+
$lines = explode("\n", $fieldnotes);
119+
$lines = array_filter($lines); // Drop empty lines
120+
121+
foreach ($lines as $line) {
122+
if ($start) {
123+
$buffer = $line;
124+
$start = false;
125+
} else {
126+
if (strpos($line, 'OC') !== 0) {
127+
$buffer .= "\n" . $line;
128+
} else {
129+
$output[] = trim($buffer);
130+
$buffer = $line;
131+
}
132+
}
133+
}
134+
135+
if (!$start) {
136+
$output[] = trim($buffer);
137+
}
138+
139+
return $output;
140+
}
141+
142+
// ------------------------------------------------------------------
143+
144+
private static function CSVtoArray($text)
145+
{
146+
$ret = [''];
147+
$i = 0;
148+
$p = '';
149+
$s = true;
150+
151+
foreach (str_split($text) as $l) {
152+
if ('"' === $l) {
153+
$s = !$s;
154+
if ('"' === $p) {
155+
$ret[$i] .= '"';
156+
$l = '-';
157+
} elseif ('' === $p) {
158+
$l = '-';
159+
}
160+
} elseif ($s && ',' === $l) {
161+
$l = $ret[++$i] = '';
162+
} else {
163+
$ret[$i] .= $l;
164+
}
165+
$p = $l;
166+
}
167+
168+
return $ret;
169+
}
170+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
</ul>
46+
</returns>
47+
</xml>

0 commit comments

Comments
 (0)