Coverage for family/models.py: 99%

215 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-05 19:26 +0800

1from django.db import models 

2from django.contrib.auth.models import User 

3from django.urls import reverse 

4from pgvector.django import VectorField 

5 

6 

7class Person(models.Model): 

8 """Family members and important individuals""" 

9 GENDER_CHOICES = [ 

10 ('M', 'Male'), 

11 ('F', 'Female'), 

12 ('O', 'Other'), 

13 ] 

14 

15 name = models.CharField(max_length=100) 

16 birth_date = models.DateField(null=True, blank=True) 

17 death_date = models.DateField(null=True, blank=True) 

18 gender = models.CharField(max_length=1, choices=GENDER_CHOICES, blank=True) 

19 bio = models.TextField(blank=True) 

20 photo = models.ImageField(upload_to='people/', blank=True) 

21 email = models.EmailField(blank=True) 

22 phone = models.CharField(max_length=20, blank=True) 

23 

24 created_at = models.DateTimeField(auto_now_add=True) 

25 updated_at = models.DateTimeField(auto_now=True) 

26 

27 class Meta: 

28 ordering = ['name'] 

29 

30 def __str__(self): 

31 return self.name 

32 

33 

34class Location(models.Model): 

35 """Geographic information and places""" 

36 LOCATION_TYPES = [ 

37 ('home', 'Home'), 

38 ('work', 'Work'), 

39 ('school', 'School'), 

40 ('hospital', 'Hospital'), 

41 ('travel', 'Travel'), 

42 ('other', 'Other'), 

43 ] 

44 

45 name = models.CharField(max_length=200) 

46 address = models.TextField(blank=True) 

47 latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) 

48 longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) 

49 location_type = models.CharField(max_length=20, choices=LOCATION_TYPES, default='other') 

50 description = models.TextField(blank=True) 

51 

52 created_at = models.DateTimeField(auto_now_add=True) 

53 

54 def __str__(self): 

55 return self.name 

56 

57 

58class Institution(models.Model): 

59 """External organizations""" 

60 INSTITUTION_TYPES = [ 

61 ('hospital', 'Hospital'), 

62 ('school', 'School'), 

63 ('company', 'Company'), 

64 ('government', 'Government'), 

65 ('religious', 'Religious'), 

66 ('restaurant', 'Restaurant'), 

67 ('entertainment', 'Entertainment'), 

68 ('other', 'Other'), 

69 ] 

70 

71 name = models.CharField(max_length=200) 

72 institution_type = models.CharField(max_length=20, choices=INSTITUTION_TYPES) 

73 website = models.URLField(blank=True) 

74 phone = models.CharField(max_length=20, blank=True) 

75 email = models.EmailField(blank=True) 

76 address = models.TextField(blank=True) 

77 established_date = models.DateField(null=True, blank=True) 

78 description = models.TextField(blank=True) 

79 

80 created_at = models.DateTimeField(auto_now_add=True) 

81 

82 def __str__(self): 

83 return self.name 

84 

85 

86class Event(models.Model): 

87 """Important milestones and activities""" 

88 EVENT_TYPES = [ 

89 ('birthday', 'Birthday'), 

90 ('wedding', 'Wedding'), 

91 ('graduation', 'Graduation'), 

92 ('funeral', 'Funeral'), 

93 ('reunion', 'Family Reunion'), 

94 ('holiday', 'Holiday'), 

95 ('milestone', 'Milestone'), 

96 ('other', 'Other'), 

97 ] 

98 

99 name = models.CharField(max_length=200) 

100 description = models.TextField(blank=True) 

101 event_type = models.CharField(max_length=20, choices=EVENT_TYPES) 

102 start_date = models.DateTimeField() 

103 end_date = models.DateTimeField(null=True, blank=True) 

104 location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True, blank=True) 

105 institution = models.ForeignKey(Institution, on_delete=models.SET_NULL, null=True, blank=True) 

106 

107 # Many-to-many relationships 

108 participants = models.ManyToManyField(Person, related_name='events', blank=True) 

109 

110 created_at = models.DateTimeField(auto_now_add=True) 

111 updated_at = models.DateTimeField(auto_now=True) 

112 

113 # AI Integration fields 

114 content_embedding = VectorField(dimensions=1536, null=True, blank=True) 

115 embedding_updated = models.DateTimeField(null=True, blank=True) 

116 

117 class Meta: 

118 ordering = ['-start_date'] 

119 

120 def __str__(self): 

121 return f"{self.name} ({self.start_date.year})" 

122 

123 

124class Story(models.Model): 

125 """Family memories, anecdotes, and experiences""" 

126 STORY_TYPES = [ 

127 ('memory', 'Memory'), 

128 ('legend', 'Family Legend'), 

129 ('experience', 'Life Experience'), 

130 ('wisdom', 'Wisdom'), 

131 ('tradition', 'Tradition'), 

132 ('other', 'Other'), 

133 ] 

134 

135 title = models.CharField(max_length=200) 

136 content = models.TextField() 

137 story_type = models.CharField(max_length=20, choices=STORY_TYPES, default='memory') 

138 date_occurred = models.DateField(null=True, blank=True) 

139 location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True, blank=True) 

140 

141 # Many-to-many relationships 

142 people = models.ManyToManyField(Person, related_name='stories', blank=True) 

143 events = models.ManyToManyField(Event, related_name='stories', blank=True) 

144 

145 created_at = models.DateTimeField(auto_now_add=True) 

146 updated_at = models.DateTimeField(auto_now=True) 

147 

148 # AI Integration fields 

149 content_embedding = VectorField(dimensions=1536, null=True, blank=True) 

150 embedding_updated = models.DateTimeField(null=True, blank=True) 

151 

152 class Meta: 

153 ordering = ['-created_at'] 

154 verbose_name_plural = "Stories" 

155 

156 def __str__(self): 

157 return self.title 

158 

159 

160class Multimedia(models.Model): 

161 """Photos, videos, documents, audio files""" 

162 MEDIA_TYPES = [ 

163 ('photo', 'Photo'), 

164 ('video', 'Video'), 

165 ('audio', 'Audio'), 

166 ('document', 'Document'), 

167 ('other', 'Other'), 

168 ] 

169 

170 title = models.CharField(max_length=200) 

171 description = models.TextField(blank=True) 

172 media_type = models.CharField(max_length=20, choices=MEDIA_TYPES) 

173 file = models.FileField(upload_to='media/') 

174 file_size = models.PositiveIntegerField(null=True, blank=True) 

175 created_date = models.DateTimeField(null=True, blank=True) 

176 location = models.ForeignKey(Location, on_delete=models.SET_NULL, null=True, blank=True) 

177 

178 # Many-to-many relationships 

179 people = models.ManyToManyField(Person, related_name='media', blank=True) 

180 events = models.ManyToManyField(Event, related_name='media', blank=True) 

181 stories = models.ManyToManyField(Story, related_name='media', blank=True) 

182 

183 created_at = models.DateTimeField(auto_now_add=True) 

184 updated_at = models.DateTimeField(auto_now=True) 

185 

186 class Meta: 

187 ordering = ['-created_at'] 

188 verbose_name_plural = "Multimedia" 

189 

190 def __str__(self): 

191 return self.title 

192 

193 

194class Relationship(models.Model): 

195 """Network of relationships between people""" 

196 RELATIONSHIP_TYPES = [ 

197 ('parent', 'Parent'), 

198 ('child', 'Child'), 

199 ('spouse', 'Spouse'), 

200 ('sibling', 'Sibling'), 

201 ('grandparent', 'Grandparent'), 

202 ('grandchild', 'Grandchild'), 

203 ('friend', 'Friend'), 

204 ('colleague', 'Colleague'), 

205 ('other', 'Other'), 

206 ] 

207 

208 person_from = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='relationships_from') 

209 person_to = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='relationships_to') 

210 relationship_type = models.CharField(max_length=20, choices=RELATIONSHIP_TYPES) 

211 start_date = models.DateField(null=True, blank=True) 

212 end_date = models.DateField(null=True, blank=True) 

213 description = models.TextField(blank=True) 

214 

215 created_at = models.DateTimeField(auto_now_add=True) 

216 

217 class Meta: 

218 unique_together = ['person_from', 'person_to', 'relationship_type'] 

219 

220 def __str__(self): 

221 return f"{self.person_from} -> {self.person_to} ({self.relationship_type})" 

222 

223 

224class Health(models.Model): 

225 """Personal and family health records""" 

226 RECORD_TYPES = [ 

227 ('checkup', 'Medical Checkup'), 

228 ('illness', 'Illness'), 

229 ('medication', 'Medication'), 

230 ('surgery', 'Surgery'), 

231 ('allergy', 'Allergy'), 

232 ('genetic', 'Genetic Condition'), 

233 ('other', 'Other'), 

234 ] 

235 

236 person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='health_records') 

237 record_type = models.CharField(max_length=20, choices=RECORD_TYPES) 

238 title = models.CharField(max_length=200) 

239 description = models.TextField() 

240 date = models.DateField() 

241 doctor = models.CharField(max_length=100, blank=True) 

242 institution = models.ForeignKey(Institution, on_delete=models.SET_NULL, null=True, blank=True) 

243 is_hereditary = models.BooleanField(default=False) 

244 

245 created_at = models.DateTimeField(auto_now_add=True) 

246 

247 # AI Integration fields 

248 content_embedding = VectorField(dimensions=1536, null=True, blank=True) 

249 embedding_updated = models.DateTimeField(null=True, blank=True) 

250 

251 class Meta: 

252 ordering = ['-date'] 

253 

254 def __str__(self): 

255 return f"{self.person.name} - {self.title}" 

256 

257 

258class Heritage(models.Model): 

259 """Family values, traditions, and wisdom""" 

260 HERITAGE_TYPES = [ 

261 ('values', 'Family Values'), 

262 ('tradition', 'Tradition'), 

263 ('wisdom', 'Wisdom'), 

264 ('skill', 'Skill'), 

265 ('recipe', 'Recipe'), 

266 ('other', 'Other'), 

267 ] 

268 

269 IMPORTANCE_LEVELS = [ 

270 (1, 'Low'), 

271 (2, 'Medium'), 

272 (3, 'High'), 

273 (4, 'Critical'), 

274 ] 

275 

276 title = models.CharField(max_length=200) 

277 heritage_type = models.CharField(max_length=20, choices=HERITAGE_TYPES) 

278 description = models.TextField() 

279 origin_person = models.ForeignKey(Person, on_delete=models.SET_NULL, null=True, related_name='heritage_originated') 

280 inheritors = models.ManyToManyField(Person, related_name='heritage_inherited', blank=True) 

281 importance = models.IntegerField(choices=IMPORTANCE_LEVELS, default=2) 

282 

283 # Relationships 

284 stories = models.ManyToManyField(Story, related_name='heritage', blank=True) 

285 events = models.ManyToManyField(Event, related_name='heritage', blank=True) 

286 

287 created_at = models.DateTimeField(auto_now_add=True) 

288 

289 # AI Integration fields 

290 content_embedding = VectorField(dimensions=1536, null=True, blank=True) 

291 embedding_updated = models.DateTimeField(null=True, blank=True) 

292 

293 def __str__(self): 

294 return self.title 

295 

296 

297class Planning(models.Model): 

298 """Future goals and family vision""" 

299 TIME_RANGES = [ 

300 ('short', 'Short-term (< 1 year)'), 

301 ('medium', 'Medium-term (1-5 years)'), 

302 ('long', 'Long-term (> 5 years)'), 

303 ] 

304 

305 PRIORITY_LEVELS = [ 

306 (1, 'Low'), 

307 (2, 'Medium'), 

308 (3, 'High'), 

309 (4, 'Critical'), 

310 ] 

311 

312 STATUS_CHOICES = [ 

313 ('planned', 'Planned'), 

314 ('in_progress', 'In Progress'), 

315 ('completed', 'Completed'), 

316 ('cancelled', 'Cancelled'), 

317 ] 

318 

319 title = models.CharField(max_length=200) 

320 description = models.TextField() 

321 time_range = models.CharField(max_length=10, choices=TIME_RANGES) 

322 priority = models.IntegerField(choices=PRIORITY_LEVELS, default=2) 

323 status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planned') 

324 target_date = models.DateField(null=True, blank=True) 

325 expected_outcome = models.TextField(blank=True) 

326 

327 # Relationships 

328 involved_people = models.ManyToManyField(Person, related_name='plans', blank=True) 

329 

330 created_at = models.DateTimeField(auto_now_add=True) 

331 updated_at = models.DateTimeField(auto_now=True) 

332 

333 def __str__(self): 

334 return self.title 

335 

336 

337class Career(models.Model): 

338 """Work and education history""" 

339 CAREER_TYPES = [ 

340 ('education', 'Education'), 

341 ('work', 'Work'), 

342 ('volunteer', 'Volunteer'), 

343 ('internship', 'Internship'), 

344 ('other', 'Other'), 

345 ] 

346 

347 person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='career_history') 

348 career_type = models.CharField(max_length=20, choices=CAREER_TYPES) 

349 title = models.CharField(max_length=200) 

350 institution = models.ForeignKey(Institution, on_delete=models.SET_NULL, null=True, blank=True) 

351 start_date = models.DateField() 

352 end_date = models.DateField(null=True, blank=True) 

353 description = models.TextField(blank=True) 

354 achievements = models.TextField(blank=True) 

355 salary_range = models.CharField(max_length=50, blank=True) 

356 

357 # Relationships 

358 events = models.ManyToManyField(Event, related_name='careers', blank=True) 

359 

360 created_at = models.DateTimeField(auto_now_add=True) 

361 

362 class Meta: 

363 ordering = ['-start_date'] 

364 

365 def __str__(self): 

366 return f"{self.person.name} - {self.title}" 

367 

368 

369class Assets(models.Model): 

370 """Important property and documents""" 

371 ASSET_TYPES = [ 

372 ('property', 'Real Estate'), 

373 ('vehicle', 'Vehicle'), 

374 ('jewelry', 'Jewelry'), 

375 ('insurance', 'Insurance'), 

376 ('investment', 'Investment'), 

377 ('document', 'Important Document'), 

378 ('other', 'Other'), 

379 ] 

380 

381 LEGAL_STATUS = [ 

382 ('owned', 'Owned'), 

383 ('leased', 'Leased'), 

384 ('shared', 'Shared Ownership'), 

385 ('trust', 'In Trust'), 

386 ('other', 'Other'), 

387 ] 

388 

389 name = models.CharField(max_length=200) 

390 asset_type = models.CharField(max_length=20, choices=ASSET_TYPES) 

391 description = models.TextField(blank=True) 

392 estimated_value = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True) 

393 acquisition_date = models.DateField(null=True, blank=True) 

394 location = models.CharField(max_length=200, blank=True) 

395 legal_status = models.CharField(max_length=20, choices=LEGAL_STATUS, default='owned') 

396 importance = models.IntegerField(choices=[(1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical')], default=2) 

397 

398 # Relationships 

399 owners = models.ManyToManyField(Person, related_name='assets') 

400 related_documents = models.ManyToManyField(Multimedia, related_name='assets', blank=True) 

401 plans = models.ManyToManyField(Planning, related_name='assets', blank=True) 

402 

403 created_at = models.DateTimeField(auto_now_add=True) 

404 updated_at = models.DateTimeField(auto_now=True) 

405 

406 class Meta: 

407 verbose_name_plural = "Assets" 

408 

409 def __str__(self): 

410 return self.name 

411 

412 

413class Timeline(models.Model): 

414 """Time-based organization of information""" 

415 TIMELINE_TYPES = [ 

416 ('personal', 'Personal Milestone'), 

417 ('family', 'Family Event'), 

418 ('historical', 'Historical Context'), 

419 ('other', 'Other'), 

420 ] 

421 

422 title = models.CharField(max_length=200) 

423 description = models.TextField(blank=True) 

424 timeline_type = models.CharField(max_length=20, choices=TIMELINE_TYPES) 

425 date = models.DateField() 

426 end_date = models.DateField(null=True, blank=True) 

427 importance = models.IntegerField(choices=[(1, 'Low'), (2, 'Medium'), (3, 'High'), (4, 'Critical')], default=2) 

428 historical_context = models.TextField(blank=True) 

429 

430 # Relationships (generic foreign keys could be used here for more flexibility) 

431 people = models.ManyToManyField(Person, related_name='timeline_entries', blank=True) 

432 events = models.ManyToManyField(Event, related_name='timeline_entries', blank=True) 

433 stories = models.ManyToManyField(Story, related_name='timeline_entries', blank=True) 

434 

435 created_at = models.DateTimeField(auto_now_add=True) 

436 

437 class Meta: 

438 ordering = ['-date'] 

439 

440 def __str__(self): 

441 return f"{self.title} ({self.date})"