mirror of
https://github.com/fspc/workstand.git
synced 2025-02-23 01:13:22 -05:00
Use channel to dispatch an action to check CPIC.
This commit is contained in:
parent
3ef3723d01
commit
970a9a3eef
@ -3,3 +3,6 @@ from django.apps import AppConfig
|
||||
|
||||
class BikeConfig(AppConfig):
|
||||
name = 'bike'
|
||||
|
||||
def ready(self):
|
||||
import bike.signals #noqa
|
||||
|
60
bikeshop_project/bike/consumers.py
Normal file
60
bikeshop_project/bike/consumers.py
Normal file
@ -0,0 +1,60 @@
|
||||
import logging
|
||||
import re
|
||||
from typing import Dict, Union, Optional
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils import timezone
|
||||
|
||||
from bike.models import Bike
|
||||
|
||||
logger = logging.getLogger('cpic')
|
||||
|
||||
|
||||
def _is_stolen(serial: str) -> Optional[bool]:
|
||||
url = 'http://app.cpic-cipc.ca/English/searchFormResultsbikes.cfm'
|
||||
data = {'ser': message.get('serial_number'),
|
||||
'toc': 1,
|
||||
'Submit': 'Begin Search'}
|
||||
|
||||
r = requests.post(url, data=data)
|
||||
html = r.text
|
||||
soup = BeautifulSoup(html)
|
||||
|
||||
no_records = r'^No Records were found in our database on.+$'
|
||||
found_records = r'^WE HAVE A RECORD ON FILE THAT MATCHES THE IDENTIFIERS THAT YOU PROVIDED.+$'
|
||||
if soup.body.findAll(text=re.compile(no_records)):
|
||||
return False
|
||||
elif soup.body.findAll(text=re.compile(found_records)):
|
||||
return True
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def check_cpic(message: Dict[str, Union[str, int]]) -> None:
|
||||
"""
|
||||
Makes a remote call to CPIC to determine whether a bike has been stolen.
|
||||
"""
|
||||
try:
|
||||
bike = Bike.objects.get(id=message['bike_id'])
|
||||
except ObjectDoesNotExist:
|
||||
logger.error(f'check_epic: Invalid Bike id: {message["bike_id"]}')
|
||||
return
|
||||
|
||||
stolen = _is_stolen(message['serial_number'])
|
||||
|
||||
if stolen:
|
||||
bike.cpic_searched_at = timezone.now()
|
||||
bike.stolen = True
|
||||
elif stolen is None:
|
||||
logger.error(f'check_epic: Unable to check CPIC records with serial number: {message["serial_number"]}.')
|
||||
return
|
||||
else:
|
||||
bike.cpic_searched_at = timezone.now()
|
||||
bike.stolen = False
|
||||
|
||||
bike.save()
|
||||
|
||||
|
||||
|
7
bikeshop_project/bike/routing.py
Normal file
7
bikeshop_project/bike/routing.py
Normal file
@ -0,0 +1,7 @@
|
||||
from channels.routing import route
|
||||
|
||||
from .consumers import check_cpic
|
||||
|
||||
channel_routing = [
|
||||
route('check-cpic', check_cpic),
|
||||
]
|
16
bikeshop_project/bike/signals.py
Normal file
16
bikeshop_project/bike/signals.py
Normal file
@ -0,0 +1,16 @@
|
||||
from channels import Channel
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from .models import Bike
|
||||
|
||||
|
||||
@receiver(post_save, sender=Bike)
|
||||
def bike_save_handler(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
message = {
|
||||
'bike_id': instance.id,
|
||||
'serial_number': instance.serial_number,
|
||||
}
|
||||
|
||||
Channel('check-cpic').send(message)
|
@ -6,8 +6,10 @@ from rest_framework.test import APIClient
|
||||
from model_mommy import mommy
|
||||
from rest_framework import status
|
||||
|
||||
from bike.consumers import check_cpic
|
||||
from registration.models import Member
|
||||
from .models import Bike, BikeState
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestGet(TestCase):
|
||||
@ -344,3 +346,29 @@ class TestGet(TestCase):
|
||||
result = client.put(f'/api/v1/bikes/{bike.id}/stolen/')
|
||||
|
||||
self.assertEqual(result.status_code, status.HTTP_200_OK)
|
||||
|
||||
|
||||
class TestBikeSignals(TestCase):
|
||||
@patch('bike.consumers._is_stolen')
|
||||
def test_check_cpic_stolen_bike(self, is_stolen_mock):
|
||||
bike = mommy.make(Bike)
|
||||
message = {'bike_id': bike.id, 'serial_number': bike.serial_number}
|
||||
is_stolen_mock.return_value = True
|
||||
check_cpic(message)
|
||||
|
||||
updated_bike = Bike.objects.get(id=bike.id)
|
||||
|
||||
self.assertTrue(updated_bike.stolen)
|
||||
self.assertIsNotNone(updated_bike.cpic_searched_at)
|
||||
|
||||
@patch('bike.consumers._is_stolen')
|
||||
def test_check_cpic_not_stolen_bike(self, is_stolen_mock):
|
||||
bike = mommy.make(Bike)
|
||||
message = {'bike_id': bike.id, 'serial_number': bike.serial_number}
|
||||
is_stolen_mock.return_value = False
|
||||
check_cpic(message)
|
||||
|
||||
updated_bike = Bike.objects.get(id=bike.id)
|
||||
|
||||
self.assertFalse(updated_bike.stolen)
|
||||
self.assertIsNotNone(updated_bike.cpic_searched_at)
|
||||
|
@ -33,6 +33,7 @@ INSTALLED_APPS = [
|
||||
'webpack_loader',
|
||||
'compressor',
|
||||
'rest_framework',
|
||||
'channels',
|
||||
|
||||
'registration',
|
||||
'core',
|
||||
@ -176,3 +177,13 @@ REST_FRAMEWORK = {
|
||||
}
|
||||
|
||||
DATE_INPUT_FORMATS = ['iso-8601']
|
||||
|
||||
CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "asgi_redis.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [os.environ.get('REDIS_URL', 'redis://redis:6379')],
|
||||
},
|
||||
"ROUTING": "bike.routing.channel_routing",
|
||||
},
|
||||
}
|
||||
|
@ -12,4 +12,7 @@ djangorestframework
|
||||
django-webpack-loader
|
||||
requests
|
||||
PyYAML
|
||||
djangorestframework-jwt==1.9.0
|
||||
djangorestframework-jwt==1.9.0
|
||||
channels
|
||||
asgi-redis
|
||||
beautifulsoup4
|
||||
|
Loading…
x
Reference in New Issue
Block a user