Browse Source

dynamically create the buttons based on what the user has entered in the past

pull/10/head
Aaron Parecki 5 years ago
parent
commit
33bd37fbe1
5 changed files with 261 additions and 88 deletions
  1. +28
    -7
      controllers/controllers.php
  2. +7
    -14
      controllers/pebble.php
  3. +167
    -21
      lib/helpers.php
  4. +31
    -46
      views/new-post.php
  5. +28
    -0
      views/partials/entry-buttons.php

+ 28
- 7
controllers/controllers.php View File

@@ -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();


+ 7
- 14
controllers/pebble.php View File

@@ -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));
}
});


+ 167
- 21
lib/helpers.php View File

@@ -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;
}


+ 31
- 46
views/new-post.php View File

@@ -3,40 +3,9 @@

<form role="form" style="margin-top: 20px;" id="note_form" action="/post" method="post">

<h3>Caffeine</h3>
<ul class="caffeine entry-buttons">
<?php foreach(caffeine_options() as $val): ?>
<li><input type="submit" name="drank" class="btn btn-default" value="<?= $val ?>"></li>
<?php endforeach; ?>
</ul>
<br>

<h3>Alcohol</h3>
<ul class="alcohol entry-buttons">
<?php foreach(alcohol_options() as $val): ?>
<li><input type="submit" name="drank" class="btn btn-default" value="<?= $val ?>"></li>
<?php endforeach; ?>
</ul>
<br>

<h3>Drank</h3>
<ul class="other entry-buttons">
<li>
<input type="text" class="form-control text-custom-drank" name="custom_drank" placeholder="Custom" style="width: 72%; float: left; margin-right: 2px;">
<input type="submit" class="btn btn-default btn-custom-drank" value="Post" style="width: 26%; float: right;">
</li>
</ul>
<br><br>

<h3>Ate</h3>
<ul class="other entry-buttons">
<li>
<input type="text" class="form-control text-custom-ate" name="custom_ate" placeholder="Custom" style="width: 72%; float: left; margin-right: 2px;">
<input type="submit" class="btn btn-default btn-custom-ate" value="Post" style="width: 26%; float: right;">
</li>
</ul>
<br><br>

<div id="entry-buttons">
<?= partial('partials/entry-buttons', ['options'=>$this->default_options]) ?>
</div>

<div class="form-group">
<h3>Location <input type="checkbox" id="note_location_chk" value=""><img src="/images/spinner.gif" id="note_location_loading" style="display: none;"></h3>
@@ -78,6 +47,10 @@
<td>p3k-food</td>
<td>The button you tap (or your custom text) will be sent to your Micropub endpoint in a field named <code>p3k-food</code></td>
</tr>
<tr>
<td>p3k-type</td>
<td>Will be either <code>drink</code> or <code>eat</code> depending on the type of entry</td>
</tr>
</table>
</div>

@@ -88,18 +61,22 @@
<script>
$(function(){

$(".text-custom-ate").keydown(function(e){
if(e.keyCode == 13) {
$(".btn-custom-ate").click();
return false;
}
});
$(".text-custom-drank").keydown(function(e){
if(e.keyCode == 13) {
$(".btn-custom-drank").click();
return false;
}
});
function bind_keyboard_shortcuts() {
$(".text-custom-eat").keydown(function(e){
if(e.keyCode == 13) {
$(".btn-custom-eat").click();
return false;
}
});
$(".text-custom-drink").keydown(function(e){
if(e.keyCode == 13) {
$(".btn-custom-drink").click();
return false;
}
});
}

bind_keyboard_shortcuts();

function location_error(msg) {
$("#note_location_msg").val(msg);
@@ -117,6 +94,14 @@ $(function(){

navigator.geolocation.getCurrentPosition(function(position){

$.get('/new/options', {
latitude: position.coords.latitude,
longitude: position.coords.longitude
}, function(response) {
$("#entry-buttons").html(response);
bind_keyboard_shortcuts();
});

$("#note_location_loading").hide();
var geo = "geo:" + (Math.round(position.coords.latitude * 100000) / 100000) + "," + (Math.round(position.coords.longitude * 100000) / 100000) + ";u=" + position.coords.accuracy;
$("#note_location_msg").val(geo);


+ 28
- 0
views/partials/entry-buttons.php View File

@@ -0,0 +1,28 @@
<?php
foreach($this->options['sections'] as $section) {
echo '<div class="entry-section" style="margin-bottom: 20px;">';
echo '<h3>' . $section['title'] . '</h3>';
echo '<ul class="entry-buttons">';
foreach($section['items'] as $item) {
echo '<li><input type="submit" name="' . $item['type'] . '" class="btn btn-default" value="' . $item['title'] . '"></li>';
}
$type = null;

if($section['title'] == 'Drinks') {
$type = 'drink';
$noun = 'Drink';
}
if($section['title'] == 'Food') {
$type = 'eat';
$noun = 'Food';
}
if($type == 'drink' || $type == 'eat') {
echo '<li>';
echo '<input type="text" class="form-control text-custom-'.$type.'" name="custom_'.$type.'" placeholder="Custom '.$noun.'" style="width: 72%; float: left; margin-right: 2px;">';
echo '<input type="submit" class="btn btn-default btn-custom-'.$type.'" value="Post" style="width: 26%; float: right;">';
echo '<div style="clear:both;"></div>';
echo '</li>';
}
echo '</ul>';
echo '</div>';
}

Loading…
Cancel
Save