diff options
author | CruiseDevice | 2020-04-13 16:45:42 +0530 |
---|---|---|
committer | CruiseDevice | 2020-04-13 16:45:42 +0530 |
commit | 0e6c7d589114450d5cd1bc581ee1692c235f1a73 (patch) | |
tree | 3e1749b9695a708ac65deb5953d4913250335522 | |
parent | 2a9f81cb32acfd7a2efc18f58c4529b39ce4061b (diff) | |
download | online_test-0e6c7d589114450d5cd1bc581ee1692c235f1a73.tar.gz online_test-0e6c7d589114450d5cd1bc581ee1692c235f1a73.tar.bz2 online_test-0e6c7d589114450d5cd1bc581ee1692c235f1a73.zip |
Add feature for uploading images
-rw-r--r-- | requirements/requirements-common.txt | 1 | ||||
-rw-r--r-- | yaksh/forms.py | 43 | ||||
-rw-r--r-- | yaksh/models.py | 46 | ||||
-rw-r--r-- | yaksh/static/yaksh/css/custom.css | 9 | ||||
-rw-r--r-- | yaksh/templates/yaksh/course_forum.html | 14 | ||||
-rw-r--r-- | yaksh/templates/yaksh/thread_comments.html | 11 | ||||
-rw-r--r-- | yaksh/urls.py | 1 | ||||
-rw-r--r-- | yaksh/views.py | 55 |
8 files changed, 130 insertions, 50 deletions
diff --git a/requirements/requirements-common.txt b/requirements/requirements-common.txt index d1fed93..80dadb3 100644 --- a/requirements/requirements-common.txt +++ b/requirements/requirements-common.txt @@ -10,3 +10,4 @@ coverage ruamel.yaml==0.15.23 markdown==2.6.9 pygments==2.2.0 +Pillow
\ No newline at end of file diff --git a/yaksh/forms.py b/yaksh/forms.py index 52ef75d..e66c898 100644 --- a/yaksh/forms.py +++ b/yaksh/forms.py @@ -1,7 +1,7 @@ from django import forms from yaksh.models import ( get_model_class, Profile, Quiz, Question, Course, QuestionPaper, Lesson, - LearningModule, TestCase, languages, question_types + LearningModule, TestCase, languages, question_types, Thread, Comment ) from grades.models import GradingSystem from django.contrib.auth import authenticate @@ -552,3 +552,44 @@ class TestcaseForm(forms.ModelForm): class Meta: model = TestCase fields = ["type"] + + +class ThreadForm(forms.ModelForm): + class Meta: + model = Thread + fields = ["title", "description", "image"] + widgets = { + 'title': forms.TextInput( + attrs = { + 'class': 'form-control' + } + ), + 'description': forms.Textarea( + attrs = { + 'class': 'form-control' + } + ), + 'image': forms.FileInput( + attrs = { + 'class': 'form-control-file' + } + ) + } + + +class CommentForm(forms.ModelForm): + class Meta: + model = Comment + fields = ["description", "image"] + widgets = { + 'description': forms.Textarea( + attrs = { + 'class': 'form-control' + } + ), + 'image': forms.FileInput( + attrs = { + 'class': 'form-control-file' + } + ) + } diff --git a/yaksh/models.py b/yaksh/models.py index 83c644a..f9878e4 100644 --- a/yaksh/models.py +++ b/yaksh/models.py @@ -11,6 +11,7 @@ from collections import Counter, defaultdict from django.db import models from django.contrib.auth.models import User, Group, Permission +from django.core.exceptions import ValidationError from django.contrib.contenttypes.models import ContentType from taggit.managers import TaggableManager from django.utils import timezone @@ -233,6 +234,18 @@ def render_template(template_path, data=None): return render +def validate_image(image): + file_size = image.file.size + limit_mb = 30 + if file_size > limit_mb * 1024 * 1024: + raise ValidationError("Max size of file is {0} MB".format(limit_mb)) + + +def get_image_dir(instance, filename): + return os.sep.join(( + 'thread_%s' % (instance), filename + )) + ############################################################################### class CourseManager(models.Manager): @@ -2634,16 +2647,21 @@ class TestCaseOrder(models.Model): order = models.TextField() ############################################################################## -class Thread(models.Model): +class ForumBase(models.Model): uid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False) creator = models.ForeignKey(User, on_delete=models.CASCADE) - title = models.CharField(max_length=200) description = models.TextField() - course = models.ForeignKey(Course, - on_delete=models.CASCADE, related_name='thread') created_at = models.DateTimeField(auto_now_add=True) modified_at = models.DateTimeField(auto_now=True) - # image = models.ImageField(upload_to='images/%y/%m/%d', blank=True) + image = models.ImageField(upload_to=get_image_dir, blank=True, + null=True, validators=[validate_image]) + active = models.BooleanField(default=True) + + +class Thread(ForumBase): + title = models.CharField(max_length=200) + course = models.ForeignKey(Course, + on_delete=models.CASCADE, related_name='thread') def __str__(self): return self.title @@ -2654,20 +2672,12 @@ class Thread(models.Model): def get_comments_count(self): return self.comment.count() -############################################################################## -class Comment(models.Model): - uid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False) - user = models.ForeignKey(User, on_delete=models.CASCADE) - thread = models.ForeignKey(Thread, + +class Comment(ForumBase): + thread_field = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name='comment') - body = models.TextField() - created_at = models.DateTimeField(auto_now_add=True) - modified_at = models.DateTimeField(auto_now=True) - active = models.BooleanField(default=True) #make it false if improper comment - # image = models.ImageField(upload_to='images/%y/%m/%d', blank=True) - def __str__(self): - return 'Comment by {0}: \n {1}'.format(self.user.username, - self.thread.title) + return 'Comment by {0}: {1}'.format(self.creator.username, + self.thread_field.title) diff --git a/yaksh/static/yaksh/css/custom.css b/yaksh/static/yaksh/css/custom.css index 63ee455..d6b9bdc 100644 --- a/yaksh/static/yaksh/css/custom.css +++ b/yaksh/static/yaksh/css/custom.css @@ -97,3 +97,12 @@ body, .dropdown-menu { min-height: 100vh; transition: all 0.3s; } + +/* --------------------------------------------------- + Forum STYLE +----------------------------------------------------- */ + +.comment_image { + width: 350px; + height: 350px; +}
\ No newline at end of file diff --git a/yaksh/templates/yaksh/course_forum.html b/yaksh/templates/yaksh/course_forum.html index b0c7024..4741ae0 100644 --- a/yaksh/templates/yaksh/course_forum.html +++ b/yaksh/templates/yaksh/course_forum.html @@ -23,13 +23,10 @@ <button type="button" class="close" data-dismiss="modal">×</button> </div> <div class="modal-body"> - <form action="." method="POST"> + <form action="." method="POST" enctype='multipart/form-data'> <div class="form-group"> {% csrf_token %} - <label>Title: </label> - <input name='title' type="text" class="form-control" required> - <label>Description: </label> - <textarea class="form-control" name="description" id="" cols="45" rows="3" required></textarea> + {{form}} </div> <input type="submit" class="btn btn-primary" value="Create Thread"> </form> @@ -50,12 +47,17 @@ <div class="card"> <div class="card-body"> <a href="{% url "yaksh:thread_comments" course.id thread.uid %}">{{thread.title}}</a> + <br> + {% if thread.image %} + <img src="{{thread.image.url}}" class="thread_image"> + {% endif %} <p class="pull-right"><small>{{thread.get_comments_count}}{% if thread.get_comments_count > 1 %} replies{% else %} reply{% endif %}</small></p> </div> <div class="card-footer"> {% with thread.get_last_comment as last_comment %} - <small> {% if thread.creator.profile.is_moderator %} INSTRUCTOR CREATED {% endif %} Last Post by: <strong>{{last_comment.user}}</strong> . {{last_comment.modified_at|naturaltime}}</small> + <small> {% if thread.creator.profile.is_moderator %} INSTRUCTOR CREATED {% endif %} Last Post by: <strong>{{last_comment.creator}}</strong> . {{last_comment.modified_at|naturaltime}}</small> {% endwith %} + <small><a href="{% url "yaksh:delete_thread" course.id thread.uid %}" class="pull-right">Delete</i></a></small> </div> </div> <br> diff --git a/yaksh/templates/yaksh/thread_comments.html b/yaksh/templates/yaksh/thread_comments.html index 245c363..ab0ade9 100644 --- a/yaksh/templates/yaksh/thread_comments.html +++ b/yaksh/templates/yaksh/thread_comments.html @@ -23,7 +23,12 @@ <div class="card-header">Comments:</div> {% for comment in comments %} <div class="card-body"> - <p>{{comment.body}}</p> + {% if comment.image %} + <a href="{{comment.image.url}}" target="_blank"> + <img src="{{comment.image.url}}" class='comment_image'> + </a> + {% endif %} + <p>{{comment.description}}</p> <small class="pull-right">by: <strong>{{comment.user.username}} </strong>. {{comment.created_at}}</small> </div> <hr> @@ -34,10 +39,10 @@ </div> <br> <div> - <form action="{% url "yaksh:thread_comments" thread.course.id thread.uid %}" method="POST"> + <form action="{% url "yaksh:thread_comments" thread.course.id thread.uid %}" method="POST" enctype='multipart/form-data'> <div class="form-group"> {% csrf_token %} - <textarea class="form-control" name="comment" id="" cols="55" rows="5" required></textarea> + {{form}} </div> <input type="submit" value="Submit" class="btn btn-primary"> </form> diff --git a/yaksh/urls.py b/yaksh/urls.py index 47cfad4..a8aa224 100644 --- a/yaksh/urls.py +++ b/yaksh/urls.py @@ -61,6 +61,7 @@ urlpatterns = [ views.course_modules, name='course_modules'), url(r'^forum/(?P<course_id>\d+)/$', views.course_forum, name='course_forum'), url(r'^forum/(?P<course_id>\d+)/thread/(?P<uuid>[0-9a-f-]+)/', views.thread_comments, name='thread_comments'), + url(r'^forum/(?P<course_id>\d+)/thread/(?P<uuid>[0-9a-f-]+)/delete/', views.delete_thread, name='delete_thread'), url(r'^manage/$', views.prof_manage, name='manage'), url(r'^manage/addquestion/$', views.add_question, name="add_question"), url(r'^manage/addquestion/(?P<question_id>\d+)/$', views.add_question, diff --git a/yaksh/views.py b/yaksh/views.py index 9350f0a..afa21df 100644 --- a/yaksh/views.py +++ b/yaksh/views.py @@ -44,7 +44,7 @@ from yaksh.forms import ( QuestionFilterForm, CourseForm, ProfileForm, UploadFileForm, FileForm, QuestionPaperForm, LessonForm, LessonFileForm, LearningModuleForm, ExerciseForm, TestcaseForm, - SearchFilterForm + SearchFilterForm, ThreadForm, CommentForm ) from yaksh.settings import SERVER_POOL_PORT, SERVER_HOST_NAME from .settings import URL_ROOT @@ -3193,42 +3193,53 @@ def download_course_progress(request, course_id): return response +@login_required +@email_verified def course_forum(request, course_id): user = request.user course = get_object_or_404(Course, id=course_id) - threads = course.thread.all().order_by('modified_at') + threads = course.thread.all().order_by('-modified_at') if request.method == "POST": - title = request.POST['title'] - description = request.POST['description'] - if title and description: - new_thread = Thread.objects.create(title=title, - description=description, - creator=user, course=course) + form = ThreadForm(request.POST, request.FILES) + if form.is_valid(): + new_thread = form.save(commit=False) + new_thread.creator = user + new_thread.course = course new_thread.save() - return render(request, 'yaksh/thread_comments.html', { - 'thread': new_thread, - 'course': course, - 'user': user, - }) + return redirect('yaksh:thread_comments', + course_id=course.id, uuid=new_thread.uid) + else: + form = ThreadForm() return render(request, 'yaksh/course_forum.html', { 'user': user, 'course': course, - 'threads': threads + 'threads': threads, + 'form': form }) +@login_required +@email_verified def thread_comments(request, course_id, uuid): thread = get_object_or_404(Thread, uid=uuid) comments = thread.comment.filter(active=True) + form = CommentForm() if request.method == "POST": - comment = request.POST.get('comment') - if comment is not None: - new_comment = Comment.objects.create(thread=thread, - body=comment, - user=request.user) + form = CommentForm(request.POST, request.FILES) + if form.is_valid(): + new_comment = form.save(commit=False) + new_comment.creator=request.user + new_comment.thread_field=thread new_comment.save() - return HttpResponseRedirect(request.path_info) + return redirect(request.path_info) return render(request, 'yaksh/thread_comments.html', { 'thread': thread, - 'comments': comments - })
\ No newline at end of file + 'comments': comments, + 'form': form + }) + + +@login_required +@email_verified +def delete_thread(request, course_id, uuid): + thread = get_object_or_404(Thread, uid=uuid) |