241 lines
8.9 KiB
Python
241 lines
8.9 KiB
Python
"""
|
|
Location checkin view with evaluation_value based interaction logic
|
|
"""
|
|
|
|
from django.http import JsonResponse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.utils.decorators import method_decorator
|
|
from django.views import View
|
|
import json
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@method_decorator(csrf_exempt, name='dispatch')
|
|
@method_decorator(login_required, name='dispatch')
|
|
class LocationCheckinView(View):
|
|
"""
|
|
evaluation_valueに基づく拡張チェックイン処理
|
|
"""
|
|
|
|
def post(self, request):
|
|
"""
|
|
ロケーションチェックイン処理
|
|
|
|
Request body:
|
|
{
|
|
"location_id": int,
|
|
"latitude": float,
|
|
"longitude": float,
|
|
"photo": str (base64) - evaluation_value=1の場合必須,
|
|
"qr_code_data": str - evaluation_value=2の場合必須,
|
|
"quiz_answer": str - evaluation_value=2の場合必須
|
|
}
|
|
"""
|
|
try:
|
|
data = json.loads(request.body)
|
|
location_id = data.get('location_id')
|
|
user_lat = data.get('latitude')
|
|
user_lon = data.get('longitude')
|
|
|
|
if not all([location_id, user_lat, user_lon]):
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'location_id, latitude, longitude are required'
|
|
}, status=400)
|
|
|
|
# ロケーション取得
|
|
from .models import Location
|
|
try:
|
|
location = Location.objects.get(id=location_id)
|
|
except Location.DoesNotExist:
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Location not found'
|
|
}, status=404)
|
|
|
|
# 距離チェック
|
|
if not self._is_within_checkin_radius(location, user_lat, user_lon):
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Too far from location',
|
|
'required_radius': location.checkin_radius or 15.0
|
|
}, status=400)
|
|
|
|
# evaluation_valueに基づく要件検証
|
|
from .location_interaction import validate_interaction_requirements
|
|
validation_result = validate_interaction_requirements(location, data)
|
|
|
|
if not validation_result['valid']:
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Interaction requirements not met',
|
|
'errors': validation_result['errors']
|
|
}, status=400)
|
|
|
|
# インタラクション処理
|
|
interaction_result = self._process_interaction(location, data)
|
|
|
|
# ポイント計算
|
|
from .location_interaction import get_point_calculation
|
|
point_info = get_point_calculation(location, interaction_result)
|
|
|
|
# チェックイン記録保存
|
|
checkin_record = self._save_checkin_record(
|
|
request.user, location, user_lat, user_lon,
|
|
interaction_result, point_info
|
|
)
|
|
|
|
# レスポンス
|
|
response_data = {
|
|
'success': True,
|
|
'checkin_id': checkin_record.id,
|
|
'points_awarded': point_info['points_awarded'],
|
|
'point_type': point_info['point_type'],
|
|
'message': point_info['message'],
|
|
'location_name': location.location_name,
|
|
'interaction_type': location.evaluation_value or "0",
|
|
}
|
|
|
|
# インタラクション結果の詳細を追加
|
|
if interaction_result:
|
|
response_data['interaction_result'] = interaction_result
|
|
|
|
return JsonResponse(response_data)
|
|
|
|
except json.JSONDecodeError:
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Invalid JSON data'
|
|
}, status=400)
|
|
except Exception as e:
|
|
logger.error(f"Checkin error: {e}")
|
|
return JsonResponse({
|
|
'success': False,
|
|
'error': 'Internal server error'
|
|
}, status=500)
|
|
|
|
def _is_within_checkin_radius(self, location, user_lat, user_lon):
|
|
"""チェックイン範囲内かどうかを判定"""
|
|
from math import radians, cos, sin, asin, sqrt
|
|
|
|
# ロケーションの座標を取得
|
|
if location.geom and location.geom.coords:
|
|
loc_lon, loc_lat = location.geom.coords[0][:2]
|
|
else:
|
|
loc_lat = location.latitude
|
|
loc_lon = location.longitude
|
|
|
|
if not all([loc_lat, loc_lon]):
|
|
return False
|
|
|
|
# Haversine公式で距離計算
|
|
def haversine(lon1, lat1, lon2, lat2):
|
|
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
|
|
dlon = lon2 - lon1
|
|
dlat = lat2 - lat1
|
|
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
|
|
c = 2 * asin(sqrt(a))
|
|
r = 6371000 # 地球の半径(メートル)
|
|
return c * r
|
|
|
|
distance = haversine(loc_lon, loc_lat, user_lon, user_lat)
|
|
allowed_radius = location.checkin_radius or 15.0
|
|
|
|
return distance <= allowed_radius
|
|
|
|
def _process_interaction(self, location, data):
|
|
"""evaluation_valueに基づくインタラクション処理"""
|
|
evaluation_value = location.evaluation_value or "0"
|
|
result = {}
|
|
|
|
if evaluation_value == "1":
|
|
# 写真撮影処理
|
|
photo_data = data.get('photo')
|
|
if photo_data:
|
|
result['photo_saved'] = True
|
|
result['photo_filename'] = self._save_photo(photo_data, location)
|
|
|
|
elif evaluation_value == "2":
|
|
# QRコード + クイズ処理
|
|
qr_data = data.get('qr_code_data')
|
|
quiz_answer = data.get('quiz_answer')
|
|
|
|
if qr_data and quiz_answer:
|
|
result['qr_scanned'] = True
|
|
result['quiz_answer'] = quiz_answer
|
|
result['quiz_correct'] = self._check_quiz_answer(qr_data, quiz_answer)
|
|
|
|
return result
|
|
|
|
def _save_photo(self, photo_data, location):
|
|
"""写真データを保存(実装は要調整)"""
|
|
import base64
|
|
import os
|
|
from django.conf import settings
|
|
from datetime import datetime
|
|
|
|
try:
|
|
# Base64デコード
|
|
photo_binary = base64.b64decode(photo_data)
|
|
|
|
# ファイル名生成
|
|
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
|
filename = f"checkin_{location.id}_{timestamp}.jpg"
|
|
|
|
# 保存先ディレクトリ
|
|
photo_dir = os.path.join(settings.MEDIA_ROOT, 'checkin_photos')
|
|
os.makedirs(photo_dir, exist_ok=True)
|
|
|
|
# ファイル保存
|
|
file_path = os.path.join(photo_dir, filename)
|
|
with open(file_path, 'wb') as f:
|
|
f.write(photo_binary)
|
|
|
|
return filename
|
|
|
|
except Exception as e:
|
|
logger.error(f"Photo save error: {e}")
|
|
return None
|
|
|
|
def _check_quiz_answer(self, qr_data, quiz_answer):
|
|
"""クイズ回答の正答チェック(実装は要調整)"""
|
|
# QRコードデータから正答を取得
|
|
# 実際の実装では、QRコードに含まれるクイズIDから正答を取得
|
|
try:
|
|
import json
|
|
qr_info = json.loads(qr_data)
|
|
correct_answer = qr_info.get('correct_answer', '').lower()
|
|
user_answer = quiz_answer.lower().strip()
|
|
|
|
return correct_answer == user_answer
|
|
|
|
except (json.JSONDecodeError, KeyError):
|
|
# QRデータの形式が不正な場合はデフォルトで不正解
|
|
return False
|
|
|
|
def _save_checkin_record(self, user, location, lat, lon, interaction_result, point_info):
|
|
"""チェックイン記録を保存"""
|
|
from .models import Useractions
|
|
from datetime import datetime
|
|
|
|
# Useractionsレコード作成/更新
|
|
checkin_record, created = Useractions.objects.get_or_create(
|
|
user=user,
|
|
location=location,
|
|
defaults={
|
|
'checkin': True,
|
|
'created_at': datetime.now(),
|
|
'last_updated_at': datetime.now()
|
|
}
|
|
)
|
|
|
|
if not created:
|
|
checkin_record.checkin = True
|
|
checkin_record.last_updated_at = datetime.now()
|
|
checkin_record.save()
|
|
|
|
return checkin_record
|