mirror of
https://github.com/fspc/workstand.git
synced 2025-02-23 09:13:23 -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):
|
class BikeConfig(AppConfig):
|
||||||
name = 'bike'
|
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 model_mommy import mommy
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
|
from bike.consumers import check_cpic
|
||||||
from registration.models import Member
|
from registration.models import Member
|
||||||
from .models import Bike, BikeState
|
from .models import Bike, BikeState
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
class TestGet(TestCase):
|
class TestGet(TestCase):
|
||||||
@ -344,3 +346,29 @@ class TestGet(TestCase):
|
|||||||
result = client.put(f'/api/v1/bikes/{bike.id}/stolen/')
|
result = client.put(f'/api/v1/bikes/{bike.id}/stolen/')
|
||||||
|
|
||||||
self.assertEqual(result.status_code, status.HTTP_200_OK)
|
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',
|
'webpack_loader',
|
||||||
'compressor',
|
'compressor',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'channels',
|
||||||
|
|
||||||
'registration',
|
'registration',
|
||||||
'core',
|
'core',
|
||||||
@ -176,3 +177,13 @@ REST_FRAMEWORK = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DATE_INPUT_FORMATS = ['iso-8601']
|
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
|
django-webpack-loader
|
||||||
requests
|
requests
|
||||||
PyYAML
|
PyYAML
|
||||||
djangorestframework-jwt==1.9.0
|
djangorestframework-jwt==1.9.0
|
||||||
|
channels
|
||||||
|
asgi-redis
|
||||||
|
beautifulsoup4
|
||||||
|
Loading…
x
Reference in New Issue
Block a user