class RecordModel(db.Document): # 系統紀錄 created_by = db.ReferenceField('sttapp.users.models.SttUser') created_at = db.DateTimeField(default=datetime.datetime.utcnow) updated_by = db.ObjectIdField() updated_at = db.DateTimeField() meta = { 'abstract': True, } @staticmethod def _dt_to_str(dt): tz_dt = get_local_dt(dt) return tz_dt.strftime("%Y/%m/%d %H:%M") @staticmethod def _d_to_str(dt): # 不可進行時區轉換 return dt.strftime("%Y/%m/%d") @property def created_at_str(self): return self._dt_to_str(self.created_at) @property def updated_at_str(self): return self._dt_to_str(self.updated_at)
class MyHistory(RecordModel): order = db.IntField() title = db.StringField() start_date = db.DateTimeField() end_date = db.DateTimeField() days = db.IntField() event_type = db.StringField() link = db.URLField() user_id = db.ObjectIdField() meta = {'ordering': ['order'], 'indexes': ['user_id']} @property def start_date_str(self): if self.start_date: return super()._d_to_str(self.start_date) return "" @property def end_date_str(self): if self.end_date: return super()._d_to_str(self.end_date) return "" @property def difficulty(self): if self.days <= 3: return Difficulty.LEVEL_D if self.days <= 5: return Difficulty.LEVEL_C if self.days <= 8: return Difficulty.LEVEL_B else: return Difficulty.LEVEL_A def serialize(self): obj = { 'id': str(self.id), 'order': self.order, 'date_str': "{}~{}".format(self.start_date_str, self.end_date_str), 'title': self.title or "", 'event_type': self.event_type or "", 'days': self.days or "", 'difficulty': self.difficulty or "", 'link': ''' <a type="button" class="btn btn-default btn-round-full" href="{}" target="_blank"><i class="tf-attachment"></i></a> '''.format(self.link) if self.link else "", } return obj
class SttUser(User): # 基本資料 name = db.StringField() # 真實姓名 password_hash = db.StringField() birthday = db.DateTimeField() cellphone_number = db.StringField() introduction = db.StringField() # 學校資料 department = db.StringField() # 系所,例如:物理、水利 graduation_year = db.IntField() # 畢業年,例如:102 # 社團相關資料 group = db.StringField(choices=Group.get_choices()) # 嚮導隊 position = db.StringField(choices=Position.get_choices()) # 工作組,總務、教學 level = db.StringField(choices=Level.get_choices()) # 新生、隊員、幹部等 member_id = db.ObjectIdField() identity = db.StringField(choices=Identity.get_choices()) # 在校狀態 # 系統紀錄 updated_at = db.DateTimeField() invitation_info = db.EmbeddedDocumentField(InvitationInfo) meta = { 'ordering': ['-created_at'], 'indexes': [ 'name', ] } @property def birthday_str(self): if self.birthday: return self.birthday.strftime("%Y/%m/%d") return "" @property def password(self): raise AttributeError('password is not a readable attribute') @password.setter def password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password)
class Member(RecordModel): # 基本資料 name = db.StringField() nickname = db.StringField() security_number = db.StringField(unique=True, sparse=True) birthday = db.DateTimeField() cellphone_number = db.StringField() # 進階資料 drug_allergy = db.StringField(default="NKDA") blood_type = db.StringField() level = db.StringField(choices=CHOICES["level"]) # 新生、隊員、幹部等 group = db.StringField(choices=CHOICES["group"]) # 學校資訊 student_id = db.StringField() department_and_grade = db.StringField() # ex: 水利四 / ob / 物理所 / 校外 # 最高資歷 highest_difficulty = db.StringField(choices=CHOICES["difficulty"]) # 級數 highest_difficulty_experience = db.StringField() # 手動輸入出隊資歷,ex: 哈崙鐵道 # 緊急聯絡人 emergency_contact = db.StringField() emergency_contact_phone = db.StringField() emergency_contact_relationship = db.StringField() # ex: 父子、母子 user_id = db.ObjectIdField() event_ids = db.ListField(db.ObjectIdField(), default=list) meta = { 'indexes': ['security_number', 'cellphone_number', ] } @property def display_name(self): return self.nickname or self.name @property def selected_name(self): return "{}|{}".format(self.name, self.security_number) @property def gender(self): if self.security_number[1] == "1": return Gender.MALE elif self.security_number[1] == "2": return Gender.FEMALE else: return "" @property def birthday_str(self): return self.birthday.strftime("%Y/%m/%d") @property def is_adult(self): today = get_local_dt(datetime.datetime.utcnow()).date() return today.replace(year=today.year-20) >= self.birthday.date() @property def attendee_display(self): return "{}({}{})".format( self.display_name, self.get_level_display()[0] if self.level else "", "、{}".format(self.get_group_display()[-1]) if self.group else "" )
class Proposal(RecordModel): title = db.StringField() start_date = db.DateTimeField() end_date = db.DateTimeField() has_d0 = db.BooleanField(default=False) event_type = db.StringField(choices=EventType.get_choices()) days = db.IntField(default=1) leader = db.ReferenceField('sttapp.members.models.Member', reverse_delete_rule=mongoengine.DENY) guide = db.ReferenceField('sttapp.members.models.Member', reverse_delete_rule=mongoengine.DENY) itinerary_list = db.EmbeddedDocumentListField(Itinerary) supporter = db.StringField() return_plan = db.StringField() buffer_days = db.IntField(default=1) approach_way = db.StringField() radio = db.StringField() satellite_telephone = db.StringField() attendees = db.ListField( db.ReferenceField('sttapp.members.models.Member', reverse_delete_rule=mongoengine.DENY)) open_time = db.StringField() event_id = db.ObjectIdField() meta = { 'ordering': ['-created_at'], 'indexes': ['title', 'created_at'] } @property def start_date_str(self): return self.start_date.strftime("%Y/%m/%d") @property def end_date_str(self): return self.end_date.strftime("%Y/%m/%d") @property def gathering_time_str(self): return self._dt_to_str(self.gathering_time) @property def total_number(self): return len(self.attendees) @property def gender_structure(self): gender_dict = {display: 0 for field, display in Gender.get_choices(False)} for a in self.attendees: if not a.gender: continue gender_dict[a.gender] += 1 return gender_dict @property def level_structure(self): level_dict = {field: 0 for field, display in Level.get_choices()} for a in self.attendees: if not getattr(a, 'level'): continue level_dict[getattr(a, 'level')] += 1 return level_dict @property def difficulty(self): if self.days <= 3: return Difficulty.LEVEL_D if self.days <= 5: return Difficulty.LEVEL_C if self.days <= 8: return Difficulty.LEVEL_B else: return Difficulty.LEVEL_A @property def attendees_display(self): if not self.attendees: return "" return ", ".join(att.selected_name for att in self.attendees) + ", " @property def leader_display(self): if not self.leader: return "" return self.leader.selected_name @property def guide_display(self): if not self.guide: return "" return self.guide.selected_name def validate_for_publishing(self): required_fields = { "title": "隊伍名稱", "start_date": "上山日期", "days": "天數", "leader": "領隊", "guide": "嚮導", "supporter": "留守", "buffer_days": "預備天數", "attendees": "成員", "return_plan": "撤退計畫", "approach_way": "交通方式", "event_type": "隊伍類型", } failed_fields = [] for field, name in required_fields.items(): if not getattr(self, field): failed_fields.append(name) failed_itinerary = [] for itinerary in self.itinerary_list: if not itinerary.content: failed_itinerary.append("D{}".format(itinerary.day_number)) return failed_fields, failed_itinerary
class InvitationInfo(db.EmbeddedDocument): invited_by = db.ObjectIdField() invited_at = db.DateTimeField() email = db.EmailField() token = db.StringField()