diff --git a/controllers/controllers.php b/controllers/controllers.php index 221e923..8301b8b 100644 --- a/controllers/controllers.php +++ b/controllers/controllers.php @@ -46,13 +46,19 @@ $app->get('/new', function() use($app) { $entry = false; $photo_url = false; + // Initially populate the page with the list of options without considering location. + // This way if browser location is disabled or not available, or JS is disabled, there + // will still be a list of options presented on the page by the time it loads. + // Javascript will replace the options after location is available. + $html = render('new-post', array( 'title' => 'New Post', 'micropub_endpoint' => $user->micropub_endpoint, 'token_scope' => $user->token_scope, 'access_token' => $user->access_token, 'response_date' => $user->last_micropub_response_date, - 'location_enabled' => $user->location_enabled + 'location_enabled' => $user->location_enabled, + 'default_options' => get_entry_options($user->id) )); $app->response()->body($html); } @@ -151,19 +157,24 @@ $app->post('/post', function() use($app) { } if(k($params, 'drank')) { - $entry->content = $params['drank']; + $entry->content = trim($params['drank']); $type = 'drink'; } elseif(k($params, 'drink')) { - $entry->content = $params['drink']; + $entry->content = trim($params['drink']); $type = 'drink'; - } elseif(k($params, 'custom_drank')) { - $entry->content = $params['custom_drank']; + } elseif(k($params, 'eat')) { + $entry->content = trim($params['eat']); + $type = 'eat'; + } elseif(k($params, 'custom_drink')) { + $entry->content = trim($params['custom_drink']); $type = 'drink'; - } elseif(k($params, 'custom_ate')) { - $entry->content = $params['custom_ate']; + } elseif(k($params, 'custom_eat')) { + $entry->content = trim($params['custom_eat']); $type = 'eat'; } + $entry->type = $type; + $entry->save(); // Send to the micropub endpoint if one is defined, and store the result @@ -206,6 +217,16 @@ $app->post('/post', function() use($app) { } }); +$app->get('/new/options', function() use($app) { + if($user=require_login($app)) { + $params = $app->request()->params(); + + $options = get_entry_options($user->id, $params['latitude'], $params['longitude']); + $html = partial('partials/entry-buttons', ['options'=>$options]); + + $app->response()->body($html); + } +}); $app->get('/map.png', function() use($app) { $params = $app->request()->params(); diff --git a/controllers/pebble.php b/controllers/pebble.php index 069d09f..daf0868 100644 --- a/controllers/pebble.php +++ b/controllers/pebble.php @@ -32,20 +32,13 @@ $app->get('/pebble/settings/finished', function() use($app) { }); $app->get('/pebble/options.json', function() use($app) { - // TODO: if a token is provided, return the user's custom list + if($user=require_login($app)) { + $params = $app->request()->params(); + + $options = get_entry_options($user->id, k($params,'latitude'), k($params,'longitude')); - $app->response()['Content-Type'] = 'application/json'; - $app->response()->body(json_encode(array( - 'sections' => array( - array( - 'title' => 'Caffeine', - 'items' => array_map(function($e){ return array('title'=>$e, 'type'=>'drink'); }, caffeine_options()) - ), - array( - 'title' => 'Alcohol', - 'items' => array_map(function($e){ return array('title'=>$e, 'type'=>'drink'); }, alcohol_options()) - ) - ) - ))); + $app->response()['Content-Type'] = 'application/json'; + $app->response()->body(json_encode($options)); + } }); diff --git a/lib/helpers.php b/lib/helpers.php index 3a86c2b..ef6bbc5 100644 --- a/lib/helpers.php +++ b/lib/helpers.php @@ -198,30 +198,176 @@ function entry_date($entry, $user) { return $date; } -function caffeine_options() { - return array( +function default_drink_options() { + return [ 'Coffee', - 'Americano', - 'Latte', - 'Cappuccino', - 'Espresso', - 'Iced Coffee', - 'Iced Americano', - 'Iced Latte', - 'Black Tea', - 'Tea' - ); -} - -function alcohol_options() { - return array( 'Beer', 'Cocktail', + 'Tea', 'Mimosa', - 'Champagne', - 'Wine', - 'Sake', - 'Cider' - ); + 'Latte', + 'Champagne' + ]; +} + +function default_food_options() { + return [ + 'Burrito', + 'Banana', + 'Pizza', + 'Soup', + 'Tacos', + 'Mac and Cheese' + ]; +} + +function query_user_nearby_options($type, $user_id, $latitude, $longitude) { + $published = date('Y-m-d H:i:s', strtotime('-4 months')); + $options = []; + $optionsQ = ORM::for_table('entries')->raw_query(' + SELECT *, SUM(num) AS num FROM + ( + SELECT id, published, content, type, + round(gc_distance(latitude, longitude, :latitude, :longitude) / 1000) AS dist, + COUNT(1) AS num + FROM entries + WHERE user_id = :user_id + AND type = :type + AND gc_distance(latitude, longitude, :latitude, :longitude) IS NOT NULL + AND published > :published /* only look at the last 4 months of posts */ + GROUP BY content, round(gc_distance(latitude, longitude, :latitude, :longitude) / 1000) /* group by 1km buckets */ + ORDER BY round(gc_distance(latitude, longitude, :latitude, :longitude) / 1000), COUNT(1) DESC /* order by distance and frequency */ + ) AS tmp + WHERE num > 2 /* only include things that have been used more than 2 times in this 1km bucket */ + GROUP BY content /* group by name again */ + ORDER BY SUM(num) DESC /* order by overall frequency */ + LIMIT 4 + ', ['user_id'=>$user_id, 'type'=>$type, 'latitude'=>$latitude, 'longitude'=>$longitude, 'published'=>$published])->find_many(); + foreach($optionsQ as $o) { + $options[] = [ + 'title' => $o->content, + 'type' => $o->type + ]; + } + return $options; +} + +function query_user_frequent_options($type, $user_id) { + $published = date('Y-m-d H:i:s', strtotime('-4 months')); + $options = []; + $optionsQ = ORM::for_table('entries')->raw_query(' + SELECT type, content + FROM entries + WHERE user_id = :user_id + AND type = :type + AND published > :published + GROUP BY content + ORDER BY COUNT(1) DESC + LIMIT 2 + ', ['user_id'=>$user_id, 'type'=>$type, 'published'=>$published])->find_many(); + foreach($optionsQ as $o) { + $options[] = [ + 'title' => $o->content, + 'type' => $o->type + ]; + } + return $options; +} + +function get_entry_options($user_id, $latitude=null, $longitude=null) { + /* + Sections: + * Recent 2 posts (food + drink combined) + * Drinks (based on location) + * custom box below + * Food (based on location) + * custom box below + + If no recent entries, remove that section. + If no nearby food/drinks, use a default list + */ + + $recent = []; + $drinks = []; + $food = []; + + $recentQ = ORM::for_table('entries')->raw_query(' + SELECT type, content FROM + (SELECT * + FROM entries + WHERE user_id = :user_id + ORDER BY published DESC) AS tmp + GROUP BY content + ORDER BY MAX(published) DESC + LIMIT 4', ['user_id'=>$user_id])->find_many(); + foreach($recentQ as $r) { + $recent[] = [ + 'title' => $r->content, + 'type' => $r->type + ]; + } + + if($latitude) { + $drinks = query_user_nearby_options('drink', $user_id, $latitude, $longitude); + } + // If there's no nearby data (like if the user isn't including location) then return the most frequently used ones instead + if(count($drinks) == 0) { + $drinks = query_user_frequent_options('drink', $user_id); + } + // If there's less than 4 options available, fill the list with the default options + if(count($drinks) < 4) { + $default = default_drink_options(); + while(count($drinks) < 4) { + $next = array_shift($default); + if(!in_array(['title'=>$next,'type'=>'drink'], $drinks)) { + $drinks[] = [ + 'title' => $next, + 'type' => 'drink' + ]; + } + } + } + + if($latitude) { + $food = query_user_nearby_options('eat', $user_id, $latitude, $longitude); + } + if(count($food) == 0) { + $food = query_user_frequent_options('eat', $user_id); + } + if(count($food) < 4) { + $default = default_food_options(); + while(count($food) < 4) { + $next = array_shift($default); + if(!in_array(['title'=>$next,'type'=>'eat'], $food)) { + $food[] = [ + 'title' => $next, + 'type' => 'eat' + ]; + } + } + } + + + $options = [ + 'sections' => [ + [ + 'title' => 'Recent', + 'items' => $recent + ], + [ + 'title' => 'Drinks', + 'items' => $drinks + ], + [ + 'title' => 'Food', + 'items' => $food + ] + ] + ]; + + if(count($options['sections'][0]['items']) == 0) + array_shift($options['sections']); + + return $options; } diff --git a/views/new-post.php b/views/new-post.php index a07f0ea..1a64eb5 100644 --- a/views/new-post.php +++ b/views/new-post.php @@ -3,40 +3,9 @@
-

Caffeine

- -
- -

Alcohol

- -
- -

Drank

- -

- -

Ate

- -

- +
+ $this->default_options]) ?> +

Location

@@ -78,6 +47,10 @@ p3k-food The button you tap (or your custom text) will be sent to your Micropub endpoint in a field named p3k-food + + p3k-type + Will be either drink or eat depending on the type of entry +
@@ -88,18 +61,22 @@