diff --git a/seshat/apps/accounts/admin.py b/seshat/apps/accounts/admin.py index 67dd6d074..79096b61c 100644 --- a/seshat/apps/accounts/admin.py +++ b/seshat/apps/accounts/admin.py @@ -2,6 +2,8 @@ from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User +from .forms import SeshatExpertAdminForm + from .models import Profile, Seshat_Expert, Seshat_Task ######EMAIL_CONFIRMATION_BRANCH is the keyword that needs to be searched @@ -38,8 +40,63 @@ def get_inline_instances(self, request, obj=None): admin.site.unregister(User) admin.site.register(User, CustomUserAdmin) admin.site.register(Profile) -admin.site.register(Seshat_Expert) +#admin.site.unregister(Seshat_Expert) admin.site.register(Seshat_Task) +class SeshatExpertAdmin(admin.ModelAdmin): + """ + Custom admin for Seshat_Expert model. + """ + form = SeshatExpertAdminForm + + list_display = ('id', 'get_full_name', 'role', 'get_username', 'email', 'is_staff', 'is_active', 'last_login', 'date_joined') + list_filter = ('role',) # Add filters for roles + search_fields = ('user__username', 'user__first_name', 'user__last_name', 'user__email') # Enable search by user details + + ordering = ('user__last_name', 'user__first_name') # Order by name + + @admin.display(description='Full Name') + def get_full_name(self, obj): + """ + Returns the full name of the user. + """ + if obj.user.first_name and obj.user.last_name: + return f"{obj.user.first_name} {obj.user.last_name}" + return "N/A" + + @admin.display(description='Username') + def get_username(self, obj): + """ + Returns the username of the user. + """ + return obj.user.username + + @admin.display(description='Last login') + def last_login(self, obj): + return obj.user.last_login + + @admin.display(description='Joined') + def date_joined(self, obj): + return obj.user.date_joined + + @admin.display(description='Email') + def email(self, obj): + """ + Returns the email of the user. + """ + return obj.user.email + + @admin.display(description='Active') + def is_active(self, obj): + return obj.user.is_active + + @admin.display(description='Staff') + def is_staff(self, obj): + return obj.user.is_staff + + + +admin.site.register(Seshat_Expert, SeshatExpertAdmin) + diff --git a/seshat/apps/accounts/forms.py b/seshat/apps/accounts/forms.py index 5edc1e1c6..485af3682 100644 --- a/seshat/apps/accounts/forms.py +++ b/seshat/apps/accounts/forms.py @@ -1,6 +1,6 @@ from django import forms from django.db.models.base import Model -from django.forms import ModelForm +from django.forms import ModelForm, ModelChoiceField, Select from django.forms.widgets import Textarea from django.core.exceptions import ValidationError @@ -13,7 +13,6 @@ from django.contrib.auth.forms import UserCreationForm - class Seshat_TaskForm(forms.ModelForm): """ Form for adding or updating a task. @@ -108,4 +107,28 @@ def clean_email(self): username_parts = username.split('.') if len(username_parts) > 5: raise ValidationError("Email address contains too many dots in the username part.") - return email \ No newline at end of file + return email + + +class SeshatExpertAdminForm(ModelForm): + class Meta: + model = Seshat_Expert + fields = '__all__' + + # Override the user field + user = ModelChoiceField( + queryset=User.objects.all(), + widget=Select(attrs={'class': 'form-control'}), + label="User", + help_text="Select a user for this expert.", + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # Customize display of user dropdown + self.fields['user'].queryset = User.objects.filter(is_staff=True).order_by('id') + self.fields['user'].label_from_instance = lambda obj: f"{obj.id}: {obj.username} ({obj.first_name} {obj.last_name} - {obj.email})" + + + + diff --git a/seshat/apps/accounts/migrations/0014_alter_seshat_expert_role.py b/seshat/apps/accounts/migrations/0014_alter_seshat_expert_role.py new file mode 100644 index 000000000..eafd9db15 --- /dev/null +++ b/seshat/apps/accounts/migrations/0014_alter_seshat_expert_role.py @@ -0,0 +1,18 @@ +# Generated by Django 4.0.3 on 2024-12-04 17:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0013_alter_seshat_task_task_url'), + ] + + operations = [ + migrations.AlterField( + model_name='seshat_expert', + name='role', + field=models.CharField(blank=True, choices=[('Seshat Admin', 'Seshat Admin'), ('RA', 'Seshat Researcher'), ('Seshat Expert', 'Seshat Expert')], max_length=60, null=True), + ), + ] diff --git a/seshat/apps/accounts/models.py b/seshat/apps/accounts/models.py index 4c64e7f51..247a99cba 100644 --- a/seshat/apps/accounts/models.py +++ b/seshat/apps/accounts/models.py @@ -79,12 +79,20 @@ class Seshat_Expert(models.Model): Model representing a Seshat Expert. """ SESHATADMIN = 'Seshat Admin' - RA = 'RA' + RA = 'Researcher' SESHATEXPERT = 'Seshat Expert' + LR = 'Lead Researcher' + SD = 'Seshat Director' + PM = 'Project Manager' + ROLE_CHOICES = ( (SESHATADMIN, 'Seshat Admin'), - (RA, 'RA'), + (RA, 'Researcher'), (SESHATEXPERT, 'Seshat Expert'), + (LR, 'Lead Researcher'), + (SD, 'Seshat Director'), + (PM, 'Project Manager'), + ) user = models.ForeignKey(User, on_delete=models.CASCADE) role = models.CharField(max_length=60, diff --git a/seshat/apps/accounts/templates/account/profile.html b/seshat/apps/accounts/templates/account/profile.html index 2948cc701..e9c95314f 100644 --- a/seshat/apps/accounts/templates/account/profile.html +++ b/seshat/apps/accounts/templates/account/profile.html @@ -6,14 +6,7 @@
- - - +
diff --git a/seshat/apps/accounts/templates/account/seshat_expert_list.html b/seshat/apps/accounts/templates/account/seshat_expert_list.html new file mode 100644 index 000000000..48db38c23 --- /dev/null +++ b/seshat/apps/accounts/templates/account/seshat_expert_list.html @@ -0,0 +1,33 @@ + + + + + + Seshat Experts + + +

List of Seshat Experts

+ + + + + + + + + + {% for expert in experts %} + + + + + + {% empty %} + + + + {% endfor %} + +
NameRoleActions
{{ expert }} {{ expert.role }}
No experts found.
+ + diff --git a/seshat/apps/accounts/templates/registration/profile.html b/seshat/apps/accounts/templates/registration/profile.html index d375851bc..0e3bc8d99 100644 --- a/seshat/apps/accounts/templates/registration/profile.html +++ b/seshat/apps/accounts/templates/registration/profile.html @@ -35,19 +35,16 @@ .nav-tabs .nav-link:hover { font-weight: bold; } + + .expert_name { + border-radius: 5px; + }
-
+
- - - +
@@ -81,7 +78,6 @@
{{user.first_name}} {{user.last_name}}

-

Notifications:

@@ -122,56 +118,56 @@

Notifications:

{% if all_their_private_comments %}
- - - - - - - - +
CommentFull DiscussionLast Modified
+ {% for a_private_comment_part in all_their_private_comments %} {% if a_private_comment_part.is_done %} - - - - {% autoescape off %} - - {% endautoescape %} {% else %} - - - - {% autoescape off %} - - {% endautoescape %} {% endif %} @@ -193,56 +189,49 @@
No Notifications for you.
{% if comments_written_by_user %}
-
+ {% autoescape off %} {{a_private_comment_part.private_comment_owner}} +   {{a_private_comment_part.last_modified_date|naturaltime}} + + Full Discussion + + + +
+ {{a_private_comment_part.private_comment_part_text}}
{% endautoescape %}
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
+ {% autoescape off %} - {{a_private_comment_part.private_comment_owner}} + {{a_private_comment_part.private_comment_owner}} + +   {{a_private_comment_part.last_modified_date|naturaltime}} + + Full Discussion + + + +
+ + {% if a_private_comment_part.private_comment.general_polity_preceding_entity_related %} + {% endif %} + + +
{{a_private_comment_part.private_comment_part_text}} {% endautoescape %}
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
- - - - - - - +
CommentFull DiscussionLast Modified
+ {% for a_private_comment_part in comments_written_by_user %} {% if a_private_comment_part.is_done %} - - - - {% autoescape off %} - - {% endautoescape %} {% else %} - - - - {% autoescape off %} - - {% endautoescape %} {% endif %} @@ -264,56 +253,48 @@
No Notifications for you.
{% if all_done_private_comments %}
-
+ {% autoescape off %} {{a_private_comment_part.private_comment_owner}} - {{a_private_comment_part.private_comment_part_text}} +   {{a_private_comment_part.last_modified_date|naturaltime}} + Full Discussion + + + +
{{a_private_comment_part.private_comment_part_text}} {% endautoescape %} +
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
+ {% autoescape off %} - {{a_private_comment_part.private_comment_owner}} + {{a_private_comment_part.private_comment_owner}} + +   {{a_private_comment_part.last_modified_date|naturaltime}} + + Full Discussion + + + +
+ {{a_private_comment_part.private_comment_part_text}}
{% endautoescape %}
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
- - - - - - - +
CommentFull DiscussionLast Modified
+ {% for a_private_comment_part in all_done_private_comments %} {% if a_private_comment_part.is_done %} - - - - {% autoescape off %} - - {% endautoescape %} {% else %} - - - - {% autoescape off %} - - {% endautoescape %} {% endif %} diff --git a/seshat/apps/accounts/templates/seshat_expert_list.html b/seshat/apps/accounts/templates/seshat_expert_list.html new file mode 100644 index 000000000..dd5d81144 --- /dev/null +++ b/seshat/apps/accounts/templates/seshat_expert_list.html @@ -0,0 +1,113 @@ +{% extends "core/seshat-base.html" %} +{% load crispy_forms_tags %} +{% load mathfilters %} +{% load custom_filters %} + +{% load humanize %} + + +{% block content %} +
+

Seshat Research Network (Researchers and Experts)

+
+ {% autoescape off %} {{a_private_comment_part.private_comment_owner}} +   {{a_private_comment_part.last_modified_date|naturaltime}} + + Full Discussion + + +
{{a_private_comment_part.private_comment_part_text}}
{% endautoescape %}
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
+ {% autoescape off %} - {{a_private_comment_part.private_comment_owner}} + {{a_private_comment_part.private_comment_owner}} + +   {{a_private_comment_part.last_modified_date|naturaltime}} + + Full Discussion + + + +
{{a_private_comment_part.private_comment_part_text}}
{% endautoescape %}
- Full Discussion - - {{a_private_comment_part.last_modified_date|naturaltime}}
+ + + + + + + + + + + + + + + {% autoescape off %} + {% for expert in experts %} + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + {% endautoescape %} + + +
IDUIDFull NameEmailRoleGroupsPermissionsDate Joined
{{ expert.id }}{{ expert.user_id }} + {% if expert.user.first_name and expert.user.last_name %} + {{ expert.user.first_name }} {{ expert.user.last_name }} + {% else %} + N/A + {% endif %} + + {% if expert.user|is_user_real and expert.iser_id != 150 %} + {{ expert.user.email }} + {% else %} + - + {% endif %} + {{ expert.role }} + {% for group in expert.user.related_groups %} + {% if group.name == "Seshat Users First Round" %} + + {% elif group.name == "Chief Seshat Researchers" %} + + + {% elif group.name == "Chief Seshat External Experts" %} + + + {% elif group.name == "Chief Seshat Admins" %} + + {% else %} + {% if request.user.email == 'benam.mjd@gmail.com' %} + {{group.name}} + {% endif %} + + {% endif %} + {% endfor %} + + + + + {% for perm in expert.user.related_permissions %} + {% if perm.name == "Can add capital" %} + + + + + {% elif perm.name == "Can add seshat private comment part" %} + + {% else %} + {% if request.user.email == 'benamggg.mjd@gmail.com' %} + {{perm.name}} + {% endif %} + + {% endif %} + {% endfor %} + + + {{ expert.user.date_joined|date:"Y-m-d" }}
No experts found.
+
+ +{% endblock %} + diff --git a/seshat/apps/accounts/views.py b/seshat/apps/accounts/views.py index db4fc5dc8..e6274c845 100644 --- a/seshat/apps/accounts/views.py +++ b/seshat/apps/accounts/views.py @@ -178,6 +178,15 @@ def profile(request): all_facts = 0 all_tasks_given = [] user_profile_id = None + + a_private_comment_part = SeshatPrivateCommentPart.objects.first() + private_comment = a_private_comment_part.private_comment + + # Explore attributes + #print("##########################") + #print(dir(private_comment)) + + if request.user.is_authenticated: user_profile_id = request.user.profile.id my_user = Profile.objects.get(pk = user_profile_id) diff --git a/seshat/apps/core/forms.py b/seshat/apps/core/forms.py index 7f7ac647a..82465de77 100644 --- a/seshat/apps/core/forms.py +++ b/seshat/apps/core/forms.py @@ -584,4 +584,15 @@ class SeshatCommentPartForm2_UPGRADE(forms.Form): #formset = ReferenceFormSet2(prefix='refs') -#comment_order = forms.IntegerField(label='Do NOT Change This Number: ', required=False,) \ No newline at end of file +#comment_order = forms.IntegerField(label='Do NOT Change This Number: ', required=False,) + + +class ExpertCheckedForm(forms.Form): + verify = forms.BooleanField( + required=False, # Checkbox is optional + label="I have checked this coded record and would like to mark it as Expert Checked under my name.", + widget=forms.CheckboxInput(attrs={ + 'class': 'form-check-input', + 'style': 'transform: scale(1.5); margin-right: 10px;', # Adjust size and spacing + }) + ) \ No newline at end of file diff --git a/seshat/apps/core/management/commands/list_tables.py b/seshat/apps/core/management/commands/list_tables.py new file mode 100644 index 000000000..1d8857c46 --- /dev/null +++ b/seshat/apps/core/management/commands/list_tables.py @@ -0,0 +1,17 @@ +from django.core.management.base import BaseCommand +from django.apps import apps + +class Command(BaseCommand): + help = "List all database tables associated with the 'general' app models." + + def handle(self, *args, **kwargs): + # Get all models in the 'general' app + general_models = apps.get_app_config('general').get_models() + + # Extract table names from models + general_tables = [model._meta.db_table for model in general_models] + + # Print the tables + self.stdout.write("Tables for models in 'general' app:") + for table in general_tables: + self.stdout.write(f"'{table}', ") diff --git a/seshat/apps/core/management/commands/update_curators.py b/seshat/apps/core/management/commands/update_curators.py new file mode 100644 index 000000000..767684a35 --- /dev/null +++ b/seshat/apps/core/management/commands/update_curators.py @@ -0,0 +1,112 @@ +from django.core.management.base import BaseCommand +from django.db import connection +from django.apps import apps + +from seshat.apps.core.models import Seshat_Expert + + +from seshat.apps.general.models import Polity_expert, Polity_original_name, Polity_alternative_name + + +# GENERAL_TABLES = [ +# 'general_polity_research_assistant', +# 'general_polity_original_name', +# 'general_polity_alternative_name', +# 'general_polity_duration', +# 'general_polity_peak_years', +# 'general_polity_degree_of_centralization', +# 'general_polity_suprapolity_relations', +# 'general_polity_utm_zone', +# 'general_polity_capital', +# 'general_polity_language', +# 'general_polity_linguistic_family', +# 'general_polity_language_genus', +# 'general_polity_religion_genus', +# 'general_polity_religion_family', +# 'general_polity_religion', +# 'general_polity_relationship_to_preceding_entity', +# 'general_polity_preceding_entity', +# 'general_polity_succeeding_entity', +# 'general_polity_supracultural_entity', +# 'general_polity_scale_of_supracultural_interaction', +# 'general_polity_alternate_religion_genus', +# 'general_polity_alternate_religion_family', +# 'general_polity_alternate_religion', +# 'general_polity_expert', +# 'general_polity_editor', +# 'general_polity_religious_tradition', +# ] +class Command(BaseCommand): + help = "Update curators table with coded records based on polity experts" + + def handle(self, *args, **kwargs): + self.stdout.write("Fetching polities and experts...") + + # Fetch polity_id and expert mapping + polity_experts = Polity_expert.objects.all().values_list('polity_id', 'expert_id') + print(polity_experts) + for item in polity_experts: + try: + main_objects = Polity_alternative_name.objects.filter(polity_id=item[0]) # Replace with your filter criteria + + except: + print("BAD item: ", item) + continue + if main_objects: + new_curator = Seshat_Expert.objects.get(id=item[1]) # Replace with your filter criteria + + # Add the new curator to the ManyToManyField for all cases + for main_object in main_objects: + main_object.curator.add(new_curator) + + # Save the main object (optional since ManyToManyField updates are saved immediately) + main_object.save() + print("Yes") + + + self.stdout.write("Update completed.") + + +class Command(BaseCommand): + help = "Update curators table with coded records based on polity experts for all general app models" + + def handle(self, *args, **kwargs): + self.stdout.write("Fetching polities and experts...") + + # Fetch polity_id and expert mapping + polity_experts = Polity_expert.objects.all().values_list('polity_id', 'expert_id') + print("Polity Experts Mapping: ", polity_experts) + + # Get all models in the 'general' app + general_models = apps.get_app_config('general').get_models() + + for model in general_models: + if hasattr(model, 'curator') and hasattr(model, 'polity_id'): # Ensure the model has required fields + self.stdout.write(f"Processing model: {model.__name__}") + + for item in polity_experts: + try: + # Filter by polity_id + main_objects = model.objects.filter(polity_id=item[0]) + + except model.DoesNotExist: + self.stdout.write(f"No matching object found in {model.__name__} for polity_id {item[0]}") + continue + + # Get the expert + try: + new_curator = Seshat_Expert.objects.get(id=item[1]) + except Seshat_Expert.DoesNotExist: + self.stdout.write(f"No expert found with ID {item[1]}") + continue + # Add the new curator to the ManyToManyField for all cases + + for main_object in main_objects: + main_object.curator.add(new_curator) + + # Save the main object (optional since ManyToManyField updates are saved immediately) + main_object.save() + print("Yes") + self.stdout.write(f"Added curator {new_curator} to {model.__name__} with polity_id {item[0]}") + + self.stdout.write("Update completed.") \ No newline at end of file diff --git a/seshat/apps/core/models.py b/seshat/apps/core/models.py index 9c307e895..b63604bfe 100644 --- a/seshat/apps/core/models.py +++ b/seshat/apps/core/models.py @@ -36,37 +36,69 @@ def give_me_a_color_for_expert(value): str: A color for the expert. """ light_colors = [ - '#e6b8af', - '#f4cccc', - '#fce5cd', - '#fff2cc', - '#d9ead3', - '#d0e0e3', - '#c9daf8', - '#cfe2f3', - '#d9d2e9', - '#ead1dc', - '#dd7e6b', - '#ea9999', - '#f9cb9c', - '#ffe599', - '#b6d7a8', - '#a2c4c9', - '#a4c2f4', - '#9fc5e8', - '#b4a7d6', - '#d5a6bd', - '#cc4125', - '#e06666', - '#f6b26b', - '#ffd966', - '#93c47d', - '#76a5af', - '#6d9eeb', - '#6fa8dc', - '#8e7cc3', - '#c27ba0', + '#b86354', # Darker red tone of #e6b8af + '#cc6666', # Darker red tone of #f4cccc + '#d9a065', # Darker orange tone of #fce5cd + '#d6b656', # Darker yellow tone of #fff2cc + '#94b373', # Darker green tone of #d9ead3 + '#6c8488', # Darker blue-gray tone of #d0e0e3 + '#6a92d1', # Darker blue tone of #c9daf8 + '#7292a6', # Darker gray-blue tone of #cfe2f3 + '#8f78c3', # Darker purple tone of #d9d2e9 + '#c35b7f', # Darker pink-purple tone of #ead1dc + '#a93d2b', # Darker orange-red tone of #dd7e6b + '#b85454', # Darker red tone of #ea9999 + '#e09957', # Darker orange tone of #f9cb9c + '#d7b942', # Darker yellow tone of #ffe599 + '#79a659', # Darker green tone of #b6d7a8 + '#4e7476', # Darker teal tone of #a2c4c9 + '#4a7bbf', # Darker blue tone of #a4c2f4 + '#4e92b8', # Darker blue tone of #9fc5e8 + '#6c5ba3', # Darker purple tone of #b4a7d6 + '#a35f88', # Darker pink-purple tone of #d5a6bd + '#892f15', # Darker red tone of #cc4125 + '#a73f3f', # Darker red tone of #e06666 + '#b86b35', # Darker orange tone of #f6b26b + '#d6ac34', # Darker yellow tone of #ffd966 + '#5d8e52', # Darker green tone of #93c47d + '#43696d', # Darker teal tone of #76a5af + '#3d64b3', # Darker blue tone of #6d9eeb + '#4176a5', # Darker blue tone of #6fa8dc + '#654da6', # Darker purple tone of #8e7cc3 + '#8f5477', # Darker pink tone of #c27ba0 ] + # light_colors = [ + # '#e6b8af', + # '#f4cccc', + # '#fce5cd', + # '#fff2cc', + # '#d9ead3', + # '#d0e0e3', + # '#c9daf8', + # '#cfe2f3', + # '#d9d2e9', + # '#ead1dc', + # '#dd7e6b', + # '#ea9999', + # '#f9cb9c', + # '#ffe599', + # '#b6d7a8', + # '#a2c4c9', + # '#a4c2f4', + # '#9fc5e8', + # '#b4a7d6', + # '#d5a6bd', + # '#cc4125', + # '#e06666', + # '#f6b26b', + # '#ffd966', + # '#93c47d', + # '#76a5af', + # '#6d9eeb', + # '#6fa8dc', + # '#8e7cc3', + # '#c27ba0', + # ] index = int(value) % 30 return light_colors[index] @@ -223,7 +255,7 @@ def __str__(self) -> str: private_comment_parts = [] for private_comment_part in all_private_comment_parts: my_color = give_me_a_color_for_expert(private_comment_part.private_comment_owner.id) - private_comment_full_text = f'' + str(private_comment_part.private_comment_owner) + " " + private_comment_part.private_comment_part_text + "
" + private_comment_full_text = f'' + str(private_comment_part.private_comment_owner) + " " + private_comment_part.private_comment_part_text + "
" private_comment_parts.append(private_comment_full_text) if not private_comment_parts or private_comment_parts == [None]: to_be_shown = " Nothing " @@ -235,6 +267,103 @@ def __str__(self) -> str: to_be_shown = "EMPTY_PRIVATE_COMMENT" return f'{to_be_shown}' + def show_inline(self) -> str: + all_private_comment_parts = self.inner_private_comments_related.all().order_by('created_date') + if all_private_comment_parts: + private_comment_parts = [] + for private_comment_part in all_private_comment_parts: + # Assign a color for the owner + owner_color = give_me_a_color_for_expert(private_comment_part.private_comment_owner.id) + + # Create the owner badge + owner_badge = ( + f'' + f'{private_comment_part.private_comment_owner}' + ) + + # Assign colors to each reader and create reader badges + reader_badges = [] + for reader in private_comment_part.private_comment_reader.all(): + reader_color = give_me_a_color_for_expert(reader.id) # Use the same function for readers + reader_badge = ( + f'' + f'{reader}' + ) + reader_badges.append(reader_badge) + + # Combine the owner badge, reader badges, and comment text + readers_html = " " + " ".join(reader_badges) if reader_badges else " " + private_comment_full_text = ( + f"
{owner_badge} {readers_html} " + f" {private_comment_part.private_comment_part_text}
" + + ) + private_comment_parts.append(private_comment_full_text) + + if not private_comment_parts or private_comment_parts == [None]: + to_be_shown = " Nothing " + else: + to_be_shown = " ".join(private_comment_parts) + elif self.text and not all_private_comment_parts: + to_be_shown = "No Private Comments." + else: + to_be_shown = "EMPTY_PRIVATE_COMMENT" + return f'{to_be_shown}' + + def show_inline_short(self) -> str: + all_private_comment_parts = self.inner_private_comments_related.all().order_by('created_date') + if all_private_comment_parts: + private_comment_parts = [] + for private_comment_part in all_private_comment_parts: + # Assign a color for the owner + owner_color = give_me_a_color_for_expert(private_comment_part.private_comment_owner.id) + + # Create the owner badge + owner_badge = ( + f'' + f'{private_comment_part.private_comment_owner}' + ) + + # Assign colors to each reader and create reader badges + reader_badges = [] + for reader in private_comment_part.private_comment_reader.all(): + reader_color = give_me_a_color_for_expert(reader.id) # Use the same function for readers + reader_badge = ( + f'' + f'{reader}' + ) + reader_badges.append(reader_badge) + + # Combine the owner badge, reader badges, and comment text + readers_html = " " + " ".join(reader_badges) if reader_badges else " " + + if private_comment_part.private_comment_part_text.endswith("I would appreciate it if you could review it."): + private_comment_full_text = ( + f"
Review Request Sent By: {owner_badge} {readers_html}
" + ) + else: + private_comment_full_text = ( + f"
{owner_badge} {readers_html} " + f" {private_comment_part.private_comment_part_text}
" + ) + + private_comment_parts.append(private_comment_full_text) + + if not private_comment_parts or private_comment_parts == [None]: + to_be_shown = " Nothing " + else: + to_be_shown = " ".join(private_comment_parts) + elif self.text and not all_private_comment_parts: + to_be_shown = "No Private Comments." + else: + to_be_shown = "EMPTY_PRIVATE_COMMENT" + return f'{to_be_shown}' + + def get_absolute_url(self): """ Returns the url to access a particular instance of the model. @@ -1103,7 +1232,7 @@ class SeshatCommon(models.Model): drb_reviewed = models.BooleanField(null=True, blank=True, default=False) curator = models.ManyToManyField(Seshat_Expert, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)ss", blank=True,) - comment = models.ForeignKey(SeshatComment, on_delete=models.DO_NOTHING, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)s", null=True, blank=True) + comment = models.ForeignKey(SeshatComment, on_delete=models.SET_NULL, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)s", null=True, blank=True) private_comment = models.ForeignKey(SeshatPrivateComment, on_delete=models.DO_NOTHING, related_name="%(app_label)s_%(class)s_related", related_query_name="%(app_label)s_%(class)s", null=True, blank=True) class Meta: diff --git a/seshat/apps/core/templates/core/description_snippet.html b/seshat/apps/core/templates/core/description_snippet.html new file mode 100644 index 000000000..bd2378ac2 --- /dev/null +++ b/seshat/apps/core/templates/core/description_snippet.html @@ -0,0 +1,19 @@ +{% load custom_filters %} + +{% autoescape off %} +{% if obj.description %} + {{ obj.description|make_references_look_nicer }} +
+{% else %} + - +{% endif %} +{% if obj.comment and obj.comment.id > 1 %} + {{ obj.comment }} + + + EDIT + + +{% endif %} +{% endautoescape %} diff --git a/seshat/apps/core/templates/core/detail_base_for_new_approach.html b/seshat/apps/core/templates/core/detail_base_for_new_approach.html index 0b0e24b64..6eee4a9de 100644 --- a/seshat/apps/core/templates/core/detail_base_for_new_approach.html +++ b/seshat/apps/core/templates/core/detail_base_for_new_approach.html @@ -113,17 +113,17 @@
-
+
- Step 1: Insert the data + 1: Insert the data
- Step 2: Add the first chunk of the description + 2: Add the first chunk of the description
- Step 3: Expand the description + 3: Expand the description
diff --git a/seshat/apps/core/templates/core/detail_base_with_desc_and_citations.html b/seshat/apps/core/templates/core/detail_base_with_desc_and_citations.html index 019e7ebed..6ea5bb156 100644 --- a/seshat/apps/core/templates/core/detail_base_with_desc_and_citations.html +++ b/seshat/apps/core/templates/core/detail_base_with_desc_and_citations.html @@ -81,9 +81,7 @@
Section: {{object.section}}

Fact Under Review:

- +
{% endblock %} \ No newline at end of file diff --git a/seshat/apps/core/templates/core/form_base.html b/seshat/apps/core/templates/core/form_base.html index b891ffb53..d983dffc7 100644 --- a/seshat/apps/core/templates/core/form_base.html +++ b/seshat/apps/core/templates/core/form_base.html @@ -100,10 +100,10 @@
{{my_exp}}
- {% if request.user.is_staff %} + {% if request.user.is_authenticated and 'core.add_capital' in request.user.get_all_permissions %}
- You are a Seshat Expert; you can enter a fact. + You are a Seshat Researcher; you can insert a new Record to the Database.