15
15
from PennCourses .settings .base import FIRST_BANNER_SEM , PRE_NGSS_PERMIT_REQ_RESTRICTION_CODES
16
16
from review .annotations import review_averages
17
17
18
-
19
18
User = get_user_model ()
20
19
21
20
@@ -154,9 +153,7 @@ class Course(models.Model):
154
153
),
155
154
)
156
155
code = models .CharField (
157
- max_length = 8 ,
158
- db_index = True ,
159
- help_text = "The course code, e.g. `120` for CIS-120." ,
156
+ max_length = 8 , db_index = True , help_text = "The course code, e.g. `120` for CIS-120."
160
157
)
161
158
semester = models .CharField (
162
159
max_length = 5 ,
@@ -204,15 +201,6 @@ class Course(models.Model):
204
201
help_text = "The dash-joined department and code of the course, e.g. `CIS-120` for CIS-120." ,
205
202
)
206
203
207
- credits = models .DecimalField (
208
- max_digits = 4 , # some course for 2019C is 14 CR...
209
- decimal_places = 2 ,
210
- null = True ,
211
- blank = True ,
212
- db_index = True ,
213
- help_text = "The number of credits this course takes. This is precomputed for efficiency." ,
214
- )
215
-
216
204
prerequisites = models .TextField (
217
205
blank = True ,
218
206
help_text = "Text describing the prereqs for a course, e.g. 'CIS 120, 160' for CIS-121." ,
@@ -276,7 +264,7 @@ class Course(models.Model):
276
264
help_text = dedent (
277
265
"""
278
266
The number of distinct activities belonging to this course (precomputed for efficiency).
279
- Maintained by the registrar import / recomputestats script.
267
+ Maintained by the registrar import / recompute_soft_state script.
280
268
"""
281
269
),
282
270
)
@@ -377,36 +365,6 @@ class Topic(models.Model):
377
365
),
378
366
)
379
367
380
- historical_probabilities_spring = models .FloatField (
381
- default = 0 ,
382
- help_text = dedent (
383
- """
384
- The historical probability of a student taking a course in this topic in the spring
385
- semester, based on historical data. This field is recomputed nightly from the
386
- `parent_course` graph (in the recompute_soft_state cron job).
387
- """
388
- ),
389
- )
390
- historical_probabilities_summer = models .FloatField (
391
- default = 0 ,
392
- help_text = dedent (
393
- """
394
- The historical probability of a student taking a course in this topic in the summer
395
- semester, based on historical data. This field is recomputed nightly from the
396
- `parent_course` graph (in the recompute_soft_state cron job).
397
- """
398
- ),
399
- )
400
- historical_probabilities_fall = models .FloatField (
401
- default = 0 ,
402
- help_text = dedent (
403
- """
404
- The historical probability of a student taking a course in this topic in the fall
405
- semester, based on historical data. This field is recomputed nightly from the
406
- `parent_course` graph (in the recompute_soft_state cron job).
407
- """
408
- ),
409
- )
410
368
branched_from = models .ForeignKey (
411
369
"Topic" ,
412
370
related_name = "branched_to" ,
@@ -763,8 +721,7 @@ class Meta:
763
721
)
764
722
765
723
instructors = models .ManyToManyField (
766
- Instructor ,
767
- help_text = "The Instructor object(s) of the instructor(s) teaching the section." ,
724
+ Instructor , help_text = "The Instructor object(s) of the instructor(s) teaching the section."
768
725
)
769
726
associated_sections = models .ManyToManyField (
770
727
"Section" ,
@@ -823,8 +780,7 @@ class Meta:
823
780
)
824
781
825
782
registration_volume = models .PositiveIntegerField (
826
- default = 0 ,
827
- help_text = "The number of active PCA registrations watching this section." ,
783
+ default = 0 , help_text = "The number of active PCA registrations watching this section."
828
784
) # For the set of PCA registrations for this section, use the related field `registrations`.
829
785
830
786
def __str__ (self ):
@@ -881,9 +837,7 @@ def current_percent_open(self):
881
837
return None
882
838
try :
883
839
last_status_update = StatusUpdate .objects .filter (
884
- section = self ,
885
- created_at__gt = add_drop_start ,
886
- created_at__lt = add_drop_end ,
840
+ section = self , created_at__gt = add_drop_start , created_at__lt = add_drop_end
887
841
).latest ("created_at" )
888
842
except StatusUpdate .DoesNotExist :
889
843
last_status_update = None
@@ -928,12 +882,7 @@ class StatusUpdate(models.Model):
928
882
A registration status update for a specific section (e.g. CIS-120-001 went from open to close)
929
883
"""
930
884
931
- STATUS_CHOICES = (
932
- ("O" , "Open" ),
933
- ("C" , "Closed" ),
934
- ("X" , "Cancelled" ),
935
- ("" , "Unlisted" ),
936
- )
885
+ STATUS_CHOICES = (("O" , "Open" ), ("C" , "Closed" ), ("X" , "Cancelled" ), ("" , "Unlisted" ))
937
886
section = models .ForeignKey (
938
887
Section ,
939
888
related_name = "status_updates" ,
@@ -968,8 +917,7 @@ class StatusUpdate(models.Model):
968
917
# and the save() method of StatusUpdate
969
918
970
919
in_add_drop_period = models .BooleanField (
971
- default = False ,
972
- help_text = "Was this status update created during the add/drop period?" ,
920
+ default = False , help_text = "Was this status update created during the add/drop period?"
973
921
) # This field is maintained in the save() method of alerts.models.AddDropPeriod,
974
922
# and the save() method of StatusUpdate
975
923
@@ -1100,8 +1048,7 @@ class Room(models.Model):
1100
1048
),
1101
1049
)
1102
1050
number = models .CharField (
1103
- max_length = 8 ,
1104
- help_text = "The room number, e.g. `101` for Wu and Chen Auditorium in Levine." ,
1051
+ max_length = 8 , help_text = "The room number, e.g. `101` for Wu and Chen Auditorium in Levine."
1105
1052
)
1106
1053
name = models .CharField (
1107
1054
max_length = 80 ,
@@ -1540,3 +1487,73 @@ def __str__(self):
1540
1487
return (
1541
1488
f"Friendship(Sender: { self .sender } , Recipient: { self .recipient } , Status: { self .status } )"
1542
1489
)
1490
+
1491
+ class Comment (models .Model ):
1492
+ """
1493
+ A single comment associated with a topic to be displayed on PCR. Comments support replies
1494
+ through the parent_id and path fields. The path field allows for efficient database querying
1495
+ and can indicate levels of nesting and can make pagination simpler. Idea implemented based
1496
+ on this guide: https://blog.miguelgrinberg.com/post/implementing-user-comments-with-sqlalchemy.
1497
+ """
1498
+
1499
+ # Log base 10 value of maximum adjacent comment length.
1500
+ _N = 10
1501
+
1502
+ text = models .TextField ()
1503
+ created_at = models .DateTimeField (auto_now_add = True )
1504
+ modified_at = models .DateTimeField (auto_now = True )
1505
+ author = models .ForeignKey (
1506
+ get_user_model (),
1507
+ on_delete = models .SET_NULL ,
1508
+ null = True ,
1509
+ related_name = "comments"
1510
+ )
1511
+ upvotes = models .ManyToManyField (
1512
+ get_user_model (),
1513
+ related_name = "upvotes" ,
1514
+ help_text = "The number of upvotes a comment gets."
1515
+ )
1516
+ downvotes = models .ManyToManyField (
1517
+ get_user_model (),
1518
+ related_name = "downvotes" ,
1519
+ help_text = "The number of downvotes a comment gets."
1520
+ )
1521
+ section = models .ForeignKey (
1522
+ Section ,
1523
+ on_delete = models .CASCADE ,
1524
+ help_text = dedent (
1525
+ """
1526
+ The section with which a comment is associated. Section was chosen instead of topics for
1527
+ hosting comments because topics are SOFT STATE and are recomputed regularly.
1528
+ """
1529
+ ),
1530
+ null = True
1531
+ )
1532
+
1533
+ base = models .ForeignKey (
1534
+ "self" ,
1535
+ on_delete = models .SET_NULL , # redundant due to special deletion conditions
1536
+ null = True ,
1537
+ )
1538
+ parent = models .ForeignKey (
1539
+ "self" ,
1540
+ on_delete = models .SET_NULL , # similarly redundant
1541
+ null = True ,
1542
+ related_name = "children"
1543
+ )
1544
+ path = models .TextField (db_index = True )
1545
+
1546
+ def level (self ):
1547
+ return len (self .path .split ('.' ))
1548
+
1549
+ def delete (self , ** kwargs ):
1550
+ if Comment .objects .filter (parent_id = self ).exists ():
1551
+ self .text = "This comment has been removed."
1552
+ self .upvotes .clear ()
1553
+ self .downvotes .clear ()
1554
+ self .author = None
1555
+ self .save ()
1556
+ else :
1557
+ super ().delete (** kwargs )
1558
+ def __str__ (self ):
1559
+ return f"{ self .author } : { self .text } "
0 commit comments