fix: conflicts

This commit is contained in:
pateljannat
2021-05-07 12:08:51 +05:30
34 changed files with 514 additions and 319 deletions

View File

@@ -1,6 +1,5 @@
{
"actions": [],
"autoname": "field:title",
"creation": "2021-03-18 19:37:34.614796",
"doctype": "DocType",
"editable_grid": 1,
@@ -51,10 +50,12 @@
"label": "Description"
},
{
"default": "Public",
"fieldname": "visibility",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Visibility",
"options": "\nPublic\nUnlisted\nPrivate"
"options": "Public\nUnlisted\nPrivate"
},
{
"fieldname": "membership",
@@ -63,16 +64,19 @@
"options": "\nOpen\nRestricted\nInvite Only\nClosed"
},
{
"default": "Active",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Status",
"options": "\nActive\nInactive"
"options": "Active\nInactive"
},
{
"default": "Ready",
"fieldname": "stage",
"fieldtype": "Select",
"label": "Stage",
"options": "\nReady\nIn Progress\nCompleted\nCancelled"
"options": "Ready\nIn Progress\nCompleted\nCancelled"
},
{
"fieldname": "column_break_3",
@@ -122,7 +126,7 @@
"link_fieldname": "batch"
}
],
"modified": "2021-04-30 09:52:18.941276",
"modified": "2021-05-06 05:46:38.469120",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Batch",

View File

@@ -8,6 +8,7 @@ from frappe.model.document import Document
from community.www.courses.utils import get_member_with_email
from frappe import _
from community.lms.doctype.lms_batch_membership.lms_batch_membership import create_membership
from community.query import find, find_all
class LMSBatch(Document):
def validate(self):
@@ -29,15 +30,20 @@ class LMSBatch(Document):
self.code = short_code + str(len(course_batches) + 1)
def get_mentors(self):
mentors = []
memberships = frappe.get_all(
"LMS Batch Membership",
{"batch": self.name, "member_type": "Mentor"},
["member"])
for membership in memberships:
member = frappe.db.get_value("Community Member", membership.member, ["full_name", "photo", "abbr"], as_dict=1)
mentors.append(member)
return mentors
member_names = [m['member'] for m in memberships]
return find_all("Community Member", name=["IN", member_names])
def is_member(self, email):
"""Checks if a person is part of a batch.
"""
member = find("Community Member", email=email)
return member and frappe.db.exists(
"LMS Batch Membership",
{"batch": self.name, "member": member.name})
@frappe.whitelist()
def get_messages(batch):

View File

@@ -2,14 +2,12 @@
"actions": [],
"allow_guest_to_view": 1,
"allow_rename": 1,
"autoname": "field:title",
"creation": "2021-03-01 16:49:33.622422",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"title",
"slug",
"is_published",
"column_break_3",
"short_code",
@@ -43,14 +41,6 @@
"fieldtype": "Data",
"label": "Short Code"
},
{
"description": "The slug of the course. Autogenerated from the title if not specified.",
"fieldname": "slug",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Slug",
"unique": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
@@ -87,9 +77,14 @@
"group": "Mentors",
"link_doctype": "LMS Course Mentor Mapping",
"link_fieldname": "course"
},
{
"group": "Mentors",
"link_doctype": "LMS Mentor Request",
"link_fieldname": "course"
}
],
"modified": "2021-05-03 05:52:30.396824",
"modified": "2021-05-06 13:37:03.318829",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Course",
@@ -108,7 +103,7 @@
"write": 1
}
],
"search_fields": "slug",
"search_fields": "title",
"sort_field": "creation",
"sort_order": "DESC",
"title_field": "title",

View File

@@ -6,13 +6,18 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from ...utils import slugify
from community.query import find, find_all
class LMSCourse(Document):
@staticmethod
def find(slug):
"""Returns the course with specified slug.
def find(name):
"""Returns the course with specified name.
"""
return find("LMS Course", is_published=True, slug=slug)
return find("LMS Course", is_published=True, name=name)
def autoname(self):
if not self.name:
self.name = self.generate_slug(title=self.title)
@staticmethod
def find_all():
@@ -20,19 +25,15 @@ class LMSCourse(Document):
"""
return find_all("LMS Course", is_published=True)
def before_save(self):
if not self.slug:
self.slug = self.generate_slug(title=self.title)
def generate_slug(self, title):
result = frappe.get_all(
'LMS Course',
fields=['slug'])
slugs = set([row['slug'] for row in result])
fields=['name'])
slugs = set([row['name'] for row in result])
return slugify(title, used_slugs=slugs)
def __repr__(self):
return f"<Course#{self.name} {self.slug}>"
return f"<Course#{self.name}>"
def get_topic(self, slug):
"""Returns the topic with given slug in this course as a Document.
@@ -123,6 +124,9 @@ class LMSCourse(Document):
# TODO: chapters should have a way to specify the order
return find_all("Chapter", course=self.name, order_by="creation")
def get_batch(self, batch_name):
return find("LMS Batch", name=batch_name, course=self.name)
def get_batches(self, mentor=None):
batches = find_all("LMS Batch", course=self.name)
if mentor:
@@ -139,24 +143,8 @@ class LMSCourse(Document):
now = frappe.utils.nowdate()
batches = find_all("LMS Batch",
course=self.name,
start_date=[">", now])
start_date=[">", now],
status="Active",
visibility="Public")
return batches
def find_all(doctype, order_by=None, **filters):
"""Queries the database for documents of a doctype matching given filters.
"""
rows = frappe.db.get_all(doctype,
filters=filters,
fields='*',
order_by=order_by)
return [frappe.get_doc(dict(row, doctype=doctype)) for row in rows]
def find(doctype, **filters):
"""Queries the database for a document of given doctype matching given filters.
"""
rows = frappe.db.get_all(doctype,
filters=filters,
fields='*')
if rows:
row = rows[0]
return frappe.get_doc(dict(row, doctype=doctype))

View File

@@ -24,7 +24,7 @@ class TestLMSCourse(unittest.TestCase):
def test_new_course(self):
course = self.new_course("Test Course")
assert course.title == "Test Course"
assert course.slug == "test-course"
assert course.name == "test-course"
assert course.get_mentors() == []
def test_find_all(self):
@@ -41,7 +41,7 @@ class TestLMSCourse(unittest.TestCase):
# now we should find one course
courses = LMSCourse.find_all()
assert [c.slug for c in courses] == [course.slug]
assert [c.name for c in courses] == [course.name]
# disabled this test as it is failing
def _test_add_mentors(self):

View File

@@ -8,86 +8,119 @@ from frappe.model.document import Document
from frappe import _
class LMSMentorRequest(Document):
def on_update(self):
if self.has_value_changed('status'):
template = frappe.db.get_single_value('LMS Settings', 'mentor_request_status_update')
if not template:
return
def on_update(self):
if self.has_value_changed('status'):
email_template = frappe.get_doc('Email Template', template)
message = frappe.render_template(email_template.response, {'member_name': self.member_name, 'status': self.status})
subject = _('The status of your application has changed.')
member_email = frappe.db.get_value("Community Member", self.member, "email")
if self.status == 'Approved' or self.status == 'Rejected':
reviewed_by = frappe.db.get_value('Community Member', self.reviewed_by, 'email')
send_email(member_email, [get_course_author(self.course), reviewed_by], subject, message)
elif self.status == 'Withdrawn':
send_email([member_email, get_course_author(self.course)], None, subject, message)
if self.status == "Approved":
self.create_course_mentor_mapping()
if self.status != "Pending":
self.send_status_change_email()
def create_course_mentor_mapping(self):
mapping = frappe.get_doc({
"doctype": "LMS Course Mentor Mapping",
"mentor": self.member,
"course": self.course
})
mapping.save()
def send_creation_email(self, member):
email_template = self.get_email_template('mentor_request_creation')
if not email_template:
return
course_details = frappe.db.get_value("LMS Course", self.course, ["owner", "slug", "title"], as_dict=True)
message = frappe.render_template(email_template.response,
{
'member_name': member.full_name,
'course_url': '/courses/' + course_details.slug,
'course': course_details.title
})
email_args = {
"recipients": [frappe.session.user, course_details.owner],
"subject": email_template.subject,
"header": email_template.subject,
"message": message
}
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
def send_status_change_email(self):
email_template = self.get_email_template('mentor_request_status_update')
if not email_template:
return
course_details = frappe.db.get_value("LMS Course", self.course, ["owner", "title"], as_dict=True)
message = frappe.render_template(email_template.response,
{
'member_name': self.member_name,
'status': self.status,
'course': course_details.title
})
member_email = frappe.db.get_value("Community Member", self.member, "email")
if self.status == 'Approved' or self.status == 'Rejected':
reviewed_by = frappe.db.get_value('Community Member', self.reviewed_by, 'email')
email_args = {
"recipients": member_email,
"cc": [course_details.owner, reviewed_by],
"subject": email_template.subject,
"header": email_template.subject,
"message": message
}
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
elif self.status == 'Withdrawn':
email_args = {
"recipients": [member_email, course_details.owner],
"subject": email_template.subject,
"header": email_template.subject,
"message": message
}
frappe.enqueue(method=frappe.sendmail, queue="short", timeout=300, is_async=True, **email_args)
def get_email_template(self, template_name):
template = frappe.db.get_single_value('LMS Settings', template_name)
if template:
return frappe.get_doc('Email Template', template)
@frappe.whitelist()
def has_requested(course):
return len(frappe.get_all('LMS Mentor Request',
filters = {
'member': get_member().name,
'course': course,
'status': ['in', ('Pending', 'Approved')]
}
)
return frappe.db.count('LMS Mentor Request',
filters = {
'member': get_member().name,
'course': course,
'status': ['in', ('Pending', 'Approved')]
}
)
@frappe.whitelist()
def create_request(course):
if not has_requested(course):
member = get_member()
frappe.get_doc({
'doctype': 'LMS Mentor Request',
'member': member.name,
'course': course,
'status': 'Pending'
}).save(ignore_permissions=True)
send_creation_email(course, member)
return 'OK'
else:
return 'Already Applied'
if not has_requested(course):
member = get_member()
request = frappe.get_doc({
'doctype': 'LMS Mentor Request',
'member': member.name,
'course': course,
'status': 'Pending'
})
request.save(ignore_permissions=True)
request.send_creation_email(member)
return 'OK'
else:
return 'Already Applied'
@frappe.whitelist()
def cancel_request(course):
request = frappe.get_doc('LMS Mentor Request', {'member': get_member().name, 'course': course, 'status': ['in', ('Pending', 'Approved')]})
request.status = 'Withdrawn'
request.save(ignore_permissions=True)
return 'OK'
request = frappe.get_doc('LMS Mentor Request', {'member': get_member().name, 'course': course, 'status': ['in', ('Pending', 'Approved')]})
request.status = 'Withdrawn'
request.save(ignore_permissions=True)
return 'OK'
def get_member():
try:
return frappe.get_doc('Community Member', {'email': frappe.session.user})
except frappe.DoesNotExistError:
return
def get_course_author(course):
return frappe.db.get_value('LMS Course', course, 'owner')
def send_creation_email(course, member):
template = frappe.db.get_single_value('LMS Settings', 'mentor_request_creation')
if not template:
return
email_template = frappe.get_doc('Email Template', template)
member_name = member.full_name
message = frappe.render_template(email_template.response, {'member_name': member_name})
subject = _('Request for Mentorship')
send_email([frappe.session.user, get_course_author(course)], None, subject, message)
def send_email(recipients, cc=None, subject=None, message=None, template=None, args=None):
frappe.sendmail(
recipients = recipients,
cc = cc,
sender = frappe.db.get_single_value('LMS Settings', 'email_sender'),
subject = subject,
send_priority = 0,
queue_separately = True,
message = message,
template=template,
args=args
)
try:
return frappe.get_doc('Community Member', {'email': frappe.session.user})
except frappe.DoesNotExistError:
return

View File

@@ -1,6 +1,6 @@
<div class="chapter-teaser">
<div class="teaser-body">
<h3 class="chapter-title">{{ chapter.title }}</h3>
<h3 class="chapter-title"><span class="chapter-number">{{index}}</span> {{ chapter.title }}</h3>
<div class="chapter-description">
{{ chapter.description or "" }}
</div>

View File

@@ -1,13 +1,15 @@
<div class="course-teaser">
<div class="course-body">
<h3 class="course-title"><a href="/courses/{{ course.slug }}">{{ course.title }}</a></h3>
<h3 class="course-title"><a href="/courses/{{ course.name }}">{{ course.title }}</a></h3>
<div class="course-intro">
{{ course.short_introduction or "" }}
</div>
</div>
<div class="course-footer">
<div class="course-author">
<img class="course-author-avatar" src="{{ course.get_instructor().avatar }}" />{{ course.get_instructor().full_name }}
{% with author = course.get_instructor() %}
{{ widgets.Avatar(member=author, avatar_class="avatar-medium") }} <a href="/{{author.username}}">{{ author.full_name }}</a>
{% endwith %}
</div>
</div>
</div>

View File

@@ -1,6 +1,12 @@
<form id="invite-request-form">
<input class="form-control field-width mr-5" id="invite_email" type="email" placeholder="Email Address">
<a type="submit" id="submit-invite-request" class="btn btn-primary btn-lg" href="#" role="button">Request Invite</a>
<div class="row">
<div class="col-md">
<input class="form-control w-100 mr-5 mb-5 mt-2" id="invite_email" type="email" placeholder="Email Address">
</div>
<div class="col-md">
<a type="submit" id="submit-invite-request" class="btn btn-primary btn-lg" href="#" role="button">Request Invite</a>
</div>
</div>
</form>
<script>
frappe.ready(() => {
@@ -16,19 +22,19 @@
if (data.message == "OK") {
var message = `<div>
<p class="lead">Thanks for your interest in Mon School. We have recorded your interest and we will get back to you shortly.</p>
<p class="lead alert alert-secondary">Thanks for your interest in Mon School. We have recorded your interest and we will get back to you shortly.</p>
</div>`;
}
else if (data.message == "invite") {
var message = `<div>
<p class="lead">Email ${invite_email} has already been used to request an invitation.</p>
<p class="lead alert alert-secondary">Email ${invite_email} has already been used to request an invitation.</p>
</div>`;
}
else if (data.message == "user") {
var message = `<div>
<p class="lead">Looks like there is already an account with email ${invite_email}. Would you like to <a href="/login">login</a>.</p>
<p class="lead alert alert-secondary">Looks like there is already an account with email ${invite_email}. Would you like to <a href="/login">login</a>?</p>
</div>`;
}

View File

@@ -9,7 +9,8 @@
<a href="sketches/{{sketch.sketch_id}}">{{sketch.title}}</a>
</div>
<div class="sketch-author">
by {{sketch.get_owner().full_name}}
{% set owner = sketch.get_owner() %}
by <a href="/{{owner.username}}">{{owner.full_name}}</a>
</div>
</div>
</div>