Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import logging 

2import uuid 

3from datetime import timedelta 

4 

5from actstream.models import followers, following, user_stream 

6from django.apps import apps 

7from django.contrib.auth.models import AbstractUser 

8from django.contrib.contenttypes.models import ContentType 

9from django.db import models 

10from django.urls import reverse 

11from django.utils import timezone 

12from django.utils.translation import gettext as _ 

13from django_bleach.models import BleachField 

14from django.template.loader import render_to_string 

15from guardian.shortcuts import get_objects_for_user 

16from PIL import Image 

17from taggit.managers import TaggableManager 

18from actstream.models import Action 

19 

20from discuss_data.core.models import AffiliationTags, EnglishTags, KeywordTags, Topic 

21from discuss_data.core.utils import weekly_td, daily_td 

22from discuss_data.ddcomments.models import Notification 

23from discuss_data.utils.cropped_thumbnail import cropped_thumbnail 

24 

25logger = logging.getLogger(__name__) 

26 

27 

28ACADEMIC_TITLE_CHOICES = ( 

29 ("PHD", "PhD"), 

30 ("DR", "Dr."), 

31 ("PROF", "Prof. Dr."), 

32 ("MA", "MA",), 

33 ("BA", "BA",), 

34 ("MAG", "Magister"), 

35 ("M.A.", "M.A."), 

36) 

37 

38ACADEMIC_TITLE_CHOICES_ORDER = { 

39 "PHD": "b", 

40 "DR": "f", 

41 "PROF": "f", 

42 "MA": "b", 

43 "BA": "b", 

44 "MAG": "f", 

45 "M.A.": "b", 

46} 

47 

48 

49class User(AbstractUser): 

50 

51 """User profile accessibility 

52 PUB - accessible for all website visitors 

53 NET - accessible for logged-in users 

54 HID - accessible for owner only 

55 """ 

56 

57 PUBLIC = "PUB" 

58 NETWORK = "NET" 

59 HIDDEN = "HID" 

60 

61 ACCESS_RULES_CHOICES = ( 

62 (PUBLIC, _("public")), 

63 (NETWORK, _("internal")), 

64 (HIDDEN, _("hidden")), 

65 ) 

66 id = models.AutoField(primary_key=True) 

67 uuid = models.UUIDField( 

68 default=uuid.uuid4, editable=False 

69 ) # uuid _not_ as pk as this disturbs django 3rd party apps 

70 middle_name = models.CharField(max_length=200, blank=True) 

71 academic_title = models.CharField( 

72 max_length=5, choices=ACADEMIC_TITLE_CHOICES, blank="None" 

73 ) # deprecated 

74 name_prefix = models.CharField( 

75 max_length=200, blank=True, verbose_name="prefix", help_text='for example "Dr"', 

76 ) 

77 name_suffix = models.CharField( 

78 max_length=200, 

79 blank=True, 

80 verbose_name="suffix", 

81 help_text='for example "PhD"', 

82 ) 

83 # position = models.CharField(max_length=600) 

84 # institution = models.ForeignKey('Institution', blank=True, null=True, related_name='user_profile_institution') 

85 # institutions = models.ManyToManyField('Institution', blank=True,) 

86 photo = models.ImageField(blank=True, null=True) 

87 # fields_of_interest = models.ManyToManyField('FOI', blank=True) 

88 topics = models.ManyToManyField(Topic, blank=True) 

89 interests = TaggableManager(blank=True, through=KeywordTags) 

90 receptive_tos = models.ManyToManyField("ReceptiveTo", blank=True) 

91 countries = models.ManyToManyField( 

92 "Country", related_name="user_country", blank=True 

93 ) 

94 research_profile_full = BleachField( 

95 blank=True, max_length=12000, verbose_name="Research Profile (full)" 

96 ) 

97 research_profile_short = BleachField( 

98 max_length=280, 

99 blank=True, 

100 verbose_name="Research Profile (brief)", 

101 help_text="brief version, max. 280 characters", 

102 ) 

103 profile_accessibility = models.CharField( 

104 max_length=3, choices=ACCESS_RULES_CHOICES, default=NETWORK 

105 ) 

106 publications = models.ManyToManyField( 

107 "ddpublications.Publication", blank=True, related_name="user_publication", 

108 ) 

109 en_tags = TaggableManager(blank=True, through=EnglishTags) 

110 affiliation_tags = TaggableManager( 

111 "affiliations", blank=True, through=AffiliationTags 

112 ) 

113 external_profile = models.URLField(blank=True) 

114 

115 accepted_dariah_tou = models.BooleanField( 

116 verbose_name="Accept DARIAH TOU", default=False 

117 ) 

118 

119 accepted_dd_tou = models.BooleanField( 

120 verbose_name="Accept Discuss Data TOU", default=False 

121 ) 

122 

123 show_first_steps = models.BooleanField( 

124 verbose_name="Show First Steps", default=True 

125 ) 

126 email_alternative = models.EmailField(blank=True) 

127 

128 class Meta: 

129 ordering = ["last_name"] 

130 

131 def get_academic_name(self): 

132 a_n = list() 

133 if self.name_prefix: 

134 a_n.append(self.name_prefix) 

135 

136 if self.first_name: 

137 a_n.append(self.first_name) 

138 

139 if self.middle_name: 

140 a_n.append(self.middle_name) 

141 

142 if self.last_name: 

143 a_n.append(self.last_name) 

144 

145 academic_name = " ".join(a_n) 

146 

147 if self.name_suffix: 

148 academic_name = academic_name + ", " + self.name_suffix 

149 

150 return academic_name 

151 

152 def get_email(self): 

153 if self.email_alternative: 

154 return self.email_alternative 

155 else: 

156 return self.email 

157 

158 def get_publications(self): 

159 return self.publications.all().order_by("-year") 

160 

161 def get_countries(self): 

162 return self.countries.all().order_by("name") 

163 

164 def get_interests(self): 

165 return self.interests.all().order_by("name") 

166 

167 def get_main_affiliation(self): 

168 return Affiliation.objects.filter(user=self).filter(main=True) 

169 

170 def get_affiliations(self): 

171 return self.affiliation_user.all().order_by("list_position") 

172 

173 def get_lists_public(self): 

174 model = apps.get_model("dddatasets", "datalist") 

175 return model.objects.filter(public=True, owner=self) 

176 

177 def get_lists_all(self): 

178 model = apps.get_model("dddatasets", "datalist") 

179 return model.objects.filter(owner=self) 

180 

181 def get_published_admin_datasets(self): 

182 model = apps.get_model("dddatasets", "dataset") 

183 return get_objects_for_user(self, "admin_dsmo", model).filter(published=True) 

184 

185 def get_published_datasets(self): 

186 # use get model to avoid circular import 

187 model = apps.get_model("dddatasets", "datasetmanagementobject") 

188 dsmos = model.objects.filter(owner=self).filter(published=True) 

189 datasets = list() 

190 for dsmo in dsmos: 

191 if dsmo.get_top_version_published_dataset(): 

192 datasets.append(dsmo.get_top_version_published_dataset()) 

193 return datasets 

194 

195 def get_published_datasets_count(self): 

196 return len(self.get_published_datasets()) 

197 

198 def curated_categories(self): 

199 return self.category_curators_user.all() 

200 

201 def get_curated_datasets(self): 

202 model = apps.get_model("dddatasets", "dataset") 

203 datasets = model.objects.filter( 

204 dataset_management_object__main_category__in=self.curated_categories() 

205 ) 

206 return datasets 

207 

208 def get_all_datasets(self): 

209 return self.dataset_set.all() 

210 

211 def get_projects(self): 

212 return Project.objects.filter(user=self).order_by("name") 

213 # projects_list = list() 

214 # for project in Project.objects.filter(user=self): 

215 # projects_list.append(project) 

216 # return projects_list 

217 

218 def __str__(self): 

219 return self.get_academic_name() 

220 

221 def get_notifications_curators(self, dataset_content_type): 

222 user_curated_datasets = list( 

223 self.get_curated_datasets().values_list("id", flat=True) 

224 ) 

225 notifications_curators = Notification.objects.filter( 

226 content_type=dataset_content_type.id, object_id__in=user_curated_datasets 

227 ) 

228 return notifications_curators 

229 

230 def get_notifications(self): 

231 user_all_datasets = list(self.get_all_datasets().values_list("id", flat=True)) 

232 

233 user_admin_datasets = list( 

234 self.get_published_admin_datasets().values_list("id", flat=True) 

235 ) 

236 

237 user_notifications_to = Notification.objects.filter(recipient=self) 

238 

239 dataset_content_type = ContentType.objects.get( 

240 app_label="dddatasets", model="dataset" 

241 ) 

242 

243 datasets_notifications = Notification.objects.filter( 

244 content_type=dataset_content_type.id, object_id__in=user_all_datasets 

245 ) 

246 

247 datasets_notifications_admins = Notification.objects.filter( 

248 content_type=dataset_content_type.id, object_id__in=user_admin_datasets 

249 ) 

250 

251 datasets_notifications_curators = self.get_notifications_curators( 

252 dataset_content_type 

253 ) 

254 

255 notifications = ( 

256 user_notifications_to 

257 | datasets_notifications 

258 | datasets_notifications_curators 

259 | datasets_notifications_admins 

260 ) 

261 return notifications 

262 

263 def get_notifications_count(self): 

264 return self.get_notifications().count() 

265 

266 def get_notifications_recent(self): 

267 startdate = timezone.now() 

268 enddate = startdate - timedelta(days=7) 

269 return ( 

270 self.get_notifications() 

271 .filter(date_added__lte=startdate) 

272 .filter(date_added__gte=enddate) 

273 ) 

274 

275 def get_notifications_recent_count(self): 

276 return self.get_notifications_recent().count() 

277 

278 def get_following_actions(self): 

279 return user_stream(self) 

280 

281 def get_following_actions_recent_weekly(self): 

282 startdate, enddate = weekly_td() 

283 return ( 

284 self.get_following_actions() 

285 .filter(timestamp__lte=startdate) 

286 .filter(timestamp__gte=enddate) 

287 ) 

288 

289 def get_following_actions_recent_daily(self): 

290 startdate, enddate = daily_td() 

291 return ( 

292 self.get_following_actions() 

293 .filter(timestamp__lte=startdate) 

294 .filter(timestamp__gte=enddate) 

295 ) 

296 

297 def get_datasets_actions(self): 

298 user_admin_datasets = list( 

299 self.get_published_admin_datasets().values_list("id", flat=True) 

300 ) 

301 actor_ctype = ContentType.objects.get(app_label="ddusers", model="user") 

302 return Action.objects.datasets_actions(user_admin_datasets).exclude( 

303 actor_content_type=actor_ctype, actor_object_id=self.id 

304 ) 

305 

306 def get_datasets_actions_recent_weekly(self): 

307 startdate, enddate = weekly_td() 

308 return ( 

309 self.get_datasets_actions() 

310 .filter(timestamp__lte=startdate) 

311 .filter(timestamp__gte=enddate) 

312 ) 

313 

314 def get_datasets_actions_recent_daily(self): 

315 startdate, enddate = daily_td() 

316 return ( 

317 self.get_datasets_actions() 

318 .filter(timestamp__lte=startdate) 

319 .filter(timestamp__gte=enddate) 

320 ) 

321 

322 def get_user_actions(self): 

323 return Action.objects.any_everything(self) 

324 

325 def get_user_actions_recent_weekly(self): 

326 startdate, enddate = weekly_td() 

327 return ( 

328 self.get_user_actions() 

329 .filter(timestamp__lte=startdate) 

330 .filter(timestamp__gte=enddate) 

331 ) 

332 

333 def get_user_actions_recent_daily(self): 

334 startdate, enddate = daily_td() 

335 return ( 

336 self.get_user_actions() 

337 .filter(timestamp__lte=startdate) 

338 .filter(timestamp__gte=enddate) 

339 ) 

340 

341 def compose_status_email_daily(self): 

342 # daily status email 

343 feed_actions = self.get_user_actions_recent_daily() 

344 return render_to_string( 

345 "ddusers/status_email.html", {"user": self, "feed_actions": feed_actions} 

346 ) 

347 

348 def compose_status_email_weekly(self): 

349 # weekly status email 

350 feed_actions = self.get_user_actions_recent_weekly() 

351 return render_to_string( 

352 "ddusers/status_email.html", {"user": self, "feed_actions": feed_actions} 

353 ) 

354 

355 def get_following(self): 

356 return following(self, User) 

357 

358 def get_followers(self): 

359 return followers(self) 

360 

361 def get_following_count(self): 

362 return len(following(self, User)) 

363 

364 def get_followers_count(self): 

365 return len(followers(self)) 

366 

367 def group_member(self, group): 

368 return self.groups.filter(id=group.id).exists() 

369 

370 @property 

371 def image_url(self): 

372 try: 

373 return self.photo.path 

374 except Exception: 

375 return "/static/images/user_default.png" 

376 

377 def get_activities(self): 

378 return user_stream(self, with_user_activity=True) 

379 

380 def get_absolute_url(self): 

381 return reverse("ddusers:detail", args=[str(self.uuid)]) 

382 

383 def class_name(self): 

384 return self.__class__.__name__ 

385 

386 # override for thumbnail generation 

387 def save(self, *args, **kwargs): 

388 super().save(*args, **kwargs) 

389 if self.photo and hasattr(self.photo, "url"): 

390 # in situ thumbnail generation at upload 

391 im = Image.open(self.photo.path) 

392 thumbnail = cropped_thumbnail(im, [250, 250]) 

393 thumbnail.save(self.photo.path) 

394 

395 if kwargs.get("force_insert"): 

396 kwargs.pop("force_insert") 

397 super().save(*args, **kwargs) 

398 

399 

400class ReceptiveTo(models.Model): 

401 text = models.CharField(max_length=200, blank=True) 

402 

403 def __str__(self): 

404 return self.text 

405 

406 

407class City(models.Model): 

408 name = models.CharField(max_length=400) 

409 slug = models.SlugField() 

410 geo_lat = models.CharField(max_length=200, blank=True) 

411 geo_lng = models.CharField(max_length=200, blank=True) 

412 country = models.ForeignKey( 

413 "Country", related_name="city_country", on_delete=models.CASCADE 

414 ) 

415 public = models.BooleanField(default=True) 

416 

417 def __str__(self): 

418 return self.name 

419 

420 

421class Country(models.Model): 

422 name = models.CharField(max_length=200) 

423 slug = models.SlugField() 

424 geo_lat = models.CharField(max_length=200) 

425 geo_lng = models.CharField(max_length=200) 

426 public = models.BooleanField(default=True) 

427 

428 class Meta: 

429 ordering = ["name"] 

430 

431 def __str__(self): 

432 return self.name 

433 

434 

435class FOI(models.Model): 

436 name = models.CharField(max_length=200) 

437 description = BleachField(max_length=1200, blank=True) 

438 is_country = models.BooleanField(default=False) 

439 

440 def __str__(self): 

441 return self.name 

442 

443 

444class Affiliation(models.Model): 

445 uuid = models.UUIDField( 

446 default=uuid.uuid4, editable=False 

447 ) # uuid _not_ as pk as this disturbs django 3rd party apps 

448 user = models.ForeignKey( 

449 "User", related_name="affiliation_user", on_delete=models.CASCADE 

450 ) 

451 institution = models.ForeignKey( 

452 "Institution", 

453 related_name="affiliation_institution", 

454 blank=True, 

455 null=True, 

456 on_delete=models.CASCADE, 

457 ) 

458 name_of_institution = models.CharField(max_length=200) 

459 place_of_institution = models.CharField(max_length=200) 

460 country_of_institution = models.CharField(max_length=200) 

461 position = models.CharField(max_length=200) 

462 website_of_institution = models.URLField(blank=True) 

463 main = models.BooleanField(default=True) 

464 list_position = models.IntegerField(null=True, blank=True) 

465 

466 def get_institution(self): 

467 return "{}, {}, {}".format( 

468 self.name_of_institution, 

469 self.place_of_institution, 

470 self.country_of_institution, 

471 ) 

472 

473 def __str__(self): 

474 return "%s, %s, %s, %s" % ( 

475 self.position, 

476 self.name_of_institution, 

477 self.place_of_institution, 

478 self.country_of_institution, 

479 ) 

480 

481 

482class Institution(models.Model): 

483 uuid = models.UUIDField( 

484 default=uuid.uuid4, editable=False 

485 ) # uuid _not_ as pk as this disturbs django 3rd party apps 

486 name = models.CharField(max_length=600) 

487 original_name = models.CharField(max_length=600, blank=True) 

488 short_name = models.CharField(max_length=60, blank=True) 

489 department = models.CharField(max_length=600, blank=True) 

490 address = models.CharField(max_length=600) 

491 postcode = models.CharField(max_length=40, blank=True, null=True) 

492 image = models.ImageField(blank=True, null=True) 

493 image_large = models.ImageField(blank=True, null=True) 

494 website = models.URLField(blank=True) 

495 description = BleachField(max_length=1200, blank=True) 

496 short_description = models.CharField(max_length=400, blank=True) 

497 location = models.CharField(max_length=20, blank=True) 

498 city = models.CharField(max_length=120) 

499 country = models.ForeignKey( 

500 "Country", 

501 related_name="institution_country", 

502 blank=True, 

503 on_delete=models.CASCADE, 

504 ) 

505 public = models.BooleanField(default=True) 

506 admin = models.ForeignKey( 

507 "User", 

508 related_name="institution_admin_user", 

509 blank=True, 

510 null=True, 

511 on_delete=models.CASCADE, 

512 ) 

513 affiliated_users = models.ManyToManyField("User", blank=True,) 

514 

515 def __str__(self): 

516 return self.name 

517 

518 

519class Project(models.Model): 

520 uuid = models.UUIDField( 

521 default=uuid.uuid4, editable=False 

522 ) # uuid _not_ as pk as this disturbs django 3rd party apps 

523 user = models.ForeignKey( 

524 "User", related_name="project_user", on_delete=models.CASCADE 

525 ) 

526 # group = models.ForeignKey( 

527 # Group, 

528 # blank=True, 

529 # null=True, 

530 # related_name="project_group", 

531 # on_delete=models.CASCADE, 

532 # ) 

533 name = models.CharField(max_length=200) 

534 url = models.URLField(blank=True) 

535 description = BleachField(blank=True, max_length=2400) 

536 

537 def __str__(self): 

538 return self.name