mirror of
				https://github.com/fspc/workstand.git
				synced 2025-10-31 00:15:35 -04: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