Merge branch 'master' of gitlab.com:jankstudio/resq into 194-catalyst-api-bundle-acl

This commit is contained in:
Kendrick Chan 2019-06-05 11:37:53 +08:00
commit 29f574e25c
35 changed files with 1104 additions and 136 deletions

View file

@ -28,3 +28,10 @@ RT_SHORTCODE=1234
MQTT_IP_ADDRESS=localhost
MQTT_PORT=8883
MQTT_CERT=/location/of/cert/file.crt
# redis client
REDIS_CLIENT_SCHEME=tcp
REDIS_CLIENT_HOST=127.0.0.1
REDIS_CLIENT_PORT=6379
REDIS_CLIENT_PASSWORD=foobared
#

View file

@ -72,14 +72,19 @@ services:
App\Service\MQTTClient:
arguments:
$ip_address: "%env(MQTT_IP_ADDRESS)%"
$port: "%env(MQTT_PORT)%"
$cert: "%env(MQTT_CERT)%"
$redis_client: "@App\\Service\\RedisClientProvider"
App\Service\APNSClient:
arguments:
$ip_address: "%env(APNS_REDIS_IP_ADDRESS)%"
$port: "%env(APNS_REDIS_PORT)%"
$redis_client: "@App\\Service\\RedisClientProvider"
App\Service\RedisClientProvider:
arguments:
$scheme: "%env(REDIS_CLIENT_SCHEME)%"
$host: "%env(REDIS_CLIENT_HOST)%"
$port: "%env(REDIS_CLIENT_PORT)%"
$password: "%env(REDIS_CLIENT_PASSWORD)%"
$env_flag: "dev"
Catalyst\APIBundle\Security\APIKeyUserProvider:
arguments:

View file

@ -0,0 +1,104 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\VehicleManufacturer;
use App\Entity\Vehicle;
use App\Entity\Battery;
use App\Entity\BatteryManufacturer;
use App\Entity\BatteryModel;
use App\Entity\BatterySize;
class GenerateBatteryCompatibilityCommand extends Command
{
protected $em;
protected $vmfg_index;
public function __construct(ObjectManager $om)
{
$this->em = $om;
parent::__construct();
}
protected function populateVehicleManufacturerIndex()
{
$vms = $this->em->getRepository(VehicleManufacturer::class)->findAll();
$this->vmfg_index = [];
foreach ($vms as $vm)
{
$mfg_name = $vm->getName();
// $this->vmfg_index[$mfg_name] = [];
// get vehicles from manufacturer
$make_array = [];
$vehicles = $vm->getVehicles();
foreach ($vehicles as $vehicle)
{
$batteries = $vehicle->getBatteries();
$comp_batt = [];
foreach ($batteries as $battery)
{
// set to the first compatible battery found until a more expensive one is found
$comp_batt['id'] = $battery->getID();
$comp_batt['name'] = $battery->getModel()->getName() . ' ' .
$battery->getSize()->getName();
$comp_batt['image_url'] = '/assets/img/products/' . strtolower($battery->getModel()->getName()) . '.png';
$comp_batt['description'] = '';
//$comp_batt['description'] = $battery->getDescription();
// store the selling price for comparison
$batt_price = $battery->getSellingPrice();
// find the most expensive compatible battery
if ($battery->getSellingPrice() > $batt_price)
{
$comp_batt['id'] = $battery->getID();
$comp_batt['name'] = $battery->getModel()->getName() . ' ' .
$battery->getSize()->getName();
$comp_batt['image_url'] = $battery->getImageFile();
//$comp_batt['description'] = $battery->getDescription();
}
}
// check if no compatible batteries
if (!empty($comp_batt))
$make_array[$vehicle->getMake()][$vehicle->getModelYearFormatted()] = $comp_batt;
}
// check if empty
if (!empty($make_array))
$this->vmfg_index[$mfg_name] = $make_array;
}
}
protected function configure()
{
$this->setName('battery:generate_compatibility_json')
->setDescription('Generate a json file containing the vehicles and their compatible batteries')
->setHelp('Generate a json file containing the vehicles and their compatible batteries')
->addArgument('file', InputArgument::REQUIRED, 'Path and name for JSON file.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$filename = $input->getArgument('file');
$this->populateVehicleManufacturerIndex();
$json_file = fopen($filename, 'w');
fwrite($json_file, json_encode($this->vmfg_index, JSON_PRETTY_PRINT));
fclose($json_file);
}
}

View file

@ -0,0 +1,215 @@
<?php
namespace App\Command;
use DateTime;
use DateInterval;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\JobOrder;
use App\Entity\Warranty;
use App\Entity\SAPBattery;
use App\Ramcar\ServiceType;
use App\Ramcar\WarrantyStatus;
class GenerateWarrantyFromJobOrderCommand extends Command
{
protected $em;
protected $sapbatt_hash;
protected $warranties_hash;
public function __construct(ObjectManager $em)
{
$this->em = $em;
$this->loadSAPBatteries();
$this->loadWarranties();
parent::__construct();
}
protected function configure()
{
$this->setName('warranty:generate')
->setDescription('Generates warranty from job order and inserts into database')
->setHelp('Generate warranty from job order');
}
protected function computeDateExpire($date_create, $warranty_period)
{
$expire_date = clone $date_create;
$expire_date->add(new DateInterval('P'.$warranty_period.'M'));
return $expire_date;
}
protected function loadSAPBatteries()
{
$this->sapbatt_hash = [];
$sap_batteries = $this->em->getRepository(SAPBattery::class)->findAll();
foreach($sap_batteries as $sap_batt)
{
$id = $sap_batt->getID();
$brand_size = $sap_batt->getBrand()->getID() . " " . $sap_batt->getSize()->getID();
$this->sapbatt_hash[$id] = $brand_size;
}
}
protected function loadWarranties()
{
$this->warranties_hash = [];
/*
$warranties = $this->em->getRepository(Warranty::class)->findAll();
foreach($warranties as $warranty)
{
$plate_number = $warranty->getPlateNumber();
$date_expire = $warranty->getDateExpire();
// skip null date expire
if ($date_expire == null)
continue;
$expiry_date = $date_expire->format('Y-m-d');
$this->warranties_hash[$plate_number][$expiry_date] = $warranty->getID();
}
*/
}
protected function findSAPBattery($batt_id)
{
if (!isset($this->sapbatt_hash[$batt_id]))
{
return false;
}
return true;
}
protected function findWarranty($plate_number, $expiry_date)
{
$date_expire = $expiry_date->format('Y-m-d');
if (!isset($this->warranties_hash[$plate_number][$date_expire]))
{
return false;
}
return true;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->em;
// to save on joins, go with invoice item first
$query = $em->createQuery('select ii,i,jo,cv from App\Entity\InvoiceItem ii inner join ii.invoice i inner join i.job_order jo inner join jo.cus_vehicle cv join jo.customer c where ii.battery is not null and jo.service_type = :service_type');
$query->setParameter('service_type', ServiceType::BATTERY_REPLACEMENT_NEW);
/*
$query = $em->createQuery('SELECT jo FROM App\Entity\JobOrder jo
WHERE jo.service_type = :service_type');
$query->setParameter('service_type', ServiceType::BATTERY_REPLACEMENT_NEW);
*/
$result = $query->iterate();
foreach ($result as $row)
{
$invoice_item = $row[0];
$invoice = $invoice_item->getInvoice();
$jo = $invoice->getJobOrder();
$cv = $jo->getCustomerVehicle();
$customer = $jo->getCustomer();
/*
$invoice_items = [];
// for now, one invoice == one battery
$invoice_items = $jo->getInvoice()->getItems();
$invoice_item = $invoice_items[0];
*/
if ($invoice_item != null)
{
if($invoice_item->getBattery() != null)
{
// manually retrieve the SAPBattery using the SAPCode
$battery_sap_code = $invoice_item->getBattery()->getSAPCode();
$found_battery = $this->findSAPBattery($battery_sap_code);
$sap_code = '\'' . $battery_sap_code . '\'';
if (!$found_battery)
{
$sap_code = 'NULL';
}
// get warranty period for battery
// TODO: base it on the battery type
$warranty_period = 0;
if ($invoice_item->getBattery()->getWarrantyPrivate() != null)
{
$warranty_period = $invoice_item->getBattery()->getWarrantyPrivate();
}
if ($invoice_item->getBattery()->getWarrantyCommercial() != null)
{
$warranty_period = $invoice_item->getBattery()->getWarrantyCommercial();
}
if ($invoice_item->getBattery()->getWarrantyTnv() != null)
{
$warranty_period = $invoice_item->getBattery()->getWarrantyTnv();
}
if ($invoice->getDateCreate() != null)
{
// check if plate number is "clean". If not, do not insert into warranty
if (!(Warranty::cleanPlateNumber($cv->getPlateNumber())))
{
continue;
}
// check if warranty already exists
$cleaned_plate_number = Warranty::cleanPlateNumber($cv->getPlateNumber());
$expiry_date = $this->computeDateExpire($jo->getInvoice()->getDateCreate(), $warranty_period);
$found_warranty = $this->findWarranty($cleaned_plate_number, $expiry_date);
if (!$found_warranty)
{
$bty_model_id = $invoice_item->getBattery()->getModel()->getID();
$bty_size_id = $invoice_item->getBattery()->getSize()->getID();
$warranty_class = $jo->getWarrantyClass();
$date = $jo->getInvoice()->getDateCreate();
$date_purchase = $date->format('Y-m-d');
$date_create = date('Y-m-d H:i:s');
$date_expire = $expiry_date->format('Y-m-d');
$first_name = addslashes(trim($customer->getFirstName()));
$last_name = addslashes(trim($customer->getLastName()));
$mobile_number = addslashes(trim($customer->getPhoneMobile()));
$values = '(' . $bty_model_id . ',' . $bty_size_id . ',NULL,\'' . $warranty_class . '\',\''
. $cleaned_plate_number . '\',\'' . WarrantyStatus::ACTIVE . '\',\'' . $date_create . '\',\'' . $date_purchase
. '\',\'' . $date_expire . '\',NULL,'
. $sap_code . ',NULL,\'' . $first_name . '\',\'' . $last_name . '\',\'' . $mobile_number . '\');';
$sql_statement = 'INSERT INTO `warranty` (bty_model_id,bty_size_id,serial,warranty_class,plate_number,status,date_create,date_purchase,date_expire,date_claim,sap_bty_id,claim_id,first_name,last_name,mobile_number) VALUES ' . $values . "\n";
echo $sql_statement;
}
}
}
}
$em->detach($row[0]);
$em->clear();
}
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Service\HubCounter;
class TestHubCounterCommand extends Command
{
protected function configure()
{
$this->setName('test:hubcounter')
->setDescription('Test hubcounter service. Currently tests addAvailableRider and getAvailableRiderCount.')
->setHelp('Test hubcounter service. Currently tests addAvailableRider and getAvailableRiderCount.')
->addArgument('hub_id', InputArgument::REQUIRED, 'Hub ID');
}
public function __construct(HubCounter $hc)
{
$this->hc = $hc;
parent::__construct();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$hub_id = $input->getArgument('hub_id');
$this->hc->addAvailableRider($hub_id);
$available_rc = $this->hc->getAvailableRiderCount($hub_id);
echo "Available Riders in Hub: " . $available_rc . "\n";
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Service\JobOrderManager;
class TestJobOrderManagerCommand extends Command
{
protected function configure()
{
$this->setName('test:fulfilljoborder')
->setDescription('Test fulfill job order service.')
->setHelp('Test the fulfill job order service.')
->addArgument('job_order_id', InputArgument::REQUIRED, 'Job Order ID');
}
public function __construct(JobOrderManager $jo_manager)
{
$this->jo_manager = $jo_manager;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$jo_id = $input->getArgument('job_order_id');
$result = $this->jo_manager->fulfillJobOrder($jo_id);
if ($result)
echo "Job Order successfully updated" . "\n";
else
echo "Job Order not updated" . "\n";
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Service\APNSClient;
use DateTime;
class TestRegAPNSCommand extends Command
{
protected $apns;
public function __construct( APNSClient $apns)
{
$this->apns = $apns;
parent::__construct();
}
protected function configure()
{
$this->setName('apns:test_reg')
->setDescription('Test new format for Apple Push Notification.')
->setHelp('Test new format for Apple Push Notification.')
->addArgument('push_id', InputArgument::REQUIRED, 'push_id');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$push_id = $input->getArgument('push_id');
$this->apns->sendReg($push_id, 'Test Delay', 'Body');
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
use App\Service\RiderTracker;
class TestRiderTrackerCommand extends Command
{
protected function configure()
{
$this->setName('test:ridertracker')
->setDescription('Test the rider tracker service')
->setHelp('Test the rider tracker service.')
->addArgument('rider_id', InputArgument::REQUIRED, 'Rider ID');
}
public function __construct(RiderTracker $rtracker)
{
$this->rtracker = $rtracker;
parent::__construct();
}
public function execute(InputInterface $input, OutputInterface $output)
{
$rider_id = $input->getArgument('rider_id');
$coordinates = $this->rtracker->getRiderLocation($rider_id);
if ($coordinates != null)
{
echo "Rider Location: " . $coordinates->getLongitude() . "," . $coordinates->getLatitude() . "\n";
}
else
{
echo "Invalid rider id." . "\n";
}
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\Common\Persistence\ObjectManager;
use App\Entity\JobOrder;
use App\Ramcar\JOStatus;
class UpdateCustomerVehicleBatteryCommand extends Command
{
protected $em;
protected $custvehicle_hash;
public function __construct(ObjectManager $om)
{
$this->em = $om;
$this->custvehicle_hash = [];
parent::__construct();
}
protected function configure()
{
$this->setName('joborder:updatebattery')
->setDescription('Update customer vehicle battery information.')
->setHelp('Updates the customer vehicle battery based on the latest battery purchased. ');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
// get all job orders with fulfilled status
$jos = $this->em->getRepository(JobOrder::class)->findBy(['status' => JOStatus::FULFILLED], ['date_create' => 'DESC']);
foreach ($jos as $jo)
{
if ($jo->getCustomerVehicle() != null)
{
$custvehicle_id = $jo->getCustomerVehicle()->getID();
// check if custvehicle is in hash. This would mean that the battery has already been set
if(!isset($this->custvehicle_hash[$custvehicle_id]))
{
// get battery purchased from job order
$items = $jo->getInvoice()->getItems();
foreach($items as $item)
{
if ($item->getBattery() != null)
{
// set battery of vehicle to the latest battery purchased
$new_battery = $item->getBattery();
$jo->getCustomerVehicle()->setCurrentBattery($new_battery);
// add customer vehicle to hash
$this->custvehicle_hash[$custvehicle_id] = true;
}
}
$this->em->flush();
}
}
}
}
}

View file

@ -26,6 +26,7 @@ use App\Service\InvoiceCreator;
use App\Service\RisingTideGateway;
use App\Service\MQTTClient;
use App\Service\GeofenceTracker;
use App\Service\RiderTracker;
use App\Entity\MobileSession;
use App\Entity\Customer;
@ -1116,7 +1117,7 @@ class APIController extends Controller
return $res->getReturnResponse();
}
public function getRiderStatus(Request $req)
public function getRiderStatus(Request $req, RiderTracker $rt)
{
$required_params = [];
$em = $this->getDoctrine()->getManager();
@ -1243,8 +1244,9 @@ class APIController extends Controller
case JOStatus::ASSIGNED:
case JOStatus::IN_TRANSIT:
case JOStatus::IN_PROGRESS:
$coord = $jo->getHub()->getCoordinates();
$rider = $jo->getRider();
// get rider coordinates from redis
$coord = $rt->getRiderLocation($rider->getID());
// default image url
$url_prefix = $req->getSchemeAndHttpHost();

View file

@ -9,6 +9,7 @@ use Doctrine\ORM\Query;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use App\Menu\Generator as MenuGenerator;
use App\Access\Generator as ACLGenerator;
@ -275,7 +276,19 @@ class RoleController extends BaseController
], 422);
} else {
// validated! save the entity
$em->flush();
// catch the exception in case user updated the id
try
{
$em->flush();
}
catch(ForeignKeyConstraintViolationException $e)
{
$error_array['id'] = 'Role has already been assigned to user/s and id cannot be updated';
return $this->json([
'success' => false,
'errors' => $error_array
], 403);
}
// return successful response
return $this->json([

View file

@ -162,30 +162,37 @@ class Warranty
// trim and make upper case
$clean_plate = strtoupper(trim($plate));
// remove invalid characters
$clean_plate = preg_replace("/[^A-Z0-9]/", '', $clean_plate);
// check for 4 to 5 digit diplomatic plate
$res = preg_match("/^[0-9]{4,5}$/", $clean_plate);
if ($res)
return $clean_plate;
// ABC-1234 or ABC-123 or ABC-12 format
$res = preg_match("/^[A-Z]{3}[0-9]{2,4}$/", $clean_plate);
if ($res)
return $clean_plate;
// AB-123 or AB-12345 or AB-1234 format (motorcycles)
$res = preg_match("/^[A-Z]{2}[0-9]{3,5}$/", $clean_plate);
if ($res)
return $clean_plate;
// 1234-AB format (motorcycles)
$res = preg_match("/^[0-9]{4}[A-Z]{2}$/", $clean_plate);
// check if alphanumeric, max length is 11, no spaces
$res = preg_match("/^[A-Z0-9]{1,11}+$/", $clean_plate);
if ($res)
return $clean_plate;
return false;
// remove invalid characters
//$clean_plate = preg_replace("/[^A-Z0-9]/", '', $clean_plate);
// check for 4 to 5 digit diplomatic plate
//$res = preg_match("/^[0-9]{4,5}$/", $clean_plate);
//if ($res)
// return $clean_plate;
// ABC-1234 or ABC-123 or ABC-12 format
//$res = preg_match("/^[A-Z]{3}[0-9]{2,4}$/", $clean_plate);
//if ($res)
// return $clean_plate;
// AB-123 or AB-12345 or AB-1234 format (motorcycles)
//$res = preg_match("/^[A-Z]{2}[0-9]{3,5}$/", $clean_plate);
//if ($res)
// return $clean_plate;
// 1234-AB format (motorcycles)
//$res = preg_match("/^[0-9]{4}[A-Z]{2}$/", $clean_plate);
//if ($res)
// return $clean_plate;
//return false;
}
public function setPlateNumber($plate)

View file

@ -5,25 +5,24 @@ namespace App\Service;
use Mosquitto\Client as MosquittoClient;
use App\Entity\JobOrder;
use App\Ramcar\MobileOSType;
use Redis;
class APNSClient
{
// TODO: make this a config entry
const REDIS_KEY = 'apns_push';
// protected $mclient;
protected $redis;
public function __construct($ip_address, $port)
public function __construct(RedisClientProvider $redis_client)
{
$this->redis = new Redis();
$this->redis->connect($ip_address, $port);
$this->redis = $redis_client->getRedisClient();
}
public function __destruct()
{
public function __destruct()
{
// $this->mclient->disconnect();
}
}
public function push($token, $message)
{
@ -53,4 +52,9 @@ class APNSClient
$this->push($push_id, $message);
}
}
public function sendReg($push_id, $message)
{
$this->push($push_id, $message);
}
}

View file

@ -15,13 +15,10 @@ class HubCounter
protected $em;
protected $redis;
public function __construct(EntityManagerInterface $em)
public function __construct(EntityManagerInterface $em, RedisClientProvider $redis_client)
{
$this->em = $em;
// TODO: make it read redis settings from config
// build a service maybe?
$this->redis = new Predis\Client();
$this->redis = $redis_client->getRedisClient();
}
// get rider key based on id
@ -45,7 +42,7 @@ class HubCounter
return $value;
// not in cache
$hub = $em->getRepository(Hub::class)->find($hub_id);
$hub = $this->em->getRepository(Hub::class)->find($hub_id);
$riders = $hub->getActiveRiders();
$rider_count = count($riders);

View file

@ -0,0 +1,53 @@
<?php
namespace App\Service;
use App\Entity\JobOrder;
use App\Ramcar\ServiceType;
use Doctrine\ORM\EntityManagerInterface;
class JobOrderManager
{
protected $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function fulfillJobOrder($jo_id)
{
$results = $this->em->getRepository(JobOrder::class)->findby(array('id' => $jo_id, 'service_type' => ServiceType::BATTERY_REPLACEMENT_NEW));
if (isset($results[0]))
{
$jo = $results[0];
$jo->fulfill();
$cust_vehicle = $jo->getCustomerVehicle();
$invoice = $jo->getInvoice();
$this->updateCustomerVehicleBattery($cust_vehicle, $invoice);
return true;
}
return false;
}
protected function updateCustomerVehicleBattery($cust_vehicle, $invoice)
{
if (($cust_vehicle != null) && ($invoice != null))
{
$items = $invoice->getItems();
foreach ($items as $item)
{
$new_battery = $item->getBattery();
$cust_vehicle->setCurrentBattery($new_battery);
}
$this->em->flush();
}
}
}

View file

@ -4,7 +4,6 @@ namespace App\Service;
use Mosquitto\Client as MosquittoClient;
use App\Entity\JobOrder;
use Redis;
class MQTTClient
{
@ -15,27 +14,15 @@ class MQTTClient
// protected $mclient;
protected $redis;
public function __construct($ip_address, $port, $cert)
public function __construct(RedisClientProvider $redis_client)
{
// we use redis now
// we have an mqtt server listening on redis and sending to mqtt
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
/*
error_log("connecting to mqtt server - $ip_address : $port");
error_log("using $cert");
$this->mclient = new MosquittoClient();
$this->mclient->setTlsCertificates($cert);
$this->mclient->setTlsOptions(MosquittoClient::SSL_VERIFY_NONE, 'tlsv1');
$this->mclient->connect($ip_address, $port);
*/
$this->redis = $redis_client->getRedisClient();
}
public function __destruct()
{
public function __destruct()
{
// $this->mclient->disconnect();
}
}
public function publish($channel, $message)
{

View file

@ -0,0 +1,44 @@
<?php
namespace App\Service;
use Predis\Client as PredisClient;
class RedisClientProvider
{
protected $redis;
protected $scheme;
protected $host;
protected $port;
protected $password;
protected $env_flag;
public function __construct($scheme, $host, $port, $password, $env_flag)
{
$this->scheme = $scheme;
$this->host = $host;
$this->port = $port;
$this->password = $password;
$this->env_flag = $env_flag;
}
public function getRedisClient()
{
if ($this->env_flag == 'dev')
{
$this->redis = new PredisClient([
"scheme"=>$this->scheme,
"host"=>$this->host,
"port"=>$this->port]);
}
else
{
$this->redis = new PredisClient([
"scheme"=>$this->scheme,
"host"=>$this->host,
"port"=>$this->port,
"password"=>$this->password]);
}
return $this->redis;
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace App\Service;
use App\Entity\Rider;
use Doctrine\ORM\EntityManagerInterface;
use CrEOF\Spatial\PHP\Types\Geometry\Point;
class RiderTracker
{
const RIDER_PREFIX_KEY = 'rider.location';
protected $em;
protected $redis;
public function __construct(EntityManagerInterface $em, RedisClientProvider $redis_client)
{
$this->em = $em;
$this->redis = $redis_client->getRedisClient();
}
protected function getRiderKey($rider_id)
{
return self::RIDER_PREFIX_KEY . '.' . $rider_id;
}
public function getRiderLocation($rider_id)
{
$coordinates = new Point(0,0);
$key = $this->getRiderKey($rider_id);
// check redis cache for rider information
if (($this->redis->hexists($key, 'longitude')) &&
($this->redis->hexists($key, 'latitude')))
{
$long = $this->redis->hget($key, 'longitude');
$lat = $this->redis->hget($key, 'latitude');
$coordinates = new Point($long, $lat);
}
else
{
$rider = $this->em->getRepository(Rider::class)->find($rider_id);
$coordinates = $rider->getHub()->getCoordinates();
}
return $coordinates;
}
}

View file

@ -1,4 +1,16 @@
{
"creof/doctrine2-spatial": {
"version": "1.2.0"
},
"creof/geo-parser": {
"version": "2.1.0"
},
"creof/wkb-parser": {
"version": "v2.3.0"
},
"creof/wkt-parser": {
"version": "2.2.0"
},
"data-dog/audit-bundle": {
"version": "v0.1.10"
},
@ -263,6 +275,9 @@
"symfony/stopwatch": {
"version": "v4.0.2"
},
"symfony/thanks": {
"version": "v1.1.0"
},
"symfony/twig-bridge": {
"version": "v4.0.2"
},

View file

@ -114,11 +114,11 @@
var actions = '';
if (row.meta.update_url != '') {
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.username + '" title="Edit"><i class="la la-edit"></i></a>';
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.name + '" title="Edit"><i class="la la-edit"></i></a>';
}
if (row.meta.delete_url != '') {
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.username + '" title="Delete"><i class="la la-trash"></i></a>';
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.name + '" title="Delete"><i class="la la-trash"></i></a>';
}
return actions;

View file

@ -893,7 +893,8 @@
{% endblock %}
{% block scripts %}
<script src="//maps.google.com/maps/api/js?key={{ gmaps_api_key }}" type="text/javascript"></script>
<!-- <script src="//maps.google.com/maps/api/js?key={{ gmaps_api_key }}" type="text/javascript"></script> -->
<script src="//maps.googleapis.com/maps/api/js?key={{ gmaps_api_key }}" type="text/javascript"></script>
<script src="/assets/vendors/custom/gmaps/gmaps.js" type="text/javascript"></script>
<script>

View file

@ -118,11 +118,11 @@
var actions = '';
if (row.meta.update_url != '') {
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.username + '" title="Edit"><i class="la la-edit"></i></a>';
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.name + '" title="Edit"><i class="la la-edit"></i></a>';
}
if (row.meta.delete_url != '') {
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.username + '" title="Delete"><i class="la la-trash"></i></a>';
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.name + '" title="Delete"><i class="la la-trash"></i></a>';
}
return actions;

View file

@ -116,7 +116,7 @@
}
});
}).fail(function(response) {
if (response.status == 422) {
if (response.status == 422 || response.status == 403) {
var errors = response.responseJSON.errors;
var firstfield = false;

View file

@ -93,11 +93,11 @@
var actions = '';
if (row.meta.update_url != '') {
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.username + '" title="Edit"><i class="la la-edit"></i></a>';
actions += '<a href="' + row.meta.update_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-accent m-btn--icon m-btn--icon-only m-btn--pill btn-edit" data-id="' + row.name + '" title="Edit"><i class="la la-edit"></i></a>';
}
if (row.meta.delete_url != '') {
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.username + '" title="Delete"><i class="la la-trash"></i></a>';
actions += '<a href="' + row.meta.delete_url + '" class="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill btn-delete" data-id="' + row.id + '" title="Delete"><i class="la la-trash"></i></a>';
}
return actions;
@ -139,4 +139,4 @@
});
});
</script>
{% endblock %}
{% endblock %}

View file

@ -59,7 +59,7 @@ class APNSClient():
def setupConnection(self):
# socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
wsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1, certfile=self.cert_file)
wsock = ssl.wrap_socket(sock, ssl_version=ssl.PROTOCOL_TLSv1_2, certfile=self.cert_file)
wsock.connect((self.host, self.port))
self.is_connected = True

View file

@ -0,0 +1,12 @@
[Unit]
Description=Apple Push Notification Sender Service
After=mosquitto.service redis.service
[Service]
Type=simple
ExecStart=/usr/bin/python /root/www/resq/utils/apns_sender/new_sender.py
StandardInput=tty-force
Restart=always
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,90 @@
from daemonize import Daemonize
from apns import APNs, Frame, Payload
import redis
import time
import signal
import sys
import os
import logging
import socket
import ssl
import struct
import binascii
def sigint_handler(signal, frame):
sys.exit(0)
os._exit(0)
def redisLoop(apns, logger):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
while 1:
#logger.info("Checking queue for messages to send...")
time.sleep(0)
data = r.brpop("apns_push", 10)
if data:
info = data[1].split('|')
logger.info("Token: " + info[0] + " message: " + info[1])
try:
apns.sendNotification(info[0], info[1], "notification")
except TypeError:
logger.info("Invalid token: " + info[0])
def sigint_handler(signal, frame):
sys.exit(0)
def get_logger():
logger = logging.getLogger("apns_logger")
logger.setLevel(logging.INFO)
fh = logging.FileHandler("/tmp/apns_sender.log")
fmt = '%(asctime)s - %(threadName)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(fmt)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
class APNSClient():
def __init__(self, title, cert_file, logger):
self.title = title
self.cert_file = cert_file
self.logger = logger
# connect
self.apns = APNs(cert_file=self.cert_file)
def sendNotification(self, token, body, notif_type):
# apns packet
alert = {"title" : self.title,
"body" : body,
"type" : notif_type}
payload = Payload(alert=alert, sound="default", badge=1, content_available=True)
self.apns.gateway_server.send_notification(token, payload)
def main():
logger = get_logger()
logger.info("Starting apns_sender")
#apns = APNs(use_sandbox=False, cert_file=cert_file)
#apns = setup_apns()
# TODO: load from config file
cert_file = "/root/ios_push_test/ios_prod.pem"
apns = APNSClient("Motolite Res-q", cert_file, logger)
logger.info("Starting redis loop")
redisLoop(apns, logger)
signal.signal(signal.SIGINT, sigint_handler)
pid = "/tmp/apns_sender.pid"
#daemon = Daemonize(app="apns_sender", pid=pid, action=main)
#daemon.start()
main()

View file

@ -1,4 +1,4 @@
load data local infile '/tmp/plate_numbers.csv' into table plate_number fields terminated by ',';
load data infile '/tmp/legacy_jo.csv' into table legacy_job_order fields terminated by '|' (id, trans_date, trans_type, origin, car_brand, car_make, car_model, car_color, cust_name, cust_first_name, cust_middle_name, cust_last_name, cust_contact, cust_mobile, cust_landline, delivery_instructions, agent_notes_1, delivery_date, delivery_time, advance_order, stage, cancel_reason, cancel_reason_specify, payment_method, prepared_by, dispatch_time, dispatch_date, dispatched_by, address, landmark, date_purchase, plate_number);
load data infile '/tmp/legacy_jo_row.csv' into table legacy_job_order_row fields terminated by ',' enclosed by '"' lines terminated by '\n' (job_order_id, item, qty, price, price_level, status, account, enrollee) set id = null;
load data local infile '/tmp/warranty.csv' into table warranty fields terminated by ',' (bty_model_id, bty_size_id, serial, warranty_class, plate_number, status, date_create, date_purchase, date_expire, date_claim, claim_id, sap_bty_id, first_name, last_name, mobile_number) set id = null;
load data infile '/tmp/legacy_jo.csv' into table legacy_job_order fields terminated by '|' (id, trans_date, trans_type, origin, car_brand, car_make, car_model, car_color, cust_name, cust_first_name, cust_middle_name, cust_last_name, cust_contact, cust_mobile, cust_landline, delivery_instructions, agent_notes_1, delivery_date, delivery_time, advance_order, stage, cancel_reason, cancel_reason_specify, payment_method, prepared_by, dispatch_time, dispatch_date, dispatched_by, address, landmark, date_purchase, plate_number);

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
[Unit]
Description=MQTT Sender Service
After=mosquitto.service redis.service
[Service]
Type=simple
ExecStart=/usr/bin/python /root/www/resq/utils/mqtt_sender/mqtt_sender.py
StandardInput=tty-force
Restart=always
[Install]
WantedBy=multi-user.target

View file

@ -1,42 +1,38 @@
import paho.mqtt.client as mqtt
import ssl
from threading import Thread
import redis
import time
import signal
import sys
import os
import mysql.connector
import json
def mysql_connect(user, password, host, database):
conn = mysql.connector.connect(user=user,
password=password,
host=host,
database=database)
return conn
class RiderLocationCache(object):
def init_subscriptions(client, conn):
# given mysql connection, get all rider sessions
query = ("select id from rider_session")
cursor = conn.cursor()
cursor.execute(query)
for (id) in cursor:
print "subscribing to rider session %s" % id
client.subscribe('motorider_%s' % id)
cursor.close()
def run(self, client):
print "running loop..."
client.loop_forever()
# TODO: fix this and put these guys back under the class
def init_subscriptions(client):
print "subscribing to wildcard #"
client.subscribe('#')
def on_connect(client, userdata, flags, rc):
conn = mysql_connect('resq', 'Motolite456', '127.0.0.1', 'resq')
init_subscriptions(client, conn)
print("Connected with result code "+str(rc))
client.subscribe("$SYS/#")
init_subscriptions(client)
#print("Connected with result code "+str(rc))
# client.subscribe("$SYS/#")
def user_data_set(userdata):
conn = redis.StrictRedis(host='localhost', port=6379, db=0)
return conn
def on_publish(client, userdata, mid):
pass
def on_message(client, userdata, message):
print("message topic=",message.topic[0:10])
redis_conn = user_data_set(userdata)
#print("message topic=", message.topic[0:10])
if message.topic[0:10] != 'motorider_':
return
@ -44,6 +40,7 @@ def on_message(client, userdata, message):
# check if json decodable
res = json.loads(message.payload)
#print res
# get rider session id
sess_id = message.topic[10:]
@ -56,51 +53,17 @@ def on_message(client, userdata, message):
if res['event'] != 'driver_location':
return
# save the longitude and latitude
# get the rider id from sess_id
rider_key = "rider.location.%s" % sess_id
rider_long = str(res['longitude'])
rider_lat = str(res['latitude'])
# set the location
redis_conn.hmset(rider_key, {'longitude': rider_long, 'latitude': rider_lat})
# update our redis key
key = 'location_%s' % sess_id
print "setting %s" % key
#print "setting %s" % key
redis_conn.setex(key, 1600, message.payload)
def getRedis(i):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
while 1:
time.sleep(0)
data = r.brpop("events", 10)
if data:
info = data[1].split('|')
print "Channel: " + info[0] + " message: " + info[1]
client.publish(info[0], info[1])
def sigint_handler(signal, frame):
print 'Interrupted'
sys.exit(0)
client = mqtt.Client()
client.on_connect = on_connect
# client.on_publish = on_publish
client.on_message = on_message
redis_conn = redis.StrictRedis(host='localhost', port=6379, db=0)
#client.tls_set(
# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
# tls_version=ssl.PROTOCOL_TLSv1)
client.tls_set(
"/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE,
tls_version=ssl.PROTOCOL_TLSv1)
client.connect("resqaws.jankstudio.com", 8883, 60)
#t = Thread(target=getRedis, args=(1,))
#t.start()
#signal.signal(signal.SIGINT, sigint_handler)
client.loop_forever()

View file

@ -0,0 +1,23 @@
import paho.mqtt.client as mqtt
import rider_location_cache as rlc
import ssl
import logging
client = mqtt.Client()
client.on_connect = rlc.on_connect
# client.on_publish = on_publish
client.on_message = rlc.on_message
#client.tls_set(
# "/etc/letsencrypt/live/resqaws.jankstudio.com/fullchain.pem", cert_reqs=ssl.CERT_NONE,
# tls_version=ssl.PROTOCOL_TLSv1)
client.tls_set(
"/root/aws_ssl_keys/fullchain.pem", cert_reqs=ssl.CERT_NONE,
tls_version=ssl.PROTOCOL_TLSv1)
#client.connect("resqaws.jankstudio.com", 8883, 60)
client.connect("localhost", 8883, 60)
rider_location = rlc.RiderLocationCache()
rider_location.run(client)

View file

@ -0,0 +1,12 @@
[Unit]
Description=Rider Location Cache Service
After=mosquitto.service redis.service
[Service]
Type=simple
ExecStart=/usr/bin/python /root/www/resq/utils/rider_location_cache/riderloc.py
StandardInput=tty-force
Restart=always
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,13 @@
from apns import APNs, Frame, Payload
#apns = APNs(use_sandbox=True, cert_file='/root/ios_push_test/motoliteUserDev.pem')
apns = APNs(cert_file='/root/ios_push_test/motoliteUserProd.pem')
alert = {"title" : "Motolite Res-q Notification",
"body" : "Test Notification",
"type" : "register"}
token_hex = "0D89F702B8E5A235E5100C47912DCBC346CE503DBB860572402031595D6F4C6C"
payload = Payload(alert=alert, sound="default", badge=1, content_available=True)
apns.gateway_server.send_notification(token_hex, payload)

View file

@ -0,0 +1 @@
update warranty set status='expired' where date_expire is not null and date_expire < now() - interval 1 day;