from django.db import models from django.shortcuts import get_object_or_404 from django.template import Template, Context from django.utils.safestring import mark_safe from .behaviors import validate_file_extension, validate_max_file_size from commentaries.models import Commentary from scipost.models import TimeStampedModel, Contributor from submissions.models import Submission, Report from theses.models import ThesisLink COMMENT_CATEGORIES = ( ('ERR', 'erratum'), ('REM', 'remark'), ('QUE', 'question'), ('ANS', 'answer to question'), ('OBJ', 'objection'), ('REP', 'reply to objection'), ('VAL', 'validation or rederivation'), ('LIT', 'pointer to related literature'), ('SUG', 'suggestion for further work'), ) COMMENT_STATUS = ( (1, 'vetted'), (0, 'not yet vetted (pending)'), (-1, 'rejected (unclear)'), (-2, 'rejected (incorrect)'), (-3, 'rejected (not useful)'), ) comment_status_dict = dict(COMMENT_STATUS) class Comment(TimeStampedModel): """ A Comment is an unsollicited note, submitted by a Contributor, on a particular publication or in reply to an earlier Comment. """ status = models.SmallIntegerField(default=0) vetted_by = models.ForeignKey(Contributor, blank=True, null=True, on_delete=models.CASCADE, related_name='comment_vetted_by') file_attachment = models.FileField(upload_to='comments/%Y/%m/%d/', blank=True, validators=[validate_file_extension, validate_max_file_size]) # a Comment is either for a Commentary or Submission or a ThesisLink. commentary = models.ForeignKey(Commentary, blank=True, null=True, on_delete=models.CASCADE) submission = models.ForeignKey(Submission, blank=True, null=True, on_delete=models.CASCADE) thesislink = models.ForeignKey(ThesisLink, blank=True, null=True, on_delete=models.CASCADE) is_author_reply = models.BooleanField(default=False) in_reply_to_comment = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE) in_reply_to_report = models.ForeignKey(Report, blank=True, null=True, on_delete=models.CASCADE) author = models.ForeignKey(Contributor, default=1, on_delete=models.CASCADE) anonymous = models.BooleanField(default=False, verbose_name='Publish anonymously') # Categories: is_cor = models.BooleanField(default=False, verbose_name='correction/erratum') is_rem = models.BooleanField(default=False, verbose_name='remark') is_que = models.BooleanField(default=False, verbose_name='question') is_ans = models.BooleanField(default=False, verbose_name='answer to question') is_obj = models.BooleanField(default=False, verbose_name='objection') is_rep = models.BooleanField(default=False, verbose_name='reply to objection') is_val = models.BooleanField(default=False, verbose_name='validation or rederivation') is_lit = models.BooleanField(default=False, verbose_name='pointer to related literature') is_sug = models.BooleanField(default=False, verbose_name='suggestion for further work') comment_text = models.TextField() remarks_for_editors = models.TextField(default='', blank=True, verbose_name='optional remarks for the Editors only') date_submitted = models.DateTimeField('date submitted') # Opinions nr_A = models.PositiveIntegerField(default=0) in_agreement = models.ManyToManyField(Contributor, related_name='in_agreement', blank=True) nr_N = models.PositiveIntegerField(default=0) in_notsure = models.ManyToManyField(Contributor, related_name='in_notsure', blank=True) nr_D = models.PositiveIntegerField(default=0) in_disagreement = models.ManyToManyField(Contributor, related_name='in_disagreement', blank=True) def __str__(self): return ('by ' + self.author.user.first_name + ' ' + self.author.user.last_name + ' on ' + self.date_submitted.strftime('%Y-%m-%d') + ', ' + self.comment_text[:30]) def update_opinions(self, contributor_id, opinion): contributor = get_object_or_404(Contributor, pk=contributor_id) self.in_agreement.remove(contributor) self.in_notsure.remove(contributor) self.in_disagreement.remove(contributor) if opinion == 'A': self.in_agreement.add(contributor) elif opinion == 'N': self.in_notsure.add(contributor) elif opinion == 'D': self.in_disagreement.add(contributor) self.nr_A = self.in_agreement.count() self.nr_N = self.in_notsure.count() self.nr_D = self.in_disagreement.count() self.save() def opinions_as_ul(self): template = Template(''' <ul class="opinionsDisplay"> <li style="background-color: #000099">Agree {{ nr_A }}</li> <li style="background-color: #555555">Not sure {{ nr_N }}</li> <li style="background-color: #990000">Disagree {{ nr_D }}</li> </ul> ''') context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D}) return template.render(context) def opinions_as_ul_tiny(self): template = Template(''' <ul class="opinionsDisplay"> <li style="background-color: #000099; font-size: 8px; padding: 2px;">Agree {{ nr_A }}</li> <li style="background-color: #555555; font-size: 8px; padding: 2px;">Not sure {{ nr_N }}</li> <li style="background-color: #990000; font-size: 8px; padding: 2px;">Disagree {{ nr_D }}</li> </ul> ''') context = Context({'nr_A': self.nr_A, 'nr_N': self.nr_N, 'nr_D': self.nr_D}) return template.render(context) def print_identifier(self): # for display output = '<div class="commentid">\n' output += '<h3><a id="comment_id{{ id }}"></a>' context = Context({'id': self.id}) if self.is_author_reply: output += 'Author ' if not self.anonymous: output += (' <a href="/contributor/{{ author_id }}">' + '{{ first_name }} {{ last_name }}</a> on ') context['author_id'] = self.author.id context['first_name'] = self.author.user.first_name context['last_name'] = self.author.user.last_name output += '{{ date_comment_submitted }}' context['date_comment_submitted'] = self.date_submitted.strftime("%Y-%m-%d") if self.in_reply_to_comment: output += (' (in reply to <a href="#comment_id{{ in_reply_to_comment_id }}">' '{{ in_reply_to_comment_first_name }} ' '{{ in_reply_to_comment_last_name }} on ' '{{ in_reply_to_comment_date }}</a>)') context['in_reply_to_comment_id'] = self.in_reply_to_comment_id context['in_reply_to_comment_first_name'] = (self.in_reply_to_comment .author.user.first_name) context['in_reply_to_comment_last_name'] = (self.in_reply_to_comment .author.user.last_name) context['in_reply_to_comment_date'] = (self.in_reply_to_comment .date_submitted.strftime("%Y-%m-%d")) elif self.in_reply_to_report: output += ' (in reply to <a href="#report_id{{ in_reply_to_report_id }}">' context['in_reply_to_report_id'] = self.in_reply_to_report_id if not self.in_reply_to_report.anonymous: output += '{{ in_reply_to_report_first_name }} {{ in_reply_to_report_last_name}}' context['in_reply_to_report_first_name'] = (self.in_reply_to_report .author.user.first_name) context['in_reply_to_report_last_name'] = (self.in_reply_to_report .author.user.last_name) else: output += 'Report {{ in_reply_to_report_id }}' context['in_reply_to_report_id'] = self.in_reply_to_report_id output += ' on {{ date_report_submitted }}</a>)' context['date_report_submitted'] = self.in_reply_to_report.date_submitted.strftime( "%Y-%m-%d") output += '</h3></div>' template = Template(output) return template.render(context) def print_identifier_for_vetting(self): # for display, same as print_identifier but named even if anonymous, not linked output = '<div class="commentid">\n' output += '<h3>' context = Context() if self.is_author_reply: output += 'Author ' output += ' <a href="/contributor/{{ author_id }}">{{ first_name }} {{ last_name }}</a>' context['author_id'] = self.author.id context['first_name'] = self.author.user.first_name context['last_name'] = self.author.user.last_name output += ' on {{ date_submitted }}' context['date_submitted'] = self.date_submitted.strftime("%Y-%m-%d") if self.in_reply_to_comment: output += (' (in reply to <a href="#comment_id{{ in_reply_to_comment_id }}">' '{{ in_reply_to_comment_first_name }} {{ in_reply_to_comment_last_name }} ' 'on {{ in_reply_to_comment_date }}</a>)') context['in_reply_to_comment_id'] = self.in_reply_to_comment_id context['in_reply_to_comment_first_name'] = (self.in_reply_to_comment .author.user.first_name) context['in_reply_to_comment_last_name'] = (self.in_reply_to_comment .author.user.last_name) context['in_reply_to_comment_date'] = (self.in_reply_to_comment .date_submitted.strftime("%Y-%m-%d")) elif self.in_reply_to_report: output += ' (in reply to <a href="#report_id{{ in_reply_to_report_id }}">' context['in_reply_to_report_id'] = self.in_reply_to_report_id if not self.in_reply_to_report.anonymous: output += '{{ in_reply_to_report_first_name }} {{ in_reply_to_report_last_name}}' context['in_reply_to_report_first_name'] = (self.in_reply_to_report .author.user.first_name) context['in_reply_to_report_last_name'] = (self.in_reply_to_report .author.user.last_name) else: output += 'Report {{ in_reply_to_report_id }}' context['in_reply_to_report_id'] = self.in_reply_to_report_id output += '</a> on {{ date_submitted }})' context['date_submitted'] = self.in_reply_to_report.date_submitted.strftime("%Y-%m-%d") output += '</h3></div>' template = Template(output) return template.render(context) def header_as_li(self): # for search lists header = '<li>' # header += '<div class="flex-container"><div class="flex-whitebox0">' header += 'Nr {{ id }}' context = Context({'id': self.id}) header += ', <div class="opinionsDisplay">' + self.opinions_as_ul_tiny() + '</div>' if self.status <= 0: header += (', status: <span style="color:red">' + comment_status_dict[self.status] + '</span>') text_cut = self.comment_text[:50] if len(self.comment_text) > 50: text_cut += '...' context['id'] = self.id context['text_cut'] = text_cut context['date_submitted'] = self.date_submitted.strftime("%Y-%m-%d") header += ': ' if not self.anonymous: header += (' <a href="/contributor/{{ author_id }}">' '{{ first_name }} {{ last_name }}</a>, ') context['author_id'] = self.author.id context['first_name'] = self.author.user.first_name context['last_name'] = self.author.user.last_name if self.submission is not None: header += ('<a href="/submission/{{ arxiv_identifier_w_vn_nr }}#comment_id{{ id }}">' ' \"{{ text_cut }}\"</a><p>submitted on {{ date_submitted }}') header += (' in submission on <a href="/submission/{{ arxiv_identifier_w_vn_nr }}"' ' class="pubtitleli">{{ submission_title }}</a> by ' '{{ submission_author_list }}</p>') context['arxiv_identifier_w_vn_nr'] = self.submission.arxiv_identifier_w_vn_nr context['submission_title'] = self.submission.title context['submission_author_list'] = self.submission.author_list if self.commentary is not None: header += ('<a href="/commentary/{{ commentary_url }}#comment_id{{ id }}">' ' \"{{ text_cut }}\"</a><p>submitted on {{ date_submitted }}') header += (' in commentary on <a href="/commentary/{{ commentary_url }}"' ' class="pubtitleli">' '{{ commentary_pub_title }}</a> by {{ commentary_author_list }}</p>') context['commentary_url'] = self.commentary.arxiv_or_DOI_string context['commentary_pub_title'] = self.commentary.pub_title context['commentary_author_list'] = self.commentary.author_list if self.thesislink is not None: header += ('<a href="/thesis/{{ thesislink_id }}#comment_id{{ id }}">' ' \"{{ text_cut }}\"</a><p>submitted on {{ date_submitted }}') header += (' in thesislink on ' '<a href="/thesis/{{ thesislink_id }}" class="pubtitleli">' '{{ thesislink_title }}</a> by {{ thesislink_author }}</p>') context['thesislink_id'] = self.thesislink.id context['thesislink_title'] = self.thesislink.title context['thesislink_author'] = self.thesislink.author # header += '</div></div>' header += '</li>' template = Template(header) return template.render(context) def simple_header_as_li(self): # for Lists header = '<li>' # header += '<div class="flex-container"><div class="flex-whitebox0">' context = Context({}) text_cut = self.comment_text[:30] if len(self.comment_text) > 30: text_cut += '...' context['text_cut'] = text_cut if not self.anonymous: header += ' <a href="/contributor/{{ author_id }}">{{ first_name }} {{ last_name }}</a>, ' context['author_id'] = self.author.id context['first_name'] = self.author.user.first_name context['last_name'] = self.author.user.last_name if self.submission is not None: header += ('<a href="/submission/{{ arxiv_identifier_w_vn_nr }}#comment_id{{ id }}"> ' '\"{{ text_cut }}\"</a>' ' in submission on <a href="/submission/{{ arxiv_identifier_w_vn_nr }}" class="pubtitleli">' '{{ submission_title }}</a> by {{ submission_author_list }}</p>') context['arxiv_identifier_w_vn_nr'] = self.submission.arxiv_identifier_w_vn_nr context['submission_title'] = self.submission.title context['submission_author_list'] = self.submission.author_list if self.commentary is not None: header += ('<a href="/commentary/{{ commentary_url }}#comment_id{{ id }}"> ' '\"{{ text_cut }}\"</a>' ' in commentary on <a href="/commentary/{{ commentary_url }}" class="pubtitleli">' '{{ commentary_pub_title }}</a> by {{ commentary_author_list }}</p>') context['commentary_url'] = self.commentary.arxiv_or_DOI_string context['commentary_pub_title'] = self.commentary.pub_title context['commentary_author_list'] = self.commentary.author_list if self.thesislink is not None: header += '<a href="/thesis/{{ thesislink_id }}#comment_id{{ id }}"> \"{{ text_cut }}\"</a>' header += (' in thesislink on ' '<a href="/thesis/{{ thesislink_id }}" class="pubtitleli">' '{{ thesislink_title }}</a> by {{ thesislink_author }}</p>') context['thesislink_id'] = self.thesislink.id context['thesislink_title'] = self.thesislink.title context['thesislink_author'] = self.thesislink.author # header += '</div></div>' header += '</li>' template = Template(header) return template.render(context) def categories_as_ul(self): output = '<div class="commentcategorydisplay"><h4>Category:</h4><ul>' if self.is_rem: output += '<li>remark</li>' if self.is_que: output += '<li>question</li>' if self.is_ans: output += '<li>answer to question</li>' if self.is_obj: output += '<li>objection</li>' if self.is_rep: output += '<li>reply to objection</li>' if self.is_cor: output += '<li>correction</li>' if self.is_val: output += '<li>validation or rederivation</li>' if self.is_lit: output += '<li>pointer to related literature</li>' if self.is_sug: output += '<li>suggestion for further work</li>' output += '</ul></div>' return mark_safe(output)