Files
rogaining_srv/rog/models.py

2224 lines
101 KiB
Python
Raw Normal View History

2024-08-02 07:01:32 +00:00
from django.contrib.auth.hashers import make_password
2022-03-14 10:50:11 +05:30
from dataclasses import field
2022-08-30 18:20:15 +05:30
import email
2022-10-06 17:42:12 +05:30
from enum import unique
2022-03-14 10:50:11 +05:30
from pyexpat import model
from sre_constants import CH_LOCALE
from typing import ChainMap
from django.contrib.gis.db import models
2025-08-27 15:01:06 +09:00
from django.contrib.postgres.fields import ArrayField
2025-08-29 09:11:20 +09:00
from django.utils import timezone
from datetime import timedelta
2025-09-06 00:11:57 +09:00
from django.conf import settings
2025-08-27 15:01:06 +09:00
try:
from django.db.models import JSONField
except ImportError:
from django.contrib.postgres.fields import JSONField
2022-03-14 10:50:11 +05:30
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
from django.db.models.signals import post_save, post_delete, pre_save
from django.dispatch import receiver
import geopandas as gpd
from sqlalchemy import *
from geoalchemy2 import Geometry, WKTElement
import os, zipfile, glob
import environ
from geo.Postgres import Db
from sqlalchemy.sql.functions import mode
from .mapping import location_mapping, location_line_mapping, location_polygon_mapping
from .choices import LAYER_CHOICES
from django.contrib.gis.utils import LayerMapping
from django.apps import apps
2022-04-27 15:47:37 +05:30
from django.db import transaction
2022-05-12 02:15:36 +05:30
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
2023-05-26 13:56:26 +05:30
from django.contrib.postgres.indexes import GistIndex
2024-07-22 06:53:48 +00:00
from django.utils import timezone
from datetime import timedelta,date
2024-07-31 00:56:23 +00:00
from django.contrib.gis.geos import Point,MultiPoint
2024-07-22 06:53:48 +00:00
#from django.db import models
from django.core.exceptions import ValidationError
2022-03-14 10:50:11 +05:30
2022-03-19 18:42:55 +05:30
import csv
import codecs
import sys
2022-03-23 09:44:05 +05:30
import time
2022-03-23 12:13:30 +05:30
import uuid
import logging
logger = logging.getLogger(__name__)
2022-03-19 18:42:55 +05:30
2022-03-14 10:50:11 +05:30
env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(env_file=".env")
2024-07-22 06:53:48 +00:00
db = Db(dbname=env("POSTGRES_DBNAME"), user=env("POSTGRES_USER"), password=env("POSTGRES_PASS"), host=env("PG_HOST"), port=env("PG_PORT"))
2022-03-14 10:50:11 +05:30
2022-08-30 18:20:15 +05:30
def get_file_path(instance, filename):
ext = filename.split('.')[-1]
filename = "%s/%s.%s" % (uuid.uuid4(), uuid.uuid4(), ext)
return os.path.join('uploads/geoms', filename)
def remove_bom_inplace(path):
"""Removes BOM mark, if it exists, from a file and rewrites it in-place"""
buffer_size = 4096
bom_length = len(codecs.BOM_UTF8)
with open(path, mode="r+b") as fp:
chunk = fp.read(buffer_size)
if chunk.startswith(codecs.BOM_UTF8):
i = 0
chunk = chunk[bom_length:]
while chunk:
fp.seek(i)
fp.write(chunk)
i += len(chunk)
fp.seek(bom_length, os.SEEK_CUR)
chunk = fp.read(buffer_size)
fp.seek(-bom_length, os.SEEK_CUR)
fp.truncate()
2024-10-27 18:22:01 +00:00
class GifurogeRegister(models.Model):
event_code = models.CharField(max_length=100)
time = models.IntegerField(choices=[(3, '3時間'), (5, '5時間')])
owner_name_kana = models.CharField(max_length=100)
owner_name = models.CharField(max_length=100)
email = models.EmailField()
password = models.CharField(max_length=100)
owner_birthday = models.DateField(blank=True,null=True)
owner_sex = models.CharField(max_length=10,blank=True,null=True)
team_name = models.CharField(max_length=100)
department = models.CharField(max_length=100)
members_count = models.IntegerField()
member2 = models.CharField(max_length=100, blank=True, null=True)
birthday2 = models.DateField(blank=True,null=True)
sex2 = models.CharField(max_length=10,blank=True,null=True)
member3 = models.CharField(max_length=100, blank=True, null=True)
birthday3 = models.DateField(blank=True,null=True)
sex3 = models.CharField(max_length=10,blank=True,null=True)
member4 = models.CharField(max_length=100, blank=True, null=True)
birthday4 = models.DateField(blank=True,null=True)
sex4 = models.CharField(max_length=10,blank=True,null=True)
member5 = models.CharField(max_length=100, blank=True, null=True)
birthday5 = models.DateField(blank=True,null=True)
sex5 = models.CharField(max_length=10,blank=True,null=True)
2022-08-30 18:20:15 +05:30
2022-05-12 02:15:36 +05:30
class CustomUserManager(BaseUserManager):
2024-07-24 00:38:32 +00:00
def create_user(self, email, password=None, **other_fields):
2022-05-12 02:15:36 +05:30
if not email:
raise ValueError(_("You must provide an email address"))
2024-07-24 00:38:32 +00:00
email = self.normalize_email(email)
user = self.model(email=email, **other_fields)
2022-05-12 02:15:36 +05:30
user.set_password(password)
2024-07-24 00:38:32 +00:00
user.save(using=self._db)
2022-05-12 02:15:36 +05:30
return user
2024-01-26 11:43:29 +05:30
def create_superuser(self, email, password, group, event_code=None, team_name=None, **other_fields):
# Providing default values for event_code and team_name if they are not provided
if event_code is None:
event_code = 'test' # replace this with some default value
if team_name is None:
team_name = 'test' # replace this with some default value
2022-05-12 02:15:36 +05:30
other_fields.setdefault('is_staff', True)
other_fields.setdefault('is_superuser', True)
other_fields.setdefault('is_active', True)
if other_fields.get('is_staff') is not True:
2024-01-26 11:43:29 +05:30
raise ValueError(_('Superuser must be assigned to staff'))
2022-05-12 02:15:36 +05:30
if other_fields.get('is_superuser') is not True:
2024-01-26 11:43:29 +05:30
raise ValueError(_('Superuser must have is_superuser set to True'))
2022-05-12 02:15:36 +05:30
2024-07-24 00:38:32 +00:00
return self.create_user(email, password, **other_fields)
2022-05-12 02:15:36 +05:30
2022-03-16 17:46:22 +05:30
class JpnAdminMainPerf(models.Model):
geom = models.MultiPolygonField(blank=True, null=True)
adm0_en = models.CharField(max_length=254, blank=True, null=True)
adm0_ja = models.CharField(max_length=254, blank=True, null=True)
adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
adm1_en = models.CharField(max_length=254, blank=True, null=True)
adm1_ja = models.CharField(max_length=254, blank=True, null=True)
adm1_pcode = models.CharField(max_length=254, blank=True, null=True)
class Meta:
managed = False
db_table = 'jpn_admin_main_perf'
2023-05-26 13:56:26 +05:30
indexes = [
models.Index(fields=['geom'], name='jpn_admin_main_perf_geom_idx'),
# Add other fields for indexing as per the requirements
]
# class JpnAdminPerf(models.Model):
# geom = models.MultiLineStringField(blank=True, null=True)
# et_id = models.IntegerField(blank=True, null=True)
# et_right = models.CharField(max_length=80, blank=True, null=True)
# et_left = models.CharField(max_length=80, blank=True, null=True)
# adm2_l = models.CharField(max_length=50, blank=True, null=True)
# adm1_l = models.CharField(max_length=50, blank=True, null=True)
# adm0_l = models.CharField(max_length=50, blank=True, null=True)
# adm0_r = models.CharField(max_length=50, blank=True, null=True)
# adm1_r = models.CharField(max_length=50, blank=True, null=True)
# adm2_r = models.CharField(max_length=50, blank=True, null=True)
# admlevel = models.IntegerField(blank=True, null=True)
# class Meta:
# managed = False
# db_table = 'jpn_admin_perf'
# indexes = [
# models.Index(fields=['geom'], name='jpn_admin_perf_geom_idx'),
# # Add other fields for indexing as per the requirements
# ]
# ###
# ### Cities
# ###
2024-08-02 07:01:32 +00:00
class JpnSubPerf(models.Model):
geom = models.MultiPolygonField(blank=True, null=True)
adm0_en = models.CharField(max_length=254, blank=True, null=True)
adm0_ja = models.CharField(max_length=254, blank=True, null=True)
adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
adm1_en = models.CharField(max_length=254, blank=True, null=True)
adm1_ja = models.CharField(max_length=254, blank=True, null=True)
adm1_pcode = models.CharField(max_length=254, blank=True, null=True)
adm2_ja = models.CharField(max_length=254, blank=True, null=True)
adm2_en = models.CharField(max_length=254, blank=True, null=True)
adm2_pcode = models.CharField(max_length=254, blank=True, null=True)
name_modified = models.CharField(max_length=254, blank=True, null=True)
area_name = models.CharField(max_length=254, blank=True, null=True)
list_order =models.IntegerField(default=0)
class Meta:
managed = False
db_table = 'jpn_sub_perf'
indexes = [
models.Index(fields=['geom'], name='jpn_sub_perf_geom_idx'),
# Add other fields for indexing as per the requirements
]
2022-04-05 16:36:21 +05:30
2022-07-24 14:17:45 +05:30
###
### Gifu Areas
###
2022-06-05 20:54:16 +05:30
class GifuAreas(models.Model):
geom = models.MultiPolygonField(blank=True, null=True)
adm0_en = models.CharField(max_length=254, blank=True, null=True)
adm0_ja = models.CharField(max_length=254, blank=True, null=True)
adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
adm1_en = models.CharField(max_length=254, blank=True, null=True)
adm1_ja = models.CharField(max_length=254, blank=True, null=True)
adm1_pcode = models.CharField(max_length=254, blank=True, null=True)
adm2_ja = models.CharField(max_length=254, blank=True, null=True)
adm2_en = models.CharField(max_length=254, blank=True, null=True)
adm2_pcode = models.CharField(max_length=254, blank=True, null=True)
area_nm = models.CharField(max_length=254, blank=True, null=True)
class Meta:
managed = False
db_table = 'gifu_areas'
2023-05-26 13:56:26 +05:30
indexes = [
models.Index(fields=['geom'], name='gifu_areas_geom_idx'),
# Add other fields for indexing as per the requirements
]
2022-06-05 20:54:16 +05:30
2022-08-30 18:20:15 +05:30
class UserUpload(models.Model):
name = models.CharField(_("User uploads"), max_length=255)
file = models.FileField(upload_to=get_file_path, blank=True)
uploaded_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
class UserUploadUser(models.Model):
userfile=models.CharField(_('User file'), max_length=2048 ,blank=True, null=True)
email=models.CharField(_('User Email'), max_length=255)
2022-06-05 20:54:16 +05:30
2022-05-12 02:15:36 +05:30
class CustomUser(AbstractBaseUser, PermissionsMixin):
2022-08-29 19:55:48 +05:30
class Groups(models.TextChoices):
2022-08-30 18:20:15 +05:30
GB1 = '大垣-初心者', '大垣-初心者'
GB2 = '大垣-3時間', '大垣-3時間'
GB3 = '大垣-5時間', '大垣-5時間'
2022-08-29 19:55:48 +05:30
2024-07-26 12:34:54 +00:00
email = models.EmailField(unique=True)
firstname = models.CharField(max_length=255,blank=True, null=True)
lastname = models.CharField(max_length=255, blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
female = models.BooleanField(default=False)
group = models.CharField(max_length=255,blank=True)
2022-05-12 02:15:36 +05:30
is_active = models.BooleanField(default=True)
2024-07-26 12:34:54 +00:00
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
2022-10-13 16:13:04 +05:30
is_rogaining = models.BooleanField(default=False)
2022-10-16 17:06:28 +05:30
zekken_number = models.CharField(_("Zekken Number"), max_length=255, blank=True, null=True)
event_code = models.CharField(_("Event Code"), max_length=255, blank=True, null=True)
team_name = models.CharField(_("Team Name"), max_length=255, blank=True, null=True)
2024-07-22 06:53:48 +00:00
2022-05-12 02:15:36 +05:30
USERNAME_FIELD = 'email'
2024-07-26 12:34:54 +00:00
REQUIRED_FIELDS = []
2022-05-12 02:15:36 +05:30
objects = CustomUserManager()
2024-08-02 07:01:32 +00:00
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
2022-05-12 02:15:36 +05:30
def __str__(self):
return self.email
2024-07-22 06:53:48 +00:00
class TempUser(models.Model):
email = models.EmailField(unique=True)
password = models.CharField(max_length=128)
is_rogaining = models.BooleanField(default=False)
zekken_number = models.CharField(max_length=255, blank=True, null=True)
event_code = models.CharField(max_length=255, blank=True, null=True)
team_name = models.CharField(max_length=255, blank=True, null=True)
group = models.CharField(max_length=255)
firstname = models.CharField(max_length=255,blank=True, null=True)
lastname = models.CharField(max_length=255, blank=True, null=True)
date_of_birth = models.DateField(blank=True, null=True)
female = models.BooleanField(default=False)
verification_code = models.UUIDField(default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
expires_at = models.DateTimeField()
2024-08-02 07:01:32 +00:00
def set_password(self, raw_password):
self.password = make_password(raw_password)
def check_password(self, raw_password):
return check_password(raw_password, self.password)
# TempUserの作成時にこのメソッドを使用
@classmethod
def create_temp_user(cls, email, password, **kwargs):
temp_user = cls(email=email, **kwargs)
temp_user.set_password(password)
temp_user.save()
return temp_user
2024-07-22 06:53:48 +00:00
def __str__(self):
return self.email
def save(self, *args, **kwargs):
if not self.expires_at:
self.expires_at = timezone.now() + timedelta(hours=24) # 24時間の有効期限
super().save(*args, **kwargs)
def is_valid(self):
return timezone.now() <= self.expires_at
2025-08-27 15:01:06 +09:00
class AppVersion(models.Model):
"""アプリバージョン管理モデル"""
PLATFORM_CHOICES = [
('android', 'Android'),
('ios', 'iOS'),
]
version = models.CharField(max_length=20, help_text="セマンティックバージョン (1.2.3)")
platform = models.CharField(max_length=10, choices=PLATFORM_CHOICES)
build_number = models.CharField(max_length=20, blank=True, null=True)
is_latest = models.BooleanField(default=False, help_text="最新版フラグ")
is_required = models.BooleanField(default=False, help_text="強制更新フラグ")
update_message = models.TextField(blank=True, null=True, help_text="ユーザー向け更新メッセージ")
download_url = models.URLField(blank=True, null=True, help_text="アプリストアURL")
release_date = models.DateTimeField(default=timezone.now)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'app_versions'
unique_together = ['version', 'platform']
indexes = [
models.Index(fields=['platform'], name='idx_app_versions_platform'),
models.Index(
fields=['is_latest'],
condition=models.Q(is_latest=True),
name='idx_app_versions_latest_true'
),
]
def __str__(self):
return f"{self.platform} {self.version}"
def save(self, *args, **kwargs):
"""最新版フラグが設定された場合、同一プラットフォームの他のバージョンを非最新にする"""
if self.is_latest:
AppVersion.objects.filter(
platform=self.platform,
is_latest=True
).exclude(pk=self.pk).update(is_latest=False)
super().save(*args, **kwargs)
@classmethod
def compare_versions(cls, version1, version2):
"""セマンティックバージョンの比較"""
def version_tuple(v):
return tuple(map(int, v.split('.')))
v1 = version_tuple(version1)
v2 = version_tuple(version2)
if v1 < v2:
return -1
elif v1 > v2:
return 1
else:
return 0
@classmethod
def get_latest_version(cls, platform):
"""指定プラットフォームの最新バージョンを取得"""
try:
return cls.objects.filter(platform=platform, is_latest=True).first()
except cls.DoesNotExist:
return None
class CheckinExtended(models.Model):
"""チェックイン拡張情報モデル"""
VALIDATION_STATUS_CHOICES = [
('pending', 'Pending'),
('approved', 'Approved'),
('rejected', 'Rejected'),
('requires_review', 'Requires Review'),
]
gpslog = models.ForeignKey('GpsCheckin', on_delete=models.CASCADE, related_name='extended_info')
# GPS拡張情報
gps_latitude = models.DecimalField(max_digits=10, decimal_places=8, null=True, blank=True)
gps_longitude = models.DecimalField(max_digits=11, decimal_places=8, null=True, blank=True)
gps_accuracy = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True, help_text="GPS精度メートル")
gps_timestamp = models.DateTimeField(null=True, blank=True)
# カメラメタデータ
camera_capture_time = models.DateTimeField(null=True, blank=True)
device_info = models.TextField(blank=True, null=True)
# 審査・検証情報
validation_status = models.CharField(
max_length=20,
choices=VALIDATION_STATUS_CHOICES,
default='pending'
)
validation_comment = models.TextField(blank=True, null=True)
validated_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True)
validated_at = models.DateTimeField(null=True, blank=True)
# スコア情報
bonus_points = models.IntegerField(default=0)
scoring_breakdown = JSONField(default=dict, blank=True)
# システム情報
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'rog_checkin_extended'
indexes = [
models.Index(fields=['validation_status'], name='idx_checkin_ext_valid'),
models.Index(fields=['created_at'], name='idx_checkin_ext_created'),
]
def __str__(self):
return f"CheckinExtended {self.gpslog_id} - {self.validation_status}"
class UploadedImage(models.Model):
"""画像アップロード管理モデル - マルチアップロード対応"""
UPLOAD_SOURCE_CHOICES = [
('direct', 'Direct'),
('sharing_intent', 'Sharing Intent'),
('bulk_upload', 'Bulk Upload'),
]
PLATFORM_CHOICES = [
('ios', 'iOS'),
('android', 'Android'),
('web', 'Web'),
]
PROCESSING_STATUS_CHOICES = [
('uploaded', 'Uploaded'),
('processing', 'Processing'),
('processed', 'Processed'),
('failed', 'Failed'),
]
MIME_TYPE_CHOICES = [
('image/jpeg', 'JPEG'),
('image/png', 'PNG'),
('image/heic', 'HEIC'),
('image/webp', 'WebP'),
]
# 基本情報
original_filename = models.CharField(max_length=255)
server_filename = models.CharField(max_length=255, unique=True)
file_url = models.URLField()
file_size = models.BigIntegerField()
mime_type = models.CharField(max_length=50, choices=MIME_TYPE_CHOICES)
# 関連情報
event_code = models.CharField(max_length=50, blank=True, null=True)
team_name = models.CharField(max_length=255, blank=True, null=True)
cp_number = models.IntegerField(blank=True, null=True)
# アップロード情報
upload_source = models.CharField(max_length=50, choices=UPLOAD_SOURCE_CHOICES, default='direct')
device_platform = models.CharField(max_length=20, choices=PLATFORM_CHOICES, blank=True, null=True)
# メタデータ
capture_timestamp = models.DateTimeField(blank=True, null=True)
upload_timestamp = models.DateTimeField(auto_now_add=True)
device_info = models.TextField(blank=True, null=True)
# 処理状況
processing_status = models.CharField(max_length=20, choices=PROCESSING_STATUS_CHOICES, default='uploaded')
thumbnail_url = models.URLField(blank=True, null=True)
# 外部キー
gpslog = models.ForeignKey('GpsCheckin', on_delete=models.SET_NULL, null=True, blank=True)
entry = models.ForeignKey('Entry', on_delete=models.SET_NULL, null=True, blank=True)
# システム情報
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'rog_uploaded_images'
indexes = [
models.Index(fields=['event_code', 'team_name'], name='idx_uploaded_event_team'),
models.Index(fields=['cp_number'], name='idx_uploaded_cp_number'),
models.Index(fields=['upload_timestamp'], name='idx_uploaded_timestamp'),
models.Index(fields=['processing_status'], name='idx_uploaded_status'),
]
def __str__(self):
return f"{self.original_filename} - {self.event_code} - CP{self.cp_number}"
def clean(self):
"""バリデーション"""
if self.file_size and (self.file_size <= 0 or self.file_size > 10485760): # 10MB
raise ValidationError("ファイルサイズは10MB以下である必要があります")
@property
def file_size_mb(self):
"""ファイルサイズをMB単位で取得"""
return round(self.file_size / 1024 / 1024, 2) if self.file_size else 0
2024-07-25 00:57:48 +00:00
class NewEvent2(models.Model):
# 既存フィールド
2024-07-25 00:57:48 +00:00
event_name = models.CharField(max_length=255, unique=True)
2024-11-09 19:28:11 +09:00
event_description=models.TextField(max_length=255,blank=True, null=True)
2024-07-25 00:57:48 +00:00
start_datetime = models.DateTimeField(default=timezone.now)
end_datetime = models.DateTimeField(null=True, blank=True)
2024-08-26 09:02:01 +00:00
deadlineDateTime = models.DateTimeField(null=True, blank=True)
#// Added @2024-10-21
public = models.BooleanField(default=False)
2025-08-27 15:01:06 +09:00
# Status field for enhanced event management (2025-08-27)
STATUS_CHOICES = [
('public', 'Public'),
('private', 'Private'),
('draft', 'Draft'),
('closed', 'Closed'),
]
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='draft',
help_text="イベントステータス"
)
hour_3 = models.BooleanField(default=False)
hour_5 = models.BooleanField(default=True)
class_general = models.BooleanField(default=True)
class_family = models.BooleanField(default=True)
class_solo_male = models.BooleanField(default=True)
class_solo_female = models.BooleanField(default=True)
2024-07-25 00:57:48 +00:00
self_rogaining = models.BooleanField(default=False)
# MobServer統合フィールド
event_code = models.CharField(max_length=50, unique=True, blank=True, null=True) # event_table.event_code
start_time = models.CharField(max_length=20, blank=True, null=True) # event_table.start_time
event_day = models.CharField(max_length=20, blank=True, null=True) # event_table.event_day
# 会場情報統合
venue_location = models.PointField(null=True, blank=True, srid=4326)
venue_address = models.CharField(max_length=500, blank=True, null=True)
2024-07-25 00:57:48 +00:00
def __str__(self):
if self.event_code:
return f"{self.event_code} - {self.event_name}"
2024-07-25 00:57:48 +00:00
return f"{self.event_name} - From:{self.start_datetime} To:{self.end_datetime}"
2024-08-26 09:02:01 +00:00
def save(self, *args, **kwargs):
if not self.deadlineDateTime:
self.deadlineDateTime = self.end_datetime #- timedelta(days=7)
2025-08-27 15:01:06 +09:00
# publicフィールドからstatusフィールドへの自動移行
if self.pk is None and self.status == 'draft': # 新規作成時
if self.public:
self.status = 'public'
2024-08-26 09:02:01 +00:00
super().save(*args, **kwargs)
2025-08-27 15:01:06 +09:00
@property
def deadline_datetime(self):
"""API応答用のフィールド名統一"""
return self.deadlineDateTime
def is_accessible_by_user(self, user):
"""ユーザーがこのイベントにアクセス可能かチェック"""
if self.status == 'public':
return True
elif self.status == 'private':
# スタッフ権限チェック(後で実装)
return hasattr(user, 'staff_privileges') and user.staff_privileges
elif self.status == 'draft':
# ドラフトは管理者のみ
return user.is_staff or user.is_superuser
elif self.status == 'closed':
return False
return False
2024-07-25 00:57:48 +00:00
2025-08-29 09:11:20 +09:00
def get_default_end_datetime():
"""デフォルトの終了日時を取得"""
from datetime import timedelta
return timezone.now() + timedelta(days=1)
2024-07-24 00:38:32 +00:00
class NewEvent(models.Model):
2024-07-22 06:53:48 +00:00
event_name = models.CharField(max_length=255, primary_key=True)
start_datetime = models.DateTimeField(default=timezone.now)
2025-08-29 09:11:20 +09:00
end_datetime = models.DateTimeField(default=get_default_end_datetime)
2024-07-22 06:53:48 +00:00
def __str__(self):
return f"{self.event_name} - From:{self.start_datetime} To:{self.end_datetime}"
2024-07-25 00:57:48 +00:00
def get_default_category():
2025-08-29 09:11:20 +09:00
"""デフォルトカテゴリーを取得または作成"""
try:
return NewCategory.objects.get_or_create(
category_name="Default Category",
defaults={'category_number': 1}
)[0].id
except Exception:
return 1 # フェイルセーフ
def get_default_multipoint():
"""デフォルトのMultiPointを取得"""
try:
from django.contrib.gis.geos import MultiPoint, Point
return MultiPoint([Point(0, 0)])
except Exception:
return None
def get_default_event():
"""デフォルトイベントを取得または作成"""
try:
from datetime import timedelta
return NewEvent2.objects.get_or_create(
event_name="Default Event",
defaults={
'start_datetime': timezone.now(),
'end_datetime': timezone.now() + timedelta(days=1)
}
)[0].id
except Exception:
return 1 # フェイルセーフ
2024-07-25 00:57:48 +00:00
2024-07-22 06:53:48 +00:00
class Team(models.Model):
# 既存フィールド
2024-07-22 06:53:48 +00:00
team_name = models.CharField(max_length=255)
2024-07-25 00:57:48 +00:00
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='owned_teams', blank=True, null=True)
category = models.ForeignKey('NewCategory', on_delete=models.SET_DEFAULT, default=get_default_category)
# MobServer統合フィールド
zekken_number = models.CharField(max_length=20, blank=True, null=True) # team_table.zekken_number
event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, blank=True, null=True) # team_table.event_code
password = models.CharField(max_length=100, blank=True, null=True) # team_table.password
class_name = models.CharField(max_length=100, blank=True, null=True) # team_table.class_name
trial = models.BooleanField(default=False) # team_table.trial
# 地理情報
location = models.PointField(null=True, blank=True, srid=4326)
# 統合管理フィールド
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
2024-07-25 00:57:48 +00:00
class Meta:
constraints = [
models.UniqueConstraint(
fields=['zekken_number', 'event'],
name='unique_team_per_event',
condition=models.Q(zekken_number__isnull=False, event__isnull=False)
)
]
2024-07-22 06:53:48 +00:00
def __str__(self):
if self.zekken_number and self.event:
return f"{self.zekken_number}-{self.team_name} ({self.event.event_name})"
return f"{self.team_name}, owner:{self.owner.lastname if self.owner else 'None'} {self.owner.firstname if self.owner else ''}"
2024-07-22 06:53:48 +00:00
class Member(models.Model):
2024-07-25 00:57:48 +00:00
team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='members')
2024-07-22 06:53:48 +00:00
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
2024-08-01 07:51:52 +00:00
firstname = models.CharField(max_length=255, blank=True, null=True)
lastname = models.CharField(max_length=255, blank=True, null=True)
date_of_birth = models.DateField(null=True, blank=True)
female = models.BooleanField(default=False)
2024-07-25 00:57:48 +00:00
is_temporary = models.BooleanField(default=False) # Akira 2024-7-24
2024-07-22 06:53:48 +00:00
class Meta:
unique_together = ('team', 'user')
def __str__(self):
2024-10-27 18:22:01 +00:00
return f"{self.team.team_name} - {self.user.lastname} {self.user.firstname}"
2024-07-22 06:53:48 +00:00
2024-07-25 00:57:48 +00:00
#
2024-07-22 06:53:48 +00:00
class Category(models.Model):
category_name = models.CharField(max_length=255, primary_key=True)
2024-07-25 00:57:48 +00:00
category_number = models.IntegerField(default=0)
duration = models.DurationField(default=timedelta(hours=5))
num_of_member = models.IntegerField(default=1)
family = models.BooleanField(default=False)
female = models.BooleanField(default=False)
class Meta:
unique_together = ('category_name','category_number')
def __str__(self):
hours = self.duration.total_seconds() // 3600
return f"{self.category_name} - {self.category_number} ({int(hours)}時間)"
@property
def hours(self):
return self.duration.total_seconds() // 3600
class NewCategory(models.Model):
category_name = models.CharField(max_length=255, unique=True)
category_number = models.IntegerField(default=0)
2024-07-22 06:53:48 +00:00
duration = models.DurationField(default=timedelta(hours=5))
num_of_member = models.IntegerField(default=1)
family = models.BooleanField(default=False)
female = models.BooleanField(default=False)
2025-01-23 17:08:50 +09:00
trial = models.BooleanField(default=False)
2024-07-22 06:53:48 +00:00
class Meta:
unique_together = ('category_name','category_number')
def __str__(self):
hours = self.duration.total_seconds() // 3600
return f"{self.category_name} - {self.category_number} ({int(hours)}時間)"
@property
def hours(self):
return self.duration.total_seconds() // 3600
class Entry(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE)
2025-08-29 09:11:20 +09:00
event = models.ForeignKey(NewEvent2, on_delete=models.CASCADE, default=get_default_event)
category = models.ForeignKey(NewCategory, on_delete=models.CASCADE, default=get_default_category)
date = models.DateTimeField(default=timezone.now)
2024-07-25 00:57:48 +00:00
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE,blank=True, null=True) # Akira 2024-7-24
zekken_number = models.IntegerField(default=0)
2025-08-20 19:15:19 +09:00
zekken_label = models.CharField(max_length=255, blank=True, null=True)
is_active = models.BooleanField(default=True) # 新しく追加
hasParticipated = models.BooleanField(default=False) # 新しく追加
hasGoaled = models.BooleanField(default=False) # 新しく追加
2025-08-27 15:01:06 +09:00
# API変更要求書対応: スタッフ権限管理 (2025-08-27)
staff_privileges = models.BooleanField(default=False, help_text="スタッフ権限フラグ")
can_access_private_events = models.BooleanField(default=False, help_text="非公開イベント参加権限")
2025-09-04 19:25:14 +09:00
# API変更要求書対応: 競技状態管理 (2025-09-04)
is_in_rog = models.BooleanField(default=False, help_text='ロゲイニング中=スタートしたらTrue')
rogaining_counted = models.BooleanField(default=False, help_text='ロゲイニングチェックイン履歴あり=一度でもスタート・ゴール以外でチェックインしたらTrue')
ready_for_goal = models.BooleanField(default=False, help_text='ゴール準備完了=スタートから遠くに移動した際にTrueになる')
is_at_goal = models.BooleanField(default=False, help_text='ゴール状態=ゴールしたらTrue')
start_time = models.DateTimeField(null=True, blank=True, help_text='スタート時刻')
goal_time = models.DateTimeField(null=True, blank=True, help_text='ゴール時刻')
last_checkin_time = models.DateTimeField(null=True, blank=True, help_text='最後のチェックイン時刻')
2025-08-27 15:01:06 +09:00
VALIDATION_STATUS_CHOICES = [
('approved', 'Approved'),
('pending', 'Pending'),
('rejected', 'Rejected'),
]
team_validation_status = models.CharField(
max_length=20,
choices=VALIDATION_STATUS_CHOICES,
default='approved',
help_text="チーム承認状況"
)
2024-07-22 06:53:48 +00:00
class Meta:
2025-01-23 17:24:42 +09:00
unique_together = ('zekken_number', 'event','date')
2024-07-22 06:53:48 +00:00
def __str__(self):
return f"{self.zekken_number} - {self.team.team_name} - {self.event.event_name} - {self.date}"
2024-07-22 06:53:48 +00:00
def clean(self):
super().clean()
if self.event and self.category and self.date:
start = self.event.start_datetime
end = self.event.end_datetime #- self.category.duration
if not (start.date() <= self.date.date() <= end.date()):
2024-07-22 06:53:48 +00:00
raise ValidationError({
'date': f'日時{self.date}{start.date()}から{end.date()}の間である必要があります。'
2024-07-22 06:53:48 +00:00
})
# メンバーの年齢と性別をチェック
2024-07-26 04:03:15 +00:00
if self.team: # and not self.team.members.exists():
members = self.team.members.all() # membersを適切に取得
if not members.exists():
raise ValidationError({'team': 'チームにメンバーが登録されていません。'})
2024-07-25 00:57:48 +00:00
2024-07-26 04:03:15 +00:00
#members = Member.objects.filter(team=self.team)
#if not members.exists():
# # ここで、owner をMemberに登録する。 Akira 2024-7-24
# raise ValidationError("チームにメンバーが登録されていません。")
adults = [m for m in members if self.is_adult(m.user.date_of_birth)]
children = [m for m in members if self.is_child(m.user.date_of_birth)]
teenagers = [m for m in members if self.is_teenager(m.user.date_of_birth)]
if self.category.family:
if not (adults and children):
raise ValidationError("ファミリーカテゴリーには、18歳以上のメンバーと小学生以下のメンバーが各1名以上必要です。")
else:
if not adults:
raise ValidationError("18歳以上のメンバーが1名以上必要です。")
2025-01-23 17:08:50 +09:00
if children and not self.category.trial:
raise ValidationError("ファミリーカテゴリーまたはお試し以外では、小学生以下のメンバーは参加できません。")
2024-07-26 04:03:15 +00:00
if self.category.num_of_member == 1:
if len(members) != 1:
raise ValidationError("このカテゴリーはソロ参加のみ可能です。")
if not adults:
raise ValidationError("ソロ参加は18歳以上のみ可能です。")
if self.category.female and not members[0].user.female:
raise ValidationError("このカテゴリーは女性のみ参加可能です。")
if not self.category.female and members[0].user.female:
raise ValidationError("このカテゴリーは男性のみ参加可能です。")
if len(members) > self.category.num_of_member:
raise ValidationError(f"このカテゴリーは{self.category.num_of_member}名までの参加が必要です。")
2024-07-22 06:53:48 +00:00
def save(self, *args, **kwargs):
self.full_clean()
super().save(*args, **kwargs)
@staticmethod
def is_adult(birth_date):
today = date.today()
age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
return age >= 18
@staticmethod
def is_child(birth_date):
today = date.today()
age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
return age <= 12
@staticmethod
def is_teenager(birth_date):
today = date.today()
age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
return 13 <= age <= 17
2024-07-25 00:57:48 +00:00
class EntryMember(models.Model):
entry = models.ForeignKey(Entry, on_delete=models.CASCADE)
member = models.ForeignKey(Member, on_delete=models.CASCADE)
is_temporary = models.BooleanField(default=False) # Akira 2024-7-24
class Meta:
unique_together = ('entry', 'member')
def __str__(self):
return f"{self.entry.team.zekken_number} - {self.member.user.lastname} {self.member.user.firstname}"
2024-07-22 06:53:48 +00:00
2022-10-18 14:58:20 +05:30
class GoalImages(models.Model):
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
goalimage = models.FileField(upload_to='goals/%y%m%d', blank=True, null=True)
2024-11-08 08:33:18 +00:00
goaltime = models.DateTimeField(_("Goal time"), blank=True, null=True,auto_now=False, auto_now_add=False)
2022-10-18 14:58:20 +05:30
team_name = models.CharField(_("Team name"), max_length=255)
event_code = models.CharField(_("event code"), max_length=255)
cp_number = models.IntegerField(_("CP numner"))
zekken_number = models.TextField(
null=True, # False にする
blank=True, # False にする
help_text="ゼッケン番号"
)
2022-10-18 14:58:20 +05:30
2022-11-05 18:11:00 +05:30
class CheckinImages(models.Model):
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
checkinimage = models.FileField(upload_to='checkin/%y%m%d', blank=True, null=True)
checkintime = models.DateTimeField(_("Goal time"), auto_now=False, auto_now_add=False)
team_name = models.CharField(_("Team name"), max_length=255)
event_code = models.CharField(_("event code"), max_length=255)
cp_number = models.IntegerField(_("CP numner"))
class Checkpoint(models.Model):
"""チェックポイント管理モデルMobServer統合"""
# MobServer完全統合
cp_number = models.IntegerField() # checkpoint_table.cp_number
event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, blank=True, null=True)
cp_name = models.CharField(max_length=200, blank=True, null=True) # checkpoint_table.cp_name
# 位置情報PostGIS対応
location = models.PointField(srid=4326, blank=True, null=True) # latitude, longitude統合
# ポイント情報
photo_point = models.IntegerField(default=0) # checkpoint_table.photo_point
buy_point = models.IntegerField(default=0) # checkpoint_table.buy_point
# サンプル・メモ
sample_photo = models.CharField(max_length=500, blank=True, null=True)
colabo_company_memo = models.TextField(blank=True, null=True)
# 統合管理フィールド
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['cp_number', 'event'],
name='unique_cp_per_event'
)
]
indexes = [
models.Index(fields=['event', 'cp_number'], name='idx_checkpoint_event_cp'),
GistIndex(fields=['location'], name='idx_checkpoint_location'),
]
def __str__(self):
return f"CP{self.cp_number} - {self.cp_name} ({self.event.event_code if self.event.event_code else self.event.event_name})"
2024-10-27 18:22:01 +00:00
class GpsCheckin(models.Model):
2025-08-29 18:39:20 +09:00
id = models.AutoField(primary_key=True)
event_code = models.CharField(max_length=255, null=False, default='')
zekken = models.CharField(max_length=20, null=True, blank=True)
serial_number = models.CharField(max_length=20, null=True, blank=True)
cp_number = models.CharField(max_length=20, null=True, blank=True)
lat = models.FloatField(null=True, blank=True)
lng = models.FloatField(null=True, blank=True)
checkin_time = models.DateTimeField(null=True, blank=True)
record_time = models.DateTimeField(null=True, blank=True)
location = models.PointField(srid=4326, null=True, blank=True)
mobserver_id = models.IntegerField(null=True, blank=True)
event_id = models.BigIntegerField(null=True, blank=True)
team_id = models.BigIntegerField(null=True, blank=True)
checkpoint_id = models.BigIntegerField(null=True, blank=True)
2025-08-29 20:05:34 +09:00
# データベースの実際のフィールドと一致させる
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
2024-10-27 18:22:01 +00:00
class Meta:
2025-08-29 18:39:20 +09:00
db_table = 'rog_gpscheckin'
managed = True
2024-11-08 04:30:58 +00:00
2024-10-27 18:22:01 +00:00
def __str__(self):
2025-08-29 18:39:20 +09:00
return f"GPS Checkin {self.id} - {self.zekken}"
2024-10-27 18:22:01 +00:00
2022-03-14 10:50:11 +05:30
class RogUser(models.Model):
2022-05-12 02:15:36 +05:30
user=models.OneToOneField(CustomUser, on_delete=models.CASCADE)
2022-03-14 10:50:11 +05:30
phone=models.CharField(_('Phone Number'), max_length=55)
first_name=models.CharField(_('First Name'), max_length=255)
middle_name=models.CharField(_('Middle Name'), max_length=255, blank=True, null=True)
last_name=models.CharField(_('last_name'), max_length=255)
nickname=models.CharField(_('Nickname'), max_length=255, blank=True, null=True)
country=models.CharField(_('Country'), max_length=255, default='Japan')
language=models.CharField(_('Language'), max_length=255, default='Japanese')
prefecture=models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
sex=models.CharField(_('Sex'), max_length=255, default='unknown', blank=True, null=True)
birthyear=models.IntegerField(_('Birth year'), blank=True, null=True)
family_structure =models.IntegerField(_('Family Structure'), blank=True, null=True)
2022-05-12 02:15:36 +05:30
introducer = models.ForeignKey(CustomUser, related_name='introduced_uesr', on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
level= models.IntegerField(_('Level'), blank=True, null=True, default=0)
2022-06-08 21:40:43 +05:30
paid=models.BooleanField(_("Is Paid"),default=False)
parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
2022-03-14 10:50:11 +05:30
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="roguser_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
2022-05-12 02:15:36 +05:30
2022-03-14 10:50:11 +05:30
class SystemSettings(models.Model):
setting_name=models.CharField(_('Settings Name'), max_length=255)
version=models.CharField(_('Version'), max_length=10, blank=True, null=True)
effective_date=models.DateTimeField()
end_date=models.DateTimeField()
parammeters=models.CharField(_('Parameters'), max_length=512)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="system_setting_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
class Location(models.Model):
2023-05-26 13:56:26 +05:30
location_id=models.IntegerField(_('Location id'), blank=True, null=True, db_index=True)
2022-09-07 14:53:57 +05:30
sub_loc_id=models.CharField(_('Sub location id'), max_length=2048, blank=True, null=True)
2022-08-22 19:50:51 +05:30
cp=models.FloatField(_('Check Point'), blank=False, null=True, default=0)
2022-06-13 14:31:56 +05:30
location_name=models.CharField(_('Location Name'), max_length=2048, default="--- 場所をお願いします --")
2023-05-26 13:56:26 +05:30
category=models.CharField(_('Category'), max_length=2048, blank=True, null=True, db_index=True)
2022-07-25 13:53:12 +05:30
subcategory=models.CharField(_('Sub Category'), max_length=2048, blank=True, null=True)
2022-03-14 10:50:11 +05:30
zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
2022-06-13 14:31:56 +05:30
address = models.CharField(_('Address'), max_length=2048, blank=True, null=True)
prefecture = models.CharField(_('Prefecture'), max_length=2048, blank=True, null=True)
area= models.CharField(_('Area'), max_length=2048, blank=True, null=True)
city= models.CharField(_('City'), max_length=2048, blank=True, null=True)
2022-04-18 11:33:49 +05:30
latitude = models.FloatField('Latitude', blank=True, null=True)
longitude = models.FloatField('Latitude', blank=True, null=True)
2022-06-19 13:07:41 +05:30
photos=models.CharField(_('Photos'), max_length=2048, blank=True, null=True)
2022-06-13 14:31:56 +05:30
videos=models.CharField(_('Videos'), max_length=2048, blank=True, null=True)
webcontents=models.CharField(_('Web Content'), max_length=2048, blank=True, null=True)
status=models.CharField(_('Status'),max_length=2048, blank=True, null=True)
portal=models.CharField(_('Portal'), max_length=2048,blank=True, null=True)
2023-05-26 13:56:26 +05:30
group=models.CharField(_('Group'), max_length=2048,blank=True, null=True, db_index=True)
2022-06-13 14:31:56 +05:30
phone=models.CharField(_('Phone'), max_length=2048,blank=True, null=True)
fax=models.CharField(_('Fax'), max_length=2048, blank=True, null=True)
email=models.EmailField(_('Email'), max_length=2048,blank=True, null=True)
facility=models.CharField(_('Facility'), max_length=2048, blank=True, null=True)
remark=models.CharField(_('Remarks'), max_length=2048, blank=True, null=True)
tags=models.CharField(_('Tags'), max_length=2048, blank=True, null=True)
2023-05-26 13:56:26 +05:30
event_name = models.CharField(_('Event name'), max_length=2048, blank=True, null=True, db_index=True)
event_active = models.BooleanField(_("Is Event active"),default=True, db_index=True)
2022-06-18 15:28:30 +05:30
hidden_location = models.BooleanField(_("Is Hidden Location"),default=False)
auto_checkin = models.BooleanField(_("Is AutoCheckin"),default=False)
2022-08-16 12:59:57 +05:30
checkin_radius = models.FloatField(_("Checkin radious"), blank=True, null=True, default=15.0)
checkin_point = models.FloatField(_("Checkin Point"), blank=True, null=True, default=10)
buy_point = models.FloatField(_("buy Point"), blank=True, null=True, default=0)
2022-06-18 15:28:30 +05:30
evaluation_value = models.CharField(_('Evaluation value (評価)'),max_length=2048, blank=True, null=True)
shop_closed = models.BooleanField(_("Shop Closed (休業)"),default=False)
shop_shutdown = models.BooleanField(_("Shop Shutdown (閉業)"),default=False)
2022-06-19 13:07:41 +05:30
opening_hours_mon = models.CharField(_("Opening hours monday (月曜)"),max_length=512, blank=True, null=True)
opening_hours_tue = models.CharField(_("Opening hours tuesday (火曜)"), max_length=512,blank=True, null=True)
opening_hours_wed = models.CharField(_("Opening hours wednesday (水曜)"), max_length=512,blank=True, null=True)
opening_hours_thu = models.CharField(_("Opening hours thursday (木曜)"), max_length=512, blank=True, null=True)
opening_hours_fri = models.CharField(_("Opening hours frinday (金曜)"), max_length=512,blank=True, null=True)
opening_hours_sat = models.CharField(_("Opening hours saturday (土曜)"), max_length=512,blank=True, null=True)
opening_hours_sun = models.CharField(_("Opening hours sunday (日曜)"),max_length=512, blank=True, null=True)
2022-06-13 14:31:56 +05:30
parammeters=models.CharField(_('Parameters'), max_length=2048, blank=True, null=True)
2022-03-14 10:50:11 +05:30
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="location_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
2025-08-29 09:11:20 +09:00
geom=models.MultiPointField(srid=4326, default=get_default_multipoint)
2022-03-14 10:50:11 +05:30
2023-05-26 13:56:26 +05:30
class Meta:
indexes = [
GistIndex(fields=['geom']),
]
2022-03-14 10:50:11 +05:30
def __str__(self):
2022-04-08 13:24:55 +05:30
return self.location_name
2022-03-14 10:50:11 +05:30
2022-08-17 16:00:17 +05:30
class templocation(models.Model):
location_id=models.IntegerField(_('Location id'), blank=True, null=True)
2022-09-09 18:54:00 +05:30
sub_loc_id=models.CharField(_('Sub location id'), max_length=2048, blank=True, null=True)
2022-08-22 19:50:51 +05:30
cp=models.FloatField(_('Check Point'), blank=False, null=True, default=0)
2022-08-17 16:00:17 +05:30
location_name=models.CharField(_('Location Name'), max_length=2048, default="--- 場所をお願いします --")
category=models.CharField(_('Category'), max_length=2048, blank=True, null=True)
subcategory=models.CharField(_('Sub Category'), max_length=2048, blank=True, null=True)
zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
address = models.CharField(_('Address'), max_length=2048, blank=True, null=True)
prefecture = models.CharField(_('Prefecture'), max_length=2048, blank=True, null=True)
area= models.CharField(_('Area'), max_length=2048, blank=True, null=True)
city= models.CharField(_('City'), max_length=2048, blank=True, null=True)
latitude = models.FloatField('Latitude', blank=True, null=True)
longitude = models.FloatField('Latitude', blank=True, null=True)
photos=models.CharField(_('Photos'), max_length=2048, blank=True, null=True)
videos=models.CharField(_('Videos'), max_length=2048, blank=True, null=True)
webcontents=models.CharField(_('Web Content'), max_length=2048, blank=True, null=True)
status=models.CharField(_('Status'),max_length=2048, blank=True, null=True)
portal=models.CharField(_('Portal'), max_length=2048,blank=True, null=True)
group=models.CharField(_('Group'), max_length=2048,blank=True, null=True)
phone=models.CharField(_('Phone'), max_length=2048,blank=True, null=True)
fax=models.CharField(_('Fax'), max_length=2048, blank=True, null=True)
email=models.EmailField(_('Email'), max_length=2048,blank=True, null=True)
facility=models.CharField(_('Facility'), max_length=2048, blank=True, null=True)
remark=models.CharField(_('Remarks'), max_length=2048, blank=True, null=True)
tags=models.CharField(_('Tags'), max_length=2048, blank=True, null=True)
event_name = models.CharField(_('Event name'), max_length=2048, blank=True, null=True)
event_active = models.BooleanField(_("Is Event active"),default=True)
hidden_location = models.BooleanField(_("Is Hidden Location"),default=False)
auto_checkin = models.BooleanField(_("Is AutoCheckin"),default=False)
checkin_radius = models.FloatField(_("Checkin radious"), blank=True, null=True, default=15.0)
checkin_point = models.FloatField(_("Checkin Point"), blank=True, null=True, default=10)
buy_point = models.FloatField(_("buy Point"), blank=True, null=True, default=0)
evaluation_value = models.CharField(_('Evaluation value (評価)'),max_length=2048, blank=True, null=True)
shop_closed = models.BooleanField(_("Shop Closed (休業)"),default=False)
shop_shutdown = models.BooleanField(_("Shop Shutdown (閉業)"),default=False)
opening_hours_mon = models.CharField(_("Opening hours monday (月曜)"),max_length=512, blank=True, null=True)
opening_hours_tue = models.CharField(_("Opening hours tuesday (火曜)"), max_length=512,blank=True, null=True)
opening_hours_wed = models.CharField(_("Opening hours wednesday (水曜)"), max_length=512,blank=True, null=True)
opening_hours_thu = models.CharField(_("Opening hours thursday (木曜)"), max_length=512, blank=True, null=True)
opening_hours_fri = models.CharField(_("Opening hours frinday (金曜)"), max_length=512,blank=True, null=True)
opening_hours_sat = models.CharField(_("Opening hours saturday (土曜)"), max_length=512,blank=True, null=True)
opening_hours_sun = models.CharField(_("Opening hours sunday (日曜)"),max_length=512, blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=2048, blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
last_updated_user=models.ForeignKey(CustomUser, related_name="temp_location_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
last_updated_at=models.DateTimeField(auto_now=True)
geom=models.MultiPointField(srid=4326)
def __str__(self):
return self.location_name
class Location2025(models.Model):
"""
2025年版チェックポイント管理モデル
CSVアップロード対応の新しいチェックポイント管理システム
"""
# 基本情報
cp_number = models.IntegerField(_('CP番号'), db_index=True)
event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, verbose_name=_('イベント'))
cp_name = models.CharField(_('CP名'), max_length=255)
2025-08-31 15:04:35 +09:00
category = models.CharField(_('カテゴリ'), max_length=255, blank=True, null=True)
2025-08-30 03:48:07 +09:00
sub_loc_id = models.CharField(_('サブロケーションID'), max_length=2048, blank=True, null=True)
subcategory = models.CharField(_('サブカテゴリ'), max_length=2048, blank=True, null=True)
# 位置情報
latitude = models.FloatField(_('緯度'), null=True, blank=True)
longitude = models.FloatField(_('経度'), null=True, blank=True)
location = models.PointField(_('位置'), srid=4326, null=True, blank=True)
# ポイント情報
checkin_point = models.IntegerField(_('チェックイン得点'), default=10)
buy_point = models.IntegerField(_('買い物ポイント'), default=0)
# チェックイン設定
checkin_radius = models.FloatField(_('チェックイン範囲(m)'), default=15.0)
auto_checkin = models.BooleanField(_('自動チェックイン'), default=False)
# 営業情報
shop_closed = models.BooleanField(_('休業中'), default=False)
shop_shutdown = models.BooleanField(_('閉業'), default=False)
opening_hours = models.TextField(_('営業時間'), blank=True, null=True)
# 詳細情報
address = models.CharField(_('住所'), max_length=512, blank=True, null=True)
2025-08-31 15:04:35 +09:00
zip_code = models.CharField(_('郵便番号'), max_length=12, blank=True, null=True)
prefecture = models.CharField(_('都道府県'), max_length=255, blank=True, null=True)
area = models.CharField(_('地域'), max_length=255, blank=True, null=True)
city = models.CharField(_('市区町村'), max_length=255, blank=True, null=True)
phone = models.CharField(_('電話番号'), max_length=32, blank=True, null=True)
website = models.URLField(_('ウェブサイト'), blank=True, null=True)
2025-08-31 15:04:35 +09:00
facility = models.CharField(_('設備'), max_length=255, blank=True, null=True)
description = models.TextField(_('説明'), blank=True, null=True)
2025-08-30 03:48:07 +09:00
# 追加フィールドLocationテーブルから移行
photos = models.CharField(_('写真'), max_length=2048, blank=True, null=True)
videos = models.CharField(_('動画'), max_length=2048, blank=True, null=True)
remark = models.TextField(_('備考'), blank=True, null=True)
tags = models.CharField(_('タグ'), max_length=2048, blank=True, null=True)
evaluation_value = models.CharField(_('評価値'), max_length=255, blank=True, null=True)
hidden_location = models.BooleanField(_('隠しロケーション'), default=False)
# 管理情報
is_active = models.BooleanField(_('有効'), default=True, db_index=True)
sort_order = models.IntegerField(_('表示順'), default=0)
# CSVアップロード関連
csv_source_file = models.CharField(_('CSVファイル名'), max_length=255, blank=True, null=True)
csv_upload_date = models.DateTimeField(_('CSVアップロード日時'), null=True, blank=True)
csv_upload_user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True,
related_name='location2025_csv_uploads', verbose_name=_('CSVアップロードユーザー'))
# タイムスタンプ
created_at = models.DateTimeField(_('作成日時'), auto_now_add=True)
updated_at = models.DateTimeField(_('更新日時'), auto_now=True)
created_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True,
related_name='location2025_created', verbose_name=_('作成者'))
updated_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True,
related_name='location2025_updated', verbose_name=_('更新者'))
class Meta:
db_table = 'rog_location2025'
verbose_name = _('チェックポイント2025')
verbose_name_plural = _('チェックポイント2025')
unique_together = ['cp_number', 'event']
ordering = ['event', 'sort_order', 'cp_number']
indexes = [
models.Index(fields=['event', 'cp_number'], name='location2025_event_cp_idx'),
models.Index(fields=['event', 'is_active'], name='location2025_event_active_idx'),
models.Index(fields=['csv_upload_date'], name='location2025_csv_date_idx'),
GistIndex(fields=['location'], name='location2025_location_gist_idx'),
]
def save(self, *args, **kwargs):
# 緯度経度からLocationフィールドを自動生成
if self.latitude and self.longitude:
from django.contrib.gis.geos import Point
self.location = Point(self.longitude, self.latitude, srid=4326)
super().save(*args, **kwargs)
def __str__(self):
return f"{self.event.event_name} - CP{self.cp_number}: {self.cp_name}"
@property
def total_point(self):
"""総得点を計算"""
return self.checkin_point + self.buy_point
@classmethod
def import_from_csv(cls, csv_file, event, user=None):
"""
CSVファイルからチェックポイントデータをインポート全フィールド対応
CSV形式:
cp_number,cp_name,latitude,longitude,checkin_point,buy_point,address,phone,description,
sub_loc_id,subcategory,photos,videos,tags,evaluation_value,remark,hidden_location
"""
import csv
import io
from django.utils import timezone
from django.contrib.gis.geos import Point
if isinstance(csv_file, str):
# ファイルパスの場合
with open(csv_file, 'r', encoding='utf-8') as f:
csv_content = f.read()
else:
# アップロードされたファイルの場合
csv_content = csv_file.read().decode('utf-8')
csv_reader = csv.DictReader(io.StringIO(csv_content))
created_count = 0
updated_count = 0
errors = []
for row_num, row in enumerate(csv_reader, start=2):
try:
2025-08-31 13:49:56 +09:00
# cp列から番号を抽出 (例: "#1(5)" -> 1, "-2" -> -2)
cp_raw = row.get('cp', row.get('cp_number', ''))
cp_number = None
if cp_raw:
import re
# #で始まる場合は#の後の数字を抽出 (例: "#1(5)" -> "1")
if cp_raw.startswith('#'):
match = re.search(r'#(-?\d+)', str(cp_raw))
if match:
cp_number = int(match.group(1))
else:
# 直接数値の場合
try:
cp_number = int(cp_raw)
except ValueError:
pass
if cp_number is None:
errors.append(f"{row_num}: CP番号が無効です (値: {cp_raw})")
continue
# 緯度経度から位置情報を作成
latitude = float(row['latitude']) if row.get('latitude') else None
longitude = float(row['longitude']) if row.get('longitude') else None
location = None
if latitude and longitude:
location = Point(longitude, latitude)
# hidden_locationのブール値変換
hidden_location = False
if row.get('hidden_location'):
hidden_str = row.get('hidden_location', '').lower()
hidden_location = hidden_str in ['true', '1', 'yes', 'on']
2025-08-31 14:31:31 +09:00
# checkin_radiusの処理
checkin_radius = None
if row.get('checkin_radius'):
radius_str = row.get('checkin_radius', '').strip()
if radius_str and radius_str.lower() not in ['false', '', 'null']:
try:
checkin_radius = float(radius_str)
except ValueError:
# デフォルト値を設定
checkin_radius = -1.0 # 要タップ
else:
checkin_radius = -1.0 # CSVでFALSEや空の場合は要タップ
else:
checkin_radius = -1.0 # デフォルトは要タップ
defaults = {
# 基本フィールド
2025-08-31 13:49:56 +09:00
'cp_name': row.get('loc_name', row.get('cp_name', f'CP{cp_number}')),
'latitude': latitude,
'longitude': longitude,
'location': location,
'checkin_point': int(row.get('checkin_point', row.get('cp_point', 10))), # 後方互換性のためcp_pointもサポート
'buy_point': int(row.get('buy_point', 0)),
'address': row.get('address', ''),
'phone': row.get('phone', ''),
2025-08-31 13:49:56 +09:00
'description': row.get('description', row.get('remark', '')),
2025-08-31 14:31:31 +09:00
'checkin_radius': checkin_radius, # チェックイン範囲を追加
# 新しいフィールド
2025-08-31 15:04:35 +09:00
'category': row.get('category', ''),
'sub_loc_id': row.get('sub_loc_id', ''),
2025-08-31 15:04:35 +09:00
'subcategory': row.get('subcategory', ''),
'photos': row.get('photos', ''),
'videos': row.get('videos', ''),
'tags': row.get('tags', ''),
'evaluation_value': row.get('evaluation_value', ''),
'remark': row.get('remark', ''),
'hidden_location': hidden_location,
2025-08-31 14:45:50 +09:00
# 追加フィールド
'area': row.get('area', ''),
'zip_code': row.get('zip', ''),
'prefecture': row.get('prefecture', ''),
'city': row.get('city', ''),
'website': row.get('webcontent', ''),
'facility': row.get('facility', ''),
# 管理フィールド
'csv_source_file': getattr(csv_file, 'name', 'uploaded_file.csv'),
'csv_upload_date': timezone.now(),
'csv_upload_user': user,
'updated_by': user,
}
if user:
defaults['created_by'] = user
obj, created = cls.objects.update_or_create(
cp_number=cp_number,
event=event,
defaults=defaults
)
if created:
created_count += 1
else:
updated_count += 1
except (ValueError, KeyError) as e:
errors.append(f"{row_num}: {str(e)}")
continue
return {
'created': created_count,
'updated': updated_count,
'errors': errors
}
2022-03-14 10:50:11 +05:30
class Location_line(models.Model):
location_id=models.IntegerField(_('Location id'), blank=True, null=True)
location_name=models.CharField(_('Location Name'), max_length=255)
category=models.CharField(_('Category'), max_length=255, blank=True, null=True)
zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
address = models.CharField(_('Address'), max_length=512, blank=True, null=True)
prefecture = models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
area= models.CharField(_('Area'), max_length=255, blank=True, null=True)
city= models.CharField(_('City'), max_length=255, blank=True, null=True)
photos=models.CharField(_('Phptos'), max_length=255, blank=True, null=True)
videos=models.CharField(_('Videos'), max_length=255, blank=True, null=True)
webcontents=models.CharField(_('Web Content'), max_length=255, blank=True, null=True)
status=models.CharField(_('Status'),max_length=255, blank=True, null=True)
portal=models.CharField(_('Portal'), max_length=255,blank=True, null=True)
group=models.CharField(_('Group'), max_length=255,blank=True, null=True)
phone=models.CharField(_('Phone'), max_length=255,blank=True, null=True)
fax=models.CharField(_('Fax'), max_length=255, blank=True, null=True)
email=models.EmailField(_('Email'), max_length=255,blank=True, null=True)
facility=models.CharField(_('Facility'), max_length=255, blank=True, null=True)
remark=models.CharField(_('Remarks'), max_length=255, blank=True, null=True)
tags=models.CharField(_('Tags'), max_length=512, blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="location_line_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
geom=models.MultiLineStringField(srid=4326, blank=True, null=True)
def __str__(self):
return str(self.location_id)
class Location_polygon(models.Model):
location_id=models.IntegerField(_('Location id'), blank=True, null=True)
location_name=models.CharField(_('Location Name'), max_length=255)
category=models.CharField(_('Category'), max_length=255, blank=True, null=True)
zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
address = models.CharField(_('Address'), max_length=512, blank=True, null=True)
prefecture = models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
area= models.CharField(_('Area'), max_length=255, blank=True, null=True)
city= models.CharField(_('City'), max_length=255, blank=True, null=True)
photos=models.CharField(_('Phptos'), max_length=255, blank=True, null=True)
videos=models.CharField(_('Videos'), max_length=255, blank=True, null=True)
webcontents=models.CharField(_('Web Content'), max_length=255, blank=True, null=True)
status=models.CharField(_('Status'),max_length=255, blank=True, null=True)
portal=models.CharField(_('Portal'), max_length=255,blank=True, null=True)
group=models.CharField(_('Group'), max_length=255,blank=True, null=True)
phone=models.CharField(_('Phone'), max_length=255,blank=True, null=True)
fax=models.CharField(_('Fax'), max_length=255, blank=True, null=True)
email=models.EmailField(_('Email'), max_length=255,blank=True, null=True)
facility=models.CharField(_('Facility'), max_length=255, blank=True, null=True)
remark=models.CharField(_('Remarks'), max_length=255, blank=True, null=True)
tags=models.CharField(_('Tags'), max_length=512, blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="location_polygon_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
geom=models.MultiPolygonField(srid=4326, blank=True, null=True)
def __str__(self):
return str(self.location_name)
2022-06-13 01:55:45 +05:30
class UserTracks(models.Model):
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
geom=models.MultiPointField(srid=4326)
created_at=models.DateTimeField(auto_now_add=True)
2022-03-14 10:50:11 +05:30
EVENT_STATUS = (
("PREPARING", _("Preparing")),
("PROMOTION", _("Promotion")),
("EVENT", _("Event")),
("END", _("End"))
)
class Event(models.Model):
2022-09-05 19:10:39 +05:30
user=models.ManyToManyField(CustomUser, through='EventUser', related_name='even')
2022-03-14 10:50:11 +05:30
tagname=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
status=models.CharField(max_length=256, choices=EVENT_STATUS)
price=models.IntegerField(_('Paid Amount'), default=0)
promotion_date=models.DateTimeField(_('Promotion date'), blank=True, null=True)
event_start=models.DateTimeField(_('Promotion date'), blank=True, null=True)
event_end=models.DateTimeField(_('Promotion date'), blank=True, null=True)
remark=models.CharField(max_length=256, blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="event_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
2022-09-05 19:10:39 +05:30
class EventUser(models.Model):
user=models.ForeignKey(CustomUser,on_delete=models.CASCADE, related_name='user')
event=models.ForeignKey(Event, on_delete=models.CASCADE, related_name='event')
2022-03-14 10:50:11 +05:30
ROG_STATUS = (
("REGISTERED", _("Registered")),
("ACCEPTED", _("accepted")),
("PAID", _("paid")),
("JOINED", _("joined")),
("CANCELED", _("Canceled"))
)
class JoinedEvent(models.Model):
2022-05-12 02:15:36 +05:30
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
tagname=models.CharField(_('Tag Name'), max_length=255, blank=True, null=True)
status=models.CharField(max_length=256, choices=ROG_STATUS)
registrationid=models.CharField(_('Registration Id'), max_length=56)
payment_code=models.CharField(_('Payment Code'), max_length=255)
paid=models.IntegerField(_('Paid Amount'), default=0)
remark=models.CharField(_('Remark'), max_length=255, blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="joined_event_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
class Favorite(models.Model):
2022-05-12 02:15:36 +05:30
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
location=models.ForeignKey(Location, on_delete=models.CASCADE)
good=models.IntegerField(_('Good'), default=0)
favorite=models.IntegerField(_('Favorite'), default=0)
evaluation=models.IntegerField(_('Evaluation'), default=0)
number_visit=models.IntegerField(_('Good'), default=0)
last_visited=models.DateTimeField(_('Last Visited'), blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="favorite_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
TRAVEL_CATEGORY = (
("PRIVATE", _("Private")),
("GROUP", _("Group")),
("AGENT", _("Agent")),
("ROGAINING", _("Rogaining"))
)
class TravelList(models.Model):
travel_id= models.IntegerField(_('Travel Id'))
2022-05-12 02:15:36 +05:30
user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
start_date=models.DateTimeField(_('Start date') ,blank=True, null=True)
finish_date=models.DateTimeField(_('End date') ,blank=True, null=True)
category=models.CharField(max_length=256, choices=TRAVEL_CATEGORY)
title=models.CharField(_('Title'), max_length=255)
transportation=models.CharField(_('Transpotation'), max_length=255 ,blank=True, null=True)
moving_distance=models.IntegerField(blank=True, null=True)
duration=models.DurationField(_('Duration') ,blank=True, null=True)
eta=models.DateTimeField(blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512 ,blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="travel_list_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
class TravelPoint(models.Model):
travel_list= models.ForeignKey(TravelList, on_delete=models.DO_NOTHING)
location=models.ForeignKey(Location, on_delete=models.CASCADE)
distance=models.FloatField(blank=True, null=True)
transportation=models.CharField(_('Transpotation'), max_length=255 ,blank=True, null=True)
eta=models.DateTimeField(blank=True, null=True)
order_number=models.IntegerField(blank=True, null=True)
parammeters=models.CharField(_('Parameters'), max_length=512 ,blank=True, null=True)
created_at=models.DateTimeField(auto_now_add=True)
2022-05-12 02:15:36 +05:30
last_updated_user=models.ForeignKey(CustomUser, related_name="travelpoint_updated_user", on_delete=models.DO_NOTHING)
2022-03-14 10:50:11 +05:30
last_updated_at=models.DateTimeField(auto_now=True)
2022-05-12 02:15:36 +05:30
class Useractions(models.Model):
user=models.ForeignKey(CustomUser, related_name="action_user", on_delete=models.CASCADE)
location=models.ForeignKey(Location, related_name="action_location", on_delete=models.CASCADE)
wanttogo=models.BooleanField(default=False)
like=models.BooleanField(default=False)
checkin=models.BooleanField(default=False)
2022-06-13 01:55:45 +05:30
checkinimage=models.FileField(upload_to='%y%m%d', blank=True, null=True)
2022-05-12 02:15:36 +05:30
order =models.IntegerField(default=0)
created_at=models.DateTimeField(auto_now_add=True)
last_updated_at=models.DateTimeField(auto_now=True)
2022-03-14 10:50:11 +05:30
2022-05-12 02:15:36 +05:30
class TestModel(models.Model):
testbane=models.CharField(_("test field"), max_length=355)
wanttogo=models.BooleanField(default=False)
like=models.BooleanField(default=False)
checkin=models.BooleanField(default=False)
created_at=models.DateTimeField(auto_now_add=True)
last_updated_at=models.DateTimeField(auto_now=True)
2022-03-14 10:50:11 +05:30
def getTableForModel(tbl):
if tbl == 1:
2022-08-17 16:00:17 +05:30
return templocation.objects.model._meta.db_table;
2022-03-14 10:50:11 +05:30
elif tbl == 2:
return Location_line.objects.model._meta.db_table;
else:
return Location_polygon.objects.model._meta.db_table;
2022-08-17 16:00:17 +05:30
def getTempMappingforModel(tbl, shp):
2022-03-14 10:50:11 +05:30
if tbl == 1:
2022-08-17 16:00:17 +05:30
return LayerMapping(templocation, shp, location_mapping, transform=False)
2022-03-14 10:50:11 +05:30
elif tbl == 2:
return LayerMapping(Location_line, shp, location_line_mapping, transform=False)
else:
return LayerMapping(Location_polygon, shp, location_polygon_mapping, transform=False)
class ShapeLayers(models.Model):
name = models.CharField(_("Shape Layer"), max_length=255)
2022-03-23 12:13:30 +05:30
file = models.FileField(upload_to=get_file_path, blank=True)
2022-03-14 10:50:11 +05:30
uploaded_date = models.DateField(auto_now_add=True)
layerof = models.IntegerField(choices=LAYER_CHOICES, default=1)
table_name = models.CharField(_("Table name"), max_length=255, blank=True)
def __str__(self):
return self.name
2022-06-21 16:32:11 +05:30
class ShapeFileLocations(models.Model):
shapefile=models.CharField(_('Shapelayer'), max_length=2048 ,blank=True, null=True)
locid=models.IntegerField(blank=True, null=True)
2022-03-14 10:50:11 +05:30
2022-04-27 15:47:37 +05:30
@receiver(pre_save, sender=Location)
def location_presave(sender, instance, *args, **kwargs):
print("------############------------", instance.location_id)
2022-08-17 16:00:17 +05:30
templocation.objects.filter(location_id = instance.location_id).delete()
2022-04-27 15:47:37 +05:30
@receiver(pre_save, sender=Location_line)
def location_presave(sender, instance, *args, **kwargs):
Location_line.objects.filter(location_id = instance.location_id).delete()
@receiver(pre_save, sender=Location_polygon)
def location_presave(sender, instance, *args, **kwargs):
Location_polygon.objects.filter(location_id = instance.location_id).delete()
2022-03-14 10:50:11 +05:30
@receiver(pre_save, sender=ShapeLayers)
def my_callback(sender, instance, *args, **kwargs):
2022-04-27 15:47:37 +05:30
2022-03-14 10:50:11 +05:30
instance.table_name = getTableForModel(instance.layerof)
2022-04-27 15:47:37 +05:30
def deletePrevious(mdl, fields):
with transaction.atomic():
mdl.objects.filter(location_id = int(fields[0])).delete();
2022-06-21 16:32:11 +05:30
@receiver(post_delete,sender=ShapeLayers)
def deleteShapelocation(sender,instance,*args,**kwargs):
locids = ShapeFileLocations.objects.filter(shapefile=instance.name).values_list('locid')
print("------- name----")
print(locids)
print("------- name----")
2022-08-17 16:00:17 +05:30
templocation.objects.all().delete()
2022-06-21 16:32:11 +05:30
ShapeFileLocations.objects.filter(shapefile=instance.name).delete()
2022-04-27 15:47:37 +05:30
2022-03-14 10:50:11 +05:30
@receiver(post_save, sender=ShapeLayers)
2022-04-27 15:47:37 +05:30
def publish_data(sender, instance, created, **kwargs):
logger.info(f"Processing ShapeLayer: {instance.name}")
2022-03-14 10:50:11 +05:30
file = instance.file.path
file_format = os.path.basename(file).split('.')[-1]
file_name = os.path.basename(file).split('.')[0]
file_path = os.path.dirname(file)
name = instance.name
conn_str = f'postgresql://{env("POSTGRES_USER")}:{env("POSTGRES_PASS")}@{env("PG_HOST")}:{env("PG_PORT")}/{env("POSTGRES_DBNAME")}'
with zipfile.ZipFile(file, 'r') as zip_ref:
zip_ref.extractall(file_path)
os.remove(file)
try:
2024-07-31 00:56:23 +00:00
#logger.debug("Attempting to read shape file")
2022-09-27 17:52:14 +05:30
# print("before reading the file")
2022-03-14 10:50:11 +05:30
shp = glob.glob(r'{}/**/*.shp'.format(file_path), recursive=True)[0]
2024-07-31 00:56:23 +00:00
#logger.info(f"Shape file read: {shp}")
2022-09-27 17:52:14 +05:30
# print("this is the read file",shp)
2022-03-14 10:50:11 +05:30
gdf = gpd.read_file(shp)
crs_name = str(gdf.crs.srs)
2024-07-31 00:56:23 +00:00
#logger.debug(f"CRS name: {crs_name}")
2022-09-27 17:52:14 +05:30
# print(crs_name, 'crs - name')
2022-03-14 10:50:11 +05:30
epsg = int(crs_name.replace('epsg:',''))
if epsg is None:
epsg=4326
2022-08-17 16:00:17 +05:30
lm2 = getTempMappingforModel(instance.layerof, shp)
2024-07-31 00:56:23 +00:00
#logger.info("Saving to temporary table")
2022-09-27 17:52:14 +05:30
# print("### shape file is ###")
2022-03-14 10:50:11 +05:30
lm2.save(strict=True, verbose=True)
2024-07-31 00:56:23 +00:00
#logger.info("Save to temporary table completed")
2022-03-14 10:50:11 +05:30
os.remove(shp)
except Exception as e:
2022-07-25 13:53:12 +05:30
print('######## shape file##########',e)
2022-03-14 10:50:11 +05:30
try:
logger.debug("Attempting to read CSV file")
2022-03-19 18:42:55 +05:30
csv_f = glob.glob(r'{}/**/*.csv'.format(file_path), recursive=True)[0]
2022-03-23 11:58:12 +05:30
remove_bom_inplace(csv_f)
2022-03-14 10:50:11 +05:30
mdl = apps.get_model(app_label="rog", model_name=LAYER_CHOICES[instance.layerof -1][1])
2024-07-31 00:56:23 +00:00
print(mdl)
print(f"#### instance.layerof - {instance.layerof}")
#logger.debug(f"Model for layer: {mdl}")
2022-03-23 11:40:32 +05:30
with open(csv_f, mode="r", encoding="utf-8") as txt_file:
#heading = next(txt_file)
2022-03-23 11:16:59 +05:30
reader = csv.reader(txt_file, delimiter=",")
2022-03-19 18:42:55 +05:30
for fields in reader:
logger.debug(f"Processing row: {fields[0]}")
2022-03-23 09:59:11 +05:30
print("@@@@@@@@@@@@")
print(fields[0])
print("@@@@@@@@@@@@")
2022-04-07 14:53:11 +05:30
if instance.layerof == 1:
2022-06-22 12:00:00 +05:30
#insertShapeLayerLocation(instance.name, fields)
2024-07-31 00:56:23 +00:00
#updateLocation(mdl, fields)
update_or_create_location(mdl, fields)
2022-03-14 10:50:11 +05:30
if instance.layerof == 2:
updateLineTable(mdl, fields)
if instance.layerof == 3:
2022-03-19 18:42:55 +05:30
updatePolygonTable(mdl, fields)
2022-06-22 12:00:00 +05:30
with open(csv_f, mode="r", encoding="utf-8") as txt_file:
reader_2 = csv.reader(txt_file, delimiter=",")
for fields in reader_2:
logger.debug(f"Inserting ShapeLayerLocation: {fields[0]}")
2022-06-22 12:00:00 +05:30
print("@@@@@@@@@@@@")
print(fields[0])
print("@@@@@@@@@@@@")
if instance.layerof == 1:
insertShapeLayerLocation(instance.name, fields)
logger.info("CSV processing completed")
2022-03-14 10:50:11 +05:30
except Exception as e:
2022-07-25 13:53:12 +05:30
print('######## csv file ##########',e)
2022-03-14 10:50:11 +05:30
2022-04-07 14:53:11 +05:30
2022-06-21 16:32:11 +05:30
def insertShapeLayerLocation(name, fields):
logger.info(f"Attempting to insert ShapeFileLocations for file: {name}, location_id: {fields[0]}")
try:
sll = UserUploadUser(userfile=name, email=fields[0])
sll.save();
except Exception as e:
logger.error(f"Error inserting ShapeFileLocations: {e}", exc_info=True)
2022-08-30 18:20:15 +05:30
def insertUserUploadUser(name, fields):
try:
with transaction.atomic():
sll = UserUploadUser(userfile=name, email=fields[0])
sll.save()
except Exception as e:
logger.error(f"Error updating TempLocation: {e}", exc_info=True)
2024-07-31 00:56:23 +00:00
def update_or_create_location(mdl, fields):
try:
with transaction.atomic():
latitude = float(fields[11]) if fields[11] and len(fields[11]) > 0 else None
longitude = float(fields[12]) if fields[12] and len(fields[12]) > 0 else None
geom = MultiPoint(Point(longitude, latitude)) if latitude is not None and longitude is not None else None
defaults={
'sub_loc_id': fields[1] if len(fields[1]) > 0 else '',
'cp': fields[2] if len(fields[2]) > 0 else 0,
# その他のフィールド...
'location_name': fields[3] if len(fields[3]) > 0 else '',
'category': fields[4] if len(fields[4]) > 0 else '',
'subcategory': fields[5] if len(fields[5]) > 0 else '',
'zip': fields[6] if len(fields[6]) > 0 else '',
'address': fields[7] if len(fields[7]) > 0 else '',
'prefecture': fields[8] if len(fields[8]) > 0 else '',
'area': fields[9] if len(fields[9]) > 0 else '',
'city': fields[10] if len(fields[10]) > 0 else '',
'latitude': latitude,
'longitude': longitude,
'photos': fields[13] if len(fields[13]) > 0 else '',
'videos': fields[14] if len(fields[14]) > 0 else '',
'webcontents': fields[15] if len(fields[15]) > 0 else '',
'status': fields[16] if len(fields[16]) > 0 else '',
'portal': fields[17] if len(fields[17]) > 0 else '',
'group': fields[18] if len(fields[18]) > 0 else '',
'phone': fields[19] if len(fields[19]) > 0 else '',
'fax': fields[20] if len(fields[20]) > 0 else '',
'email': fields[21] if len(fields[21]) > 0 else '',
'facility': fields[22] if len(fields[22]) > 0 else '',
'remark': fields[23] if len(fields[23]) > 0 else '',
'tags': fields[24] if len(fields[24]) > 0 else '',
'hidden_location': fields[25] if len(fields[25]) > 0 else False,
'auto_checkin': fields[26] if len(fields[26]) > 0 else False,
'checkin_radius': fields[27] if len(fields[27]) > 0 else 15,
'checkin_point': fields[28] if len(fields[28]) > 0 else 10,
'buy_point': fields[29] if len(fields[29]) > 0 else 0,
'evaluation_value': fields[30] if len(fields[30]) > 0 else '',
'shop_closed': fields[31] if len(fields[31]) > 0 else False,
'shop_shutdown': fields[32] if len(fields[32]) > 0 else False,
'opening_hours_mon': fields[33] if len(fields[33]) > 0 else '',
'opening_hours_tue': fields[34] if len(fields[34]) > 0 else '',
'opening_hours_wed': fields[35] if len(fields[35]) > 0 else '',
'opening_hours_thu': fields[36] if len(fields[36]) > 0 else '',
'opening_hours_fri': fields[37] if len(fields[37]) > 0 else '',
'opening_hours_sat': fields[38] if len(fields[38]) > 0 else '',
'opening_hours_sun': fields[39] if len(fields[39]) > 0 else ''
}
if geom:
defaults['geom'] = geom
obj, created = mdl.objects.update_or_create(
location_id=int(fields[0]),
defaults=defaults
)
if created:
logger.info(f"New location created with id: {obj.location_id}")
else:
logger.info(f"Location updated with id: {obj.location_id}")
except Exception as e:
logger.error(f"Error updating or creating location: {e}", exc_info=True)
2022-06-21 16:32:11 +05:30
2022-04-07 14:53:11 +05:30
def updateLocation(mdl, fields):
print(f"Updating {fields[0]} - {fields[1]}")
print(mdl.objects.filter(location_id = int(fields[0])))
2022-09-27 17:52:14 +05:30
print(f"--- ${fields} ----")
try:
with transaction.atomic():
updated = mdl.objects.filter(location_id = int(fields[0])).update(
sub_loc_id = fields[1] if len(fields[1]) > 0 else '',
cp = fields[2] if len(fields[2]) > 0 else 0,
location_name = fields[3] if len(fields[3]) > 0 else '',
category = fields[4] if len(fields[4]) > 0 else '',
subcategory = fields[5] if len(fields[5]) > 0 else '',
zip = fields[6] if len(fields[6]) > 0 else '',
address = fields[7] if len(fields[7]) > 0 else '',
prefecture = fields[8] if len(fields[8]) > 0 else '',
area = fields[9] if len(fields[9]) > 0 else '',
city = fields[10] if len(fields[10]) > 0 else '',
latitude = fields[11] if len(fields[11]) > 0 else '',
longitude = fields[12] if len(fields[12]) > 0 else '',
photos = fields[13] if len(fields[13]) > 0 else '',
videos = fields[14] if len(fields[14]) > 0 else '',
webcontents = fields[15] if len(fields[15]) > 0 else '',
status = fields[16] if len(fields[16]) > 0 else '',
portal = fields[17] if len(fields[17]) > 0 else '',
group = fields[18] if len(fields[18]) > 0 else '',
phone = fields[19] if len(fields[19]) > 0 else '',
fax = fields[20] if len(fields[20]) > 0 else '',
email = fields[21] if len(fields[21]) > 0 else '',
facility = fields[22] if len(fields[22]) > 0 else '',
remark = fields[23] if len(fields[23]) > 0 else '',
tags = fields[24] if len(fields[24]) > 0 else '',
hidden_location = fields[25] if len(fields[25]) > 0 else False,
auto_checkin = fields[26] if len(fields[26]) > 0 else False,
checkin_radius = fields[27] if len(fields[27]) > 0 else 15,
checkin_point = fields[28] if len(fields[28]) > 0 else 10,
buy_point = fields[29] if len(fields[29]) > 0 else 0,
evaluation_value = fields[30] if len(fields[30]) > 0 else '',
shop_closed = fields[31] if len(fields[31]) > 0 else False,
shop_shutdown = fields[32] if len(fields[32]) > 0 else False,
opening_hours_mon = fields[33] if len(fields[33]) > 0 else '',
opening_hours_tue = fields[34] if len(fields[34]) > 0 else '',
opening_hours_wed = fields[35] if len(fields[35]) > 0 else '',
opening_hours_thu = fields[36] if len(fields[36]) > 0 else '',
opening_hours_fri = fields[37] if len(fields[37]) > 0 else '',
opening_hours_sat = fields[38] if len(fields[38]) > 0 else '',
opening_hours_sun = fields[39] if len(fields[39]) > 0 else ''
)
logger.info(f"TempLocation updated successfully. Rows affected: {updated}")
except Exception as e:
logger.error(f"Error updating TempLocation: {e}", exc_info=True)
2022-03-14 10:50:11 +05:30
def updateLineTable(mdl, fields):
2022-03-23 09:55:16 +05:30
print(f"Updating {fields[0]} - {fields[1]}")
2022-03-19 18:42:55 +05:30
print(mdl.objects.filter(location_id = int(fields[0])))
2022-03-23 09:55:16 +05:30
print("-------")
2022-04-27 15:47:37 +05:30
with transaction.atomic():
mdl.objects.filter(location_id = int(fields[0])).update(
location_name= fields[1] if len(fields) > 1 else '',
category=fields[2] if len(fields) > 2 else '',
zip=fields[3] if len(fields) > 3 else '',
address=fields[4] if len(fields) > 4 else '',
prefecture=fields[5] if len(fields) > 5 else '',
area=fields[6] if len(fields) > 6 else '',
city=fields[7] if len(fields) > 7 else '',
photos=fields[8] if len(fields) > 8 else '',
videos=fields[9] if len(fields) > 9 else '',
webcontents=fields[10] if len(fields) > 10 else '',
status=fields[11] if len(fields) > 11 else '',
portal=fields[12] if len(fields) > 12 else '',
group=fields[13] if len(fields) > 13 else '',
phone=fields[14] if len(fields) > 14 else '',
fax=fields[15] if len(fields) > 15 else '',
email=fields[16] if len(fields) > 16 else '',
facility=fields[17] if len(fields) > 17 else '',
remark=fields[18] if len(fields) > 18 else '',
tags=fields[19] if len(fields) > 19 else '',
parammeters=fields[20] if len(fields) > 20 else ''
)
2022-03-14 10:50:11 +05:30
def updatePolygonTable(mdl, fields):
#print(f"Updated {fields[0]} - {fields[1]}")
2022-04-27 15:47:37 +05:30
with transaction.atomic():
mdl.objects.filter(location_id = fields[0]).update(
location_name= fields[1] if len(fields) > 1 else '',
category=fields[2] if len(fields) > 2 else '',
zip=fields[3] if len(fields) > 3 else '',
address=fields[4] if len(fields) > 4 else '',
prefecture=fields[5] if len(fields) > 5 else '',
area=fields[6] if len(fields) > 6 else '',
city=fields[7] if len(fields) > 7 else '',
photos=fields[8] if len(fields) > 8 else '',
videos=fields[9] if len(fields) > 9 else '',
webcontents=fields[10] if len(fields) > 10 else '',
status=fields[11] if len(fields) > 11 else '',
portal=fields[12] if len(fields) > 12 else '',
group=fields[13] if len(fields) > 13 else '',
phone=fields[14] if len(fields) > 14 else '',
fax=fields[15] if len(fields) > 15 else '',
email=fields[16] if len(fields) > 16 else '',
facility=fields[17] if len(fields) > 17 else '',
remark=fields[18] if len(fields) > 18 else '',
tags=fields[19] if len(fields) > 19 else '',
parammeters=fields[20] if len(fields) > 20 else ''
)
2022-08-30 18:20:15 +05:30
def createUser(fields):
with transaction.atomic():
2024-07-24 00:38:32 +00:00
other_fields.setdefault('event_code',fields[1])
other_fields.setdefault('team_name',fields[1])
other_fields.setdefault('group',fields[1])
user = CustomUser.objects.create_user(email=fields[0], password=fields[4], **other_fields)
2022-08-30 18:20:15 +05:30
user.is_superuser = False
user.is_staff = False
user.save()
@receiver(post_delete,sender=UserUpload)
def deleteUserUploadUser(sender,instance,*args,**kwargs):
pass
emails = UserUploadUser.objects.filter(userfile=instance.name).values_list('email')
print("------- email----")
print(emails)
print("------- name----")
CustomUser.objects.filter(email__in=emails).delete()
templocation.objects.all().delete()
UserUploadUser.objects.filter(userfile=instance.name).delete();
@receiver(post_save, sender=UserUpload)
def publish_data(sender, instance, created, **kwargs):
logger.info(f"Processing ShapeLayer: {instance.name}")
2022-08-30 18:20:15 +05:30
file = instance.file.path
#os.remove(file)
try:
#csv_f = glob.glob(r'{}/**/*.csv'.format(file), recursive=True)[0]
remove_bom_inplace(file)
print(file)
with open(file, mode="r", encoding="utf-8") as txt_file:
#heading = next(txt_file)
reader = csv.reader(txt_file, delimiter=",")
for fields in reader:
print("@@@@@@@@@@@@")
2023-05-15 16:32:01 +05:30
# print(fields[0])
# print(fields[1])
# print(fields[2])
2022-08-30 18:20:15 +05:30
print("@@@@@@@@@@@@")
createUser(fields)
with open(file, mode="r", encoding="utf-8") as txt_file:
reader_2 = csv.reader(txt_file, delimiter=",")
for fields in reader_2:
insertUserUploadUser(instance.name, fields)
except Exception as e:
print('######## user csv file ##########',e)
2025-08-20 19:15:19 +09:00
# for upper compatible
# 既存のモデルに追加=> 通過記録に相応しい名称に変更すべき
def get_default_entry():
"""
デフォルトのEntryを取得または作成する
"""
try:
# NewEvent2のデフォルトイベントを取得
default_event = NewEvent2.objects.first()
if default_event:
# デフォルトチームを取得
default_team = Team.objects.first()
if default_team:
# 既存のEntryを取得
entry = Entry.objects.filter(
teams=default_team,
event=default_event
).first()
if entry:
return entry.id
# 新しいEntryを作成
from django.contrib.auth import get_user_model
User = get_user_model()
default_user = User.objects.first()
if default_user:
entry = Entry.objects.create(
event=default_event,
main_user=default_user
)
entry.teams.add(default_team)
return entry.id
except:
pass
return None
2025-08-20 19:15:19 +09:00
class GpsLog(models.Model):
"""
GPSチェックイン情報を管理するモデル
gps_informationテーブルに対応
"""
serial_number = models.IntegerField(null=False)
# Entry へ移行
zekken_number = models.TextField(null=False, default='')
event_code = models.TextField(null=False, default='')
2025-08-20 19:15:19 +09:00
cp_number = models.TextField(null=True, blank=True)
image_address = models.TextField(null=True, blank=True)
# 新規追加
checkin_time = models.DateTimeField(auto_now_add=True)
goal_time = models.TextField(null=True, blank=True)
late_point = models.IntegerField(null=True, blank=True)
create_at = models.DateTimeField(null=True, blank=True)
create_user = models.TextField(null=True, blank=True)
update_at = models.DateTimeField(null=True, blank=True)
update_user = models.TextField(null=True, blank=True)
buy_flag = models.BooleanField(null=True, blank=True)
minus_photo_flag = models.BooleanField(null=True, blank=True)
colabo_company_memo = models.TextField(null=False, default='')
# 新規追加
is_service_checked = models.BooleanField(default=False)
# ゴール記録用に追加
score = models.IntegerField(default=0, null=True, blank=True)
scoreboard_url = models.URLField(blank=True, null=True)
2025-09-06 00:11:57 +09:00
# 外部システム登録管理用フィールド
external_registration_status = models.CharField(
max_length=20,
choices=[
('pending', 'Pending'),
('success', 'Success'),
('failed', 'Failed'),
('retry', 'Retry Required')
],
default='pending',
help_text="外部システム登録状況"
)
external_registration_attempts = models.IntegerField(default=0, help_text="外部システム登録試行回数")
external_registration_error = models.TextField(null=True, blank=True, help_text="外部システム登録エラー詳細")
last_external_registration_attempt = models.DateTimeField(null=True, blank=True, help_text="最後の外部システム登録試行時刻")
# エントリー関連情報(外部システム登録用)
team_name = models.CharField(max_length=255, null=True, blank=True, help_text="チーム名(外部システム登録用)")
category_name = models.CharField(max_length=255, null=True, blank=True, help_text="カテゴリ名(外部システム登録用)")
user_password_hash = models.TextField(null=True, blank=True, help_text="ユーザーパスワードハッシュ(外部システム登録用)")
2025-08-20 19:15:19 +09:00
class Meta:
db_table = 'gps_information'
# 複合主キーの設定
unique_together = [['serial_number', 'zekken_number', 'event_code', 'colabo_company_memo']]
# インデックスの設定(必要に応じて)
indexes = [
models.Index(fields=['zekken_number', 'event_code'], name='gpslog_zekken_event_idx'),
models.Index(fields=['create_at'], name='gpslog_create_at_idx'),
]
def __str__(self):
return f"{self.event_code}-{self.zekken_number}-{self.serial_number}"
@classmethod
def record_start(cls, entry):
"""
チームのスタート情報を記録する
以前はTeamStartモデルが担当していた機能
"""
return cls.objects.create(
serial_number=0, # スタートログを表す特別な値
zekken_number=entry.zekken_number,
event_code=entry.event.event_name,
cp_number="START",
create_at=timezone.now(),
update_at=timezone.now(),
buy_flag=False,
colabo_company_memo=""
)
@classmethod
def record_goal(cls, entry, goal_time=None, image_url=None, score=0, scoreboard_url=None):
"""
チームのゴール情報を記録する
以前はTeamGoalモデルが担当していた機能
"""
if goal_time is None:
goal_time = timezone.now()
return cls.objects.create(
serial_number=9999, # ゴールログを表す特別な値
entry=entry,
zekken_number=entry.zekken_number,
event_code=entry.event.event_name,
cp_number="GOAL",
image_address=image_url,
create_at=goal_time,
update_at=timezone.now(),
goal_time=goal_time.strftime('%Y-%m-%d %H:%M:%S'),
buy_flag=False,
score=score,
scoreboard_url=scoreboard_url,
colabo_company_memo=""
)
def is_start_record(self):
"""このレコードがスタート記録かどうかを判定"""
return self.cp_number == "START" and self.serial_number == 0
def is_goal_record(self):
"""このレコードがゴール記録かどうかを判定"""
return self.cp_number == "GOAL" and self.serial_number == 9999
@property
def start_time(self):
"""スタート時刻を返すTeamStartとの互換性のため"""
return self.create_at or self.checkin_time
@property
def goal_datetime(self):
"""ゴール時刻をDateTimeとして返すTeamGoalとの互換性のため"""
if self.is_goal_record() and self.create_at:
return self.create_at
return None
2025-09-06 00:11:57 +09:00
@classmethod
def record_external_registration_request(cls, entry):
"""
外部システム登録要求を記録する
Entry作成時に外部システム登録が失敗した場合の情報を保存
"""
return cls.objects.create(
serial_number=-1, # 外部システム登録要求を表す特別な値
zekken_number=str(entry.zekken_number),
event_code=entry.event.event_name,
cp_number="EXTERNAL_REG",
team_name=entry.team.team_name,
category_name=entry.category.category_name,
user_password_hash=entry.team.owner.password,
external_registration_status='pending',
external_registration_attempts=0,
create_at=timezone.now(),
update_at=timezone.now(),
buy_flag=False,
colabo_company_memo="Entry registration - external system pending"
)
def update_external_registration_status(self, status, error_message=None):
"""
外部システム登録状況を更新する
"""
self.external_registration_status = status
self.external_registration_attempts += 1
self.last_external_registration_attempt = timezone.now()
if error_message:
self.external_registration_error = error_message
self.update_at = timezone.now()
self.save()
def retry_external_registration(self):
"""
外部システム登録をリトライする
"""
import requests
api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
data = {
"zekken_number": self.zekken_number,
"event_code": self.event_code,
"team_name": self.team_name,
"class_name": self.category_name,
"password": self.user_password_hash
}
try:
response = requests.post(api_url, headers=headers, data=data, timeout=30)
response.raise_for_status()
self.update_external_registration_status('success')
return True
except requests.RequestException as e:
self.update_external_registration_status('failed', str(e))
return False
@classmethod
def get_pending_external_registrations(cls):
"""
外部システム登録が保留中の記録を取得する
"""
return cls.objects.filter(
serial_number=-1,
cp_number="EXTERNAL_REG",
external_registration_status__in=['pending', 'retry']
).order_by('create_at')
@classmethod
def retry_failed_external_registrations(cls, max_attempts=3):
"""
失敗した外部システム登録を一括でリトライする
"""
pending_records = cls.get_pending_external_registrations().filter(
external_registration_attempts__lt=max_attempts
)
success_count = 0
failed_count = 0
for record in pending_records:
if record.retry_external_registration():
success_count += 1
else:
failed_count += 1
return {
'success_count': success_count,
'failed_count': failed_count,
'total_processed': success_count + failed_count
}
2025-08-20 19:15:19 +09:00
class Waypoint(models.Model):
entry = models.ForeignKey('Entry', on_delete=models.CASCADE, related_name='waypoints')
latitude = models.FloatField()
longitude = models.FloatField()
altitude = models.FloatField(null=True, blank=True)
accuracy = models.FloatField(null=True, blank=True)
speed = models.FloatField(null=True, blank=True)
recorded_at = models.DateTimeField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['recorded_at']
indexes = [
models.Index(fields=['entry', 'recorded_at']),
]
def __str__(self):
return f"{self.entry.team_name} - {self.recorded_at.strftime('%Y-%m-%d %H:%M:%S')}"