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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

import logging 

 

from django.conf import settings 

from django.db import connection 

from rest_framework import status 

from rest_framework.permissions import BasePermission 

from rest_framework.response import Response 

from rest_framework.viewsets import ViewSet 

 

from backend_app.checks import check_viewsets 

from backend_app.models.abstract.base import BaseModelViewSet 

from backend_app.models.abstract.essentialModule import EssentialModuleViewSet 

from backend_app.models.country import CountryViewSet 

from backend_app.models.countryDri import CountryDriViewSet 

from backend_app.models.countryScholarship import CountryScholarshipViewSet 

from backend_app.models.course import Course 

from backend_app.models.courseFeedback import CourseFeedback 

from backend_app.models.currency import CurrencyViewSet 

from backend_app.models.exchange import Exchange, UnivMajorMinorsViewSet 

from backend_app.models.exchangeFeedback import ExchangeFeedbackViewSet 

from backend_app.models.file_picture import FileViewSet, PictureViewSet 

from backend_app.models.for_testing.moderation import ForTestingModerationViewSet 

from backend_app.models.for_testing.versioning import ForTestingVersioningViewSet 

from backend_app.models.language import LanguageViewSet 

from backend_app.models.lastVisitedUniversities import LastVisitedUniversity 

from backend_app.models.offer import OfferViewSet 

from backend_app.models.partner import Partner 

from backend_app.models.pendingModeration import ( 

PendingModerationViewSet, 

PendingModerationObjViewSet, 

) 

from backend_app.models.recommendationList import ( 

RecommendationListViewSet, 

RecommendationList, 

) 

from backend_app.models.sharedUnivFeedback import SharedUnivFeedbackViewSet 

from backend_app.models.taggedItems import ( 

CountryTaggedItemViewSet, 

UniversityTaggedItemViewSet, 

) 

from backend_app.models.university import University, UniversityViewSet 

from backend_app.models.universityDri import UniversityDriViewSet 

from backend_app.models.universityInfo import UniversityInfoViewSet 

from backend_app.models.universityScholarship import UniversityScholarshipViewSet 

from backend_app.models.universitySemestersDates import UniversitySemestersDatesViewSet 

from backend_app.models.userData import UserDataViewSet 

from backend_app.models.version import VersionViewSet 

from backend_app.permissions.app_permissions import ( 

ReadOnly, 

IsStaff, 

NoDelete, 

NoPost, 

IsAuthenticated, 

) 

from backend_app.serializers import ( 

CourseFeedbackSerializer, 

ExchangeSerializerSimple, 

CourseSerializer, 

) 

from backend_app.settings.defaults import OBJ_MODERATION_PERMISSIONS 

from base_app.models import UserViewset, User, SiteInformationViewSet 

from external_data.management.commands.utils import UtcData 

from external_data.models import ExternalDataUpdateInfo 

 

 

class CourseViewSet(BaseModelViewSet): 

queryset = Course.objects.all().select_related( 

"exchange", 

"exchange__feedbacks", 

"exchange__feedbacks__moderated_by", 

"exchange__feedbacks__updated_by", 

"course_feedback", 

"course_feedback__moderated_by", 

"course_feedback__updated_by", 

) 

serializer_class = CourseSerializer 

permission_classes = (ReadOnly,) 

end_point_route = "courses" 

filterset_fields = ("exchange",) 

required_filterset_fields = ("exchange",) 

 

 

class CourseFeedbackPermission(BasePermission): 

""" 

Permission that checks that the requester is the student concern by the exchange / course. 

""" 

 

def has_object_permission(self, request, view, obj): 

student = obj.course.exchange.student 

if student is None: 

return False 

return request.user.pk == student.pk 

 

 

class CourseFeedbackViewSet(EssentialModuleViewSet): 

permission_classes = ( 

NoDelete & NoPost & (ReadOnly | IsStaff | CourseFeedbackPermission), 

) 

queryset = CourseFeedback.objects.filter(course__unlinked=False).select_related( 

"course", "updated_by", "moderated_by" 

) 

 

serializer_class = CourseFeedbackSerializer 

end_point_route = "courseFeedbacks" 

filterset_fields = ("course__exchange",) 

required_filterset_fields = ("course__exchange",) 

 

 

class ExchangeViewSet(BaseModelViewSet): 

permission_classes = (ReadOnly,) 

queryset = ( 

Exchange.objects.filter(unlinked=False) 

.select_related("student") 

.prefetch_related("exchange_courses", "exchange_courses__course_feedback") 

) 

serializer_class = ExchangeSerializerSimple 

end_point_route = "exchanges" 

filterset_fields = ("student",) 

required_filterset_fields = ("student",) 

 

 

ALL_API_VIEWSETS = [ 

SiteInformationViewSet, 

UserViewset, 

CountryViewSet, 

CountryDriViewSet, 

CountryScholarshipViewSet, 

CountryTaggedItemViewSet, 

CourseViewSet, 

CourseFeedbackViewSet, 

CurrencyViewSet, 

OfferViewSet, 

LanguageViewSet, 

PendingModerationViewSet, 

PendingModerationObjViewSet, 

FileViewSet, 

PictureViewSet, 

ExchangeViewSet, 

ExchangeFeedbackViewSet, 

UnivMajorMinorsViewSet, 

RecommendationListViewSet, 

UniversityViewSet, 

SharedUnivFeedbackViewSet, 

UniversityDriViewSet, 

UniversityInfoViewSet, 

UniversityScholarshipViewSet, 

UniversitySemestersDatesViewSet, 

UniversityTaggedItemViewSet, 

UserDataViewSet, 

VersionViewSet, 

] 

 

if settings.TESTING: 

# We only register viewsets in a testing environment 

ALL_API_VIEWSETS += [ForTestingModerationViewSet, ForTestingVersioningViewSet] 

 

 

class AppModerationStatusViewSet(ViewSet): 

""" 

Viewset to know what is the app moderation status 

""" 

 

# Since AppModerationStatusViewSet doesn't inherit from BaseModelViewSet 

# We need to link here the correct permissions 

permission_classes = (ReadOnly,) 

end_point_route = "serverModerationStatus" 

 

def list(self, request): 

return Response( 

{ 

"activated": settings.MODERATION_ACTIVATED, 

"moderator_level": OBJ_MODERATION_PERMISSIONS["moderator"], 

} 

) 

 

 

class LatestUpdateExternalDataViewSet(ViewSet): 

""" 

Viewset to fetch the latest update dates of the external data 

""" 

 

# Since AppModerationStatusViewSet doesn't inherit from BaseModelViewSet 

# We need to link here the correct permissions 

permission_classes = (ReadOnly,) 

end_point_route = "externalDataUpdateInfo" 

 

def list(self, request): 

objects = ( 

ExternalDataUpdateInfo.objects.all() 

.order_by("source", "-timestamp") 

.distinct("source") 

) 

 

return Response( 

list( 

map( 

lambda obj: dict(timestamp=obj.timestamp, source=obj.source), 

objects, 

) 

) 

) 

 

 

class UnlinkedUtcPartners(ViewSet): 

""" 

Viewset to fetch the latest list of utc partners that are not linked to a univeristy 

""" 

 

permission_classes = (ReadOnly,) 

end_point_route = "unlinkedUtcPartners" 

 

def list(self, request): 

partners = Partner.objects.filter(university=None) 

 

return Response( 

[dict(partner_id=p.utc_id, partner_univ_name=p.univ_name) for p in partners] 

) 

 

 

class UpdateStudentExchangesViewSet(ViewSet): 

""" 

Viewset to be able to ban and un-ban users from the site 

""" 

 

end_point_route = "updateStudentExchanges" 

permission_classes = (ReadOnly,) 

 

def list(self, request, **kwargs): 

user = request.user 

UtcData().update_one_student(user.username) 

return Response() 

 

 

class LogFrontendErrorsViewSet(ViewSet): 

""" 

Viewset to handle the logging of errors coming from the frontend. 

""" 

 

permission_classes = tuple() 

end_point_route = "frontendErrors" 

 

def create(self, request): 

logger = logging.getLogger("frontend") 

data = request.data 

if "componentStack" in data.keys(): 

logger.error(request.data["componentStack"]) 

else: 

logger.error(request.data) 

return Response(status=201) 

 

 

class BannedUserViewSet(ViewSet): 

""" 

Viewset to be able to ban and un-ban users from the site 

""" 

 

end_point_route = "banned_users" 

permission_classes = (IsStaff,) 

 

def list(self, request, **kwargs): 

return Response( 

[ 

dict(user_id=user.pk, user_login=user.username) 

for user in User.objects.filter(is_banned=True) 

] 

) 

 

def update(self, request, pk=None): 

if pk is None: 

return Response(status=403) 

 

user = User.objects.get(pk=pk) 

if user.is_staff: 

# Prevent ban of admin users 

return Response(status=403) 

user.is_banned = True 

user.save() 

return Response(status=200) 

 

def delete(self, request, pk=None): 

if pk is None: 

return Response(status=403) 

 

user = User.objects.get(pk=pk) 

user.is_banned = False 

user.save() 

return Response(status=200) 

 

 

class DeleteUserViewSet(ViewSet): 

""" 

Viewset to handle account deletion 

""" 

 

end_point_route = "emptyUserAccount" 

permission_classes = (IsAuthenticated,) 

 

def create(self, request): 

""" 

Line up the user from the request for deletion 

""" 

user = request.user 

user.delete_next_time = True 

user.save() 

return Response(status=201) 

 

def update(self, request): 

# Here only to have the correct routes automatically generated, not to be used. 

return Response(status=403) 

 

def delete(self, request, pk="osef"): # don't delete this unused argument! 

""" 

Un-Line up the user from the request for deletion 

""" 

user = request.user 

user.delete_next_time = False 

user.save() 

return Response(status=200) 

 

 

class RecommendationListChangeFollowerViewSet(ViewSet): 

""" 

Viewset to be able to add or delete followers on 

a recommendation list 

""" 

 

# Since RecommendationListChangeFollowerViewSet doesn't inherit from BaseModelViewSet 

# We need to link here the correct permissions 

end_point_route = "recommendationListChangeFollower" 

permission_classes = (IsAuthenticated,) 

 

def update(self, request, pk=None): 

if pk is None: 

return Response(status=403) 

 

recommendation = RecommendationList.objects.get(pk=pk) 

if recommendation.is_public: 

recommendation.followers.add(request.user) 

recommendation.save() 

return Response(status=200) 

else: 

return Response(status=403) 

 

def delete(self, request, pk=None): 

if pk is None: 

return Response(status=403) 

# can delete folower even if list not public 

recommendation = RecommendationList.objects.get(pk=pk) 

recommendation.followers.remove(request.user) 

recommendation.save() 

return Response(status=200) 

 

 

class LastVisitedUniversityViewSet(ViewSet): 

""" 

Viewset to update the LastVisitedUniversity model when a user looks at a university 

 

Doc available here: https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions 

""" 

 

end_point_route = "lastVisitedUniversities" 

permission_classes = (IsAuthenticated,) 

 

def list(self, request): 

sql = """ 

SELECT 

university_id as university, 

max(ts) AS ts 

FROM backend_app_lastvisiteduniversity 

WHERE 

user_id = %s 

GROUP BY university_id 

ORDER BY ts DESC 

LIMIT 5 

""" 

with connection.cursor() as cursor: 

cursor.execute(sql, [request.user.pk]) 

rows = cursor.fetchall() 

data = [{"university": r[0], "ts": r[1]} for r in rows] 

 

return Response(data, status=status.HTTP_200_OK) 

 

def create(self, request): 

LastVisitedUniversity.objects.create( 

user=request.user, 

university=University.objects.get(pk=request.data["university"]), 

) 

return Response(status=status.HTTP_201_CREATED) 

 

 

ALL_API_VIEW_VIEWSETS = [ 

AppModerationStatusViewSet, 

LatestUpdateExternalDataViewSet, 

UnlinkedUtcPartners, 

UpdateStudentExchangesViewSet, 

LogFrontendErrorsViewSet, 

BannedUserViewSet, 

RecommendationListChangeFollowerViewSet, 

DeleteUserViewSet, 

LastVisitedUniversityViewSet, 

] 

 

ALL_VIEWSETS = ALL_API_VIEWSETS + ALL_API_VIEW_VIEWSETS 

 

check_viewsets(ALL_VIEWSETS)