Merge pull request #128 from fossunited/ui-fixes

fix: UI fixes
This commit is contained in:
Jannat Patel
2021-06-15 13:19:03 +05:30
committed by GitHub
50 changed files with 418 additions and 616 deletions

View File

@@ -15,13 +15,6 @@ def autosave_section(section, code):
doc.insert()
return {"name": doc.name}
@frappe.whitelist()
def get_section(name):
"""Saves the code edited in one of the sections.
"""
doc = frappe.get_doc("LMS Section", name)
return doc and doc.as_dict()
@frappe.whitelist()
def submit_solution(exercise, code):
"""Submits a solution.

View File

@@ -1,8 +0,0 @@
// Copyright (c) 2021, FOSS United and contributors
// For license information, please see license.txt
frappe.ui.form.on('Code Revision', {
// refresh: function(frm) {
// }
});

View File

@@ -1,58 +0,0 @@
{
"actions": [],
"creation": "2021-04-07 00:26:28.806520",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section",
"code",
"author"
],
"fields": [
{
"fieldname": "section",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Section",
"options": "LMS Section"
},
{
"fieldname": "code",
"fieldtype": "Code",
"label": "Code"
},
{
"fieldname": "author",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Author",
"options": "User"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-04-14 11:26:19.628317",
"modified_by": "Administrator",
"module": "LMS",
"name": "Code Revision",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "section",
"track_changes": 1
}

View File

@@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class CodeRevision(Document):
pass

View File

@@ -1,10 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and Contributors
# See license.txt
from __future__ import unicode_literals
# import frappe
import unittest
class TestCodeRevision(unittest.TestCase):
pass

View File

@@ -58,7 +58,8 @@ def create_invite_request(invite_email):
frappe.get_doc({
"doctype": "Invite Request",
"invite_email": invite_email
"invite_email": invite_email,
"status": "Approved"
}).save(ignore_permissions=True)
return "OK"

View File

@@ -18,7 +18,7 @@ class TestInviteRequest(unittest.TestCase):
filters={"invite_email": "test_invite@example.com"},
fieldname=["invite_email", "status", "signup_email"],
as_dict=True)
self.assertEqual(invite.status, "Pending")
self.assertEqual(invite.status, "Approved")
self.assertEqual(invite.signup_email, None)
def test_create_invite_request_update(self):

View File

@@ -8,11 +8,13 @@
"field_order": [
"chapter",
"lesson_type",
"include_in_preview",
"column_break_4",
"title",
"index_",
"index_label",
"body",
"sections"
"section_break_6",
"body"
],
"fields": [
{
@@ -47,23 +49,31 @@
"fieldtype": "Markdown Editor",
"label": "Body"
},
{
"fieldname": "sections",
"fieldtype": "Table",
"label": "Sections",
"options": "LMS Section"
},
{
"fieldname": "index_label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Index Label",
"read_only": 1
},
{
"default": "0",
"fieldname": "include_in_preview",
"fieldtype": "Check",
"label": "Include In Preview"
},
{
"fieldname": "section_break_6",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-06-01 05:30:48.127494",
"modified": "2021-06-11 19:03:23.138165",
"modified_by": "Administrator",
"module": "LMS",
"name": "Lesson",

View File

@@ -5,7 +5,6 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from ...section_parser import SectionParser
from ...md import markdown_to_html, find_macros
class Lesson(Document):
@@ -39,9 +38,6 @@ class Lesson(Document):
def render_html(self):
return markdown_to_html(self.body)
def get_sections(self):
return sorted(self.get('sections'), key=lambda s: s.index)
def get_exercises(self):
if not self.body:
return []
@@ -50,30 +46,6 @@ class Lesson(Document):
exercises = [value for name, value in macros if name == "Exercise"]
return [frappe.get_doc("Exercise", name) for name in exercises]
def make_lms_section(self, index, section):
s = frappe.new_doc('LMS Section', parent_doc=self, parentfield='sections')
s.type = section.type
s.id = section.id
s.label = section.label
s.contents = section.contents
s.index = index
return s
def get_next(self):
"""Returns the number for the next lesson.
The return value would be like 1.2, 2.1 etc.
It will be None if there is no next lesson.
"""
def get_prev(self):
"""Returns the number for the prev lesson.
The return value would be like 1.2, 2.1 etc.
It will be None if there is no next lesson.
"""
def get_progress(self):
return frappe.db.get_value("LMS Course Progress", {"lesson": self.name, "owner": frappe.session.user}, "status")
@@ -98,11 +70,7 @@ def save_progress(lesson, batch):
return
lesson_details = frappe.get_doc("Lesson", lesson)
dynamic_content = frappe.db.count("LMS Section",
filters={
"type": ["not in", ["example", "text"]],
"parent": lesson_details.name
})
dynamic_content = find_macros(lesson_details.body)
status = "Complete"
if dynamic_content:
@@ -121,12 +89,11 @@ def update_progress(lesson):
if frappe.db.exists("LMS Course Progress", {"lesson": lesson, "owner": user}):
course_progress = frappe.get_doc("LMS Course Progress", {"lesson": lesson, "owner": user})
course_progress.status = "Complete"
course_progress.save()
course_progress.save(ignore_permissions=True)
def all_dynamic_content_submitted(lesson, user):
exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, ["name"], pluck="name")
exercise_names = frappe.get_list("Exercise", {"lesson": lesson}, pluck="name", ignore_permissions=True)
all_exercises_submitted = False
print(exercise_names)
query = {
"exercise": ["in", exercise_names],
"owner": user

View File

@@ -80,11 +80,6 @@ class LMSBatch(Document):
membership = self.get_membership(user)
return membership and membership.current_lesson
def get_learn_url(self, lesson_number):
if not lesson_number:
return
return f"/courses/{self.course}/{self.name}/learn/{lesson_number}"
@frappe.whitelist()
def save_message(message, batch):
doc = frappe.get_doc({

View File

@@ -13,7 +13,8 @@
"course",
"member_type",
"role",
"current_lesson"
"current_lesson",
"is_current"
],
"fields": [
{
@@ -80,11 +81,19 @@
"fieldtype": "Data",
"label": "Memeber Username",
"read_only": 1
},
{
"default": "0",
"fieldname": "is_current",
"fieldtype": "Check",
"hidden": 1,
"label": "Is Currently Being Used",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-05-24 12:40:57.125694",
"modified": "2021-06-14 10:24:35.425498",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Batch Membership",

View File

@@ -53,3 +53,13 @@ def create_membership(batch, member=None, member_type="Student", role="Member"):
"member": member or frappe.session.user
}).save(ignore_permissions=True)
return "OK"
@frappe.whitelist()
def update_current_membership(batch, course, member=frappe.session.user):
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": course})
for membership in all_memberships:
frappe.db.set_value("LMS Batch Membership", membership.name, "is_current", 0)
current_membership = frappe.get_all("LMS Batch Membership", {"batch": batch, "member": member})
if len(current_membership):
frappe.db.set_value("LMS Batch Membership", current_membership[0].name, "is_current", 1)

View File

@@ -8,6 +8,7 @@ from frappe.model.document import Document
import json
from ...utils import slugify
from community.query import find, find_all
from frappe.utils import flt
class LMSCourse(Document):
@staticmethod
@@ -112,7 +113,7 @@ class LMSCourse(Document):
"""Returns all chapters of this course.
"""
# TODO: chapters should have a way to specify the order
return find_all("Chapter", course=self.name, order_by="creation")
return find_all("Chapter", course=self.name, order_by="index_")
def get_batch(self, batch_name):
return find("LMS Batch", name=batch_name, course=self.name)
@@ -186,6 +187,26 @@ class LMSCourse(Document):
exercise.save()
i += 1
def get_learn_url(self, lesson_number):
if not lesson_number:
return
return f"/courses/{self.name}/learn/{lesson_number}"
def get_current_batch(self, member=frappe.session.user):
current_membership = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name, "is_current": 1}, pluck="batch")
print(current_membership, member, self.name)
if len(current_membership):
return current_membership[0]
print(frappe.db.get_value("LMS Batch Membership", {"member": member, "course": self.name}, "batch"))
return frappe.db.get_value("LMS Batch Membership", {"member": member, "course": self.name}, "batch")
def get_all_memberships(self, member=frappe.session.user):
print(member)
all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch", "is_current"])
print(all_memberships)
for membership in all_memberships:
membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title")
return all_memberships
def get_outline(self):
return CourseOutline(self)
@@ -197,6 +218,7 @@ class CourseOutline:
self.lessons = self.get_lessons()
def get_next(self, current):
current = flt(current)
numbers = sorted(lesson['number'] for lesson in self.lessons)
try:
index = numbers.index(current)
@@ -205,6 +227,7 @@ class CourseOutline:
return None
def get_prev(self, current):
current = flt(current)
numbers = sorted(lesson['number'] for lesson in self.lessons)
try:
index = numbers.index(current)
@@ -228,7 +251,7 @@ class CourseOutline:
chapter_numbers = {c['name']: c['index_'] for c in self.chapters}
for lesson in lessons:
lesson['number'] = "{}.{}".format(chapter_numbers[lesson['chapter']], lesson['index_'])
lesson['number'] = flt("{}.{}".format(chapter_numbers[lesson['chapter']], lesson['index_']))
return lessons
@frappe.whitelist()

View File

@@ -1,66 +0,0 @@
{
"actions": [],
"creation": "2021-03-05 15:10:53.906006",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"label",
"type",
"contents",
"code",
"attrs",
"index",
"id"
],
"fields": [
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label"
},
{
"fieldname": "type",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Type"
},
{
"fieldname": "contents",
"fieldtype": "Markdown Editor",
"label": "Contents"
},
{
"fieldname": "code",
"fieldtype": "Code",
"label": "Code"
},
{
"fieldname": "attrs",
"fieldtype": "Long Text",
"label": "attrs"
},
{
"fieldname": "index",
"fieldtype": "Int",
"label": "Index"
},
{
"fieldname": "id",
"fieldtype": "Data",
"label": "id"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-05-19 18:55:26.019625",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Section",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View File

@@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2021, FOSS United and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class LMSSection(Document):
def __repr__(self):
return f"<LMSSection {self.label!r}>"
def get_exercise(self):
if self.type == "exercise":
return frappe.get_doc("Exercise", self.id)
def get_latest_code_for_user(self):
"""Returns the latest code for the logged in user.
"""
if not frappe.session.user or frappe.session.user == "Guest":
return self.contents
result = frappe.get_all('Code Revision',
fields=["code"],
filters={
"author": frappe.session.user,
"section": self.name
},
order_by="creation desc",
page_length=1)
if result:
return result[0]['code']
else:
return self.contents

View File

@@ -2,4 +2,4 @@
"""
from .doctype.lms_course.lms_course import LMSCourse as Course
from .doctype.lms_sketch.lms_sketch import LMSSketch as Sketch
from .doctype.lms_batch_membership.lms_batch_membership import LMSBatchMembership as Membership

View File

@@ -1,84 +0,0 @@
"""Utility to split the text in the topic into multiple sections.
{{ section(type="example", id="foo") }}
circle(100, 100, 50)
{{ end }}
"""
from __future__ import annotations
from dataclasses import dataclass
import re
from typing import List, Tuple, Dict, Iterator
RE_SECTION = re.compile(r"^\{\{\s(\w+)\s*(?:\((.*)\))?\s*\}\}\s*")
class SectionParser:
def parse(self, text: str) -> Iterator[Section]:
"""Parses given text into sections and return an iterator over sections.
"""
lines = text.splitlines()
marked_lines = self.parse_lines(lines)
return self.group_sections(marked_lines)
def parse_lines(self, lines: List[str]) -> List[Tuple[str, str, str]]:
for line in lines:
m = RE_SECTION.match(line)
if m:
yield m.group(1), self.parse_attrs(m.group(2)), None
else:
yield None, None, line
def parse_attrs(self, attrs_str: str) -> Dict[str, str]:
# XXX-Anand: Hack
code = "dict({})".format(attrs_str or "")
return eval(code)
def group_sections(self, marked_lines) -> Iterator[Section]:
index = 0
def make_section(type='text', id=None, label=None, **attrs):
nonlocal index
index += 1
id = id or f"section-{index}"
label = label or id
return Section(
type=type,
id=id,
label=label,
attrs=attrs)
section = make_section("text")
for mark, attrs, line in marked_lines:
if not mark:
section.append(line)
continue
yield section
if mark == 'end':
section = make_section(type='text')
else:
section = make_section(**attrs)
yield section
@dataclass
class Section:
"""One section of the Topic.
"""
type: str
id: str
label: str
contents: str = ""
attrs: dict = None
def append(self, line):
if not line.endswith("\n"):
line = line + "\n"
self.contents += line
def __repr__(self):
attrs = dict(type=self.type, id=self.id, label=self.label, **self.attrs)
attrs_str = ", ".join(f'{k}="{v}"' for k, v in attrs.items())
return f'<Section({attrs_str})>'

View File

@@ -11,7 +11,7 @@
"apply_document_permissions": 0,
"button_label": "Save",
"creation": "2021-04-20 11:37:49.135114",
"custom_css": ".datepicker.active {\n background-color: white;\n}",
"custom_css": ".datepicker.active {\n background-color: white;\n}\n\n[data-doctype=\"Web Form\"] {\n max-width: 720px;\n margin: 6rem auto;\n}",
"doc_type": "LMS Batch",
"docstatus": 0,
"doctype": "Web Form",
@@ -19,7 +19,7 @@
"is_standard": 1,
"login_required": 1,
"max_attachment_size": 0,
"modified": "2021-06-02 15:52:06.383260",
"modified": "2021-06-14 15:28:08.206622",
"modified_by": "Administrator",
"module": "LMS",
"name": "add-a-new-batch",

View File

@@ -1,3 +0,0 @@
frappe.ready(function() {
// bind events here
})

View File

@@ -1,48 +0,0 @@
{
"accept_payment": 0,
"allow_comments": 0,
"allow_delete": 0,
"allow_edit": 0,
"allow_incomplete": 0,
"allow_multiple": 0,
"allow_print": 0,
"amount": 0.0,
"amount_based_on_field": 0,
"apply_document_permissions": 0,
"button_label": "Save",
"creation": "2021-04-15 13:32:14.171328",
"doc_type": "LMS Batch Membership",
"docstatus": 0,
"doctype": "Web Form",
"idx": 0,
"is_standard": 1,
"login_required": 1,
"max_attachment_size": 0,
"modified": "2021-04-15 13:32:14.171328",
"modified_by": "Administrator",
"module": "LMS",
"name": "join-a-batch",
"owner": "Administrator",
"payment_button_label": "Buy Now",
"published": 1,
"route": "join-a-batch",
"route_to_success_link": 0,
"show_attachments": 0,
"show_in_grid": 0,
"show_sidebar": 0,
"sidebar_items": [],
"success_url": "/join-a-batch",
"title": "Join a Batch",
"web_form_fields": [
{
"allow_read_on_all_link_options": 0,
"fieldtype": "Attach",
"hidden": 0,
"max_length": 0,
"max_value": 0,
"read_only": 0,
"reqd": 0,
"show_in_filter": 0
}
]
}

View File

@@ -1,7 +0,0 @@
from __future__ import unicode_literals
import frappe
def get_context(context):
# do your magic here
pass

View File

@@ -1,28 +1,49 @@
<div class="mt-5">
<a class="anchor_style" href="/courses">Courses</a> /{% if course.is_mentor(frappe.session.user) %} <a class="anchor_style" href="/courses/{{ course.name }}"> {{ course.title }}</a> {% else %} <span class="text-muted"> {{ course.title }}</span> {% endif %}
<a class="anchor_style" href="/courses">Courses</a> /{% if course.is_mentor(frappe.session.user) %} <a
class="anchor_style" href="/courses/{{ course.name }}"> {{ course.title }}</a> {% else %} <span class="text-muted">
{{ course.title }}</span> {% endif %}
{% set all_memberships = course.get_all_memberships() %}
{% if all_memberships | length > 1 %}
<a class="nav-link pull-right" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
Switch Batch
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
{% for membership in all_memberships %}
{% if not membership.is_current %}
<a class="dropdown-item switch-batch" href="#" data-batch="{{ membership.batch | urlencode }}" data-course="{{ course.name | urlencode }}">{{ membership.batch_title }}</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
{% if not batch %}
{% set display_class = "hide" %}
{% else %}
{% set display_class = "" %}
{% endif %}
<ul class="nav nav-tabs mt-4">
<li class="nav-item">
<a class="nav-link" id="home" href="/courses/{{course.name}}/{{batch.name}}/home">Home</a>
<li class="nav-item {{ display_class }}">
<a class="nav-link" id="home" href="/courses/{{course.name}}/home">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" id="learn" href="/courses/{{course.name}}/{{batch.name}}/learn">Learn</a>
<a class="nav-link" id="learn" href="/courses/{{course.name}}/learn">Lessons</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" id="schedule" href="/courses/{{course.name}}/{{batch.name}}/schedule">Schedule</a>
<a class="nav-link" id="schedule" href="/courses/{{course.name}}/schedule">Schedule</a>
</li> -->
<li class="nav-item">
<a class="nav-link" id="members" href="/courses/{{course.name}}/{{batch.name}}/members">Members</a>
<li class="nav-item {{ display_class }}">
<a class="nav-link" id="members" href="/courses/{{course.name}}/members">Members</a>
</li>
<li class="nav-item">
<a class="nav-link" id="discussion" href="/courses/{{course.name}}/{{batch.name}}/discuss">Discussion</a>
<li class="nav-item {{ display_class }}">
<a class="nav-link" id="discussion" href="/courses/{{course.name}}/discuss">Discussion</a>
</li>
<!-- <li class="nav-item">
<a class="nav-link" id="about" href="/courses/{{course.name}}/{{batch.name}}/about">About</a>
<a class="nav-link" id="about" href="/courses/{{course.name}}/about">About</a>
</li> -->
{% if batch.is_member(frappe.session.user, member_type="Mentor") %}
{% if batch and batch.is_member(frappe.session.user, member_type="Mentor") %}
<li class="nav-item">
<a class="nav-link" id="progress" href="/courses/{{course.name}}/{{batch.name}}/progress">Progress</a>
<a class="nav-link" id="progress" href="/courses/{{course.name}}/progress">Progress</a>
</li>
{% endif %}
</ul>
@@ -35,5 +56,21 @@
else {
$("#learn").addClass('active')
}
$(".switch-batch").click((e) => {
e.preventDefault();
var batch = decodeURIComponent($(e.currentTarget).attr("data-batch"));
var course = decodeURIComponent($(e.currentTarget).attr("data-course"));
frappe.call({
method: "community.lms.doctype.lms_batch_membership.lms_batch_membership.update_current_membership",
args: {
batch: batch,
course: course
},
callback: (data) => {
window.location.reload();
}
})
})
})
</script>

View File

@@ -1,18 +1,54 @@
<div class="chapter-teaser">
<div class="teaser-body">
<h3 class="chapter-title"><span class="mr-1">{{index}}.</span> {{ chapter.title }}</h3>
<div class="chapter-title mb-5 font-weight-bold"><span class="mr-1">{{index}}.</span> {{ chapter.title }}</div>
<div class="chapter-description">
{{ chapter.description or "" }}
</div>
<div class="chapter-lessons">
{% for lesson in chapter.get_lessons() %}
<div class="lesson-teaser">
<a {% if show_link %} class="anchor_style" href="{{ batch.get_learn_url(course.get_lesson_index(lesson.name)) }}" {% endif %}>{{ lesson.title }}</a>
<a {% if show_link or lesson.include_in_preview %}
href="{{ course.get_learn_url(course.get_lesson_index(lesson.name)) }}" {% else %} href="" class="no-preview"
{% endif %} data-course="{{ course.name }}">{{ lesson.title }}</a>
{% if show_progress and not course.is_mentor(frappe.session.user) and lesson.get_progress() %}
<a class="pull-right badge p-1 {{ lesson.get_slugified_class() }}"> <img class="progress-image" src="/assets/community/images/Vector.png"> {{ lesson.get_progress() }}</a>
<a class="ml-5 badge p-1 {{ lesson.get_slugified_class() }}"> <img class="progress-image"
src="/assets/community/images/Vector.png"> {{ lesson.get_progress() }}</a>
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
<script>
frappe.ready(() => {
var d;
$(".no-preview").click((e) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
var message = __("Please enroll for this course to access the lesson.");
var label = __("Checkout Upcoming Batches");
var action = "checkout_upcoming_batches";
d = frappe.msgprint({
title: __("This lesson is not available for preview!"),
message: message,
primary_action: {
"label": label,
"client_action": action,
}
});
})
window.redirect_to_login = () => {
window.location.href = `/login?redirect-to=/courses/${$(".no-preview").attr("data-course")}`
}
window.checkout_upcoming_batches = () => {
if ($(".upcoming").length > 0) {
$('html,body').animate({ scrollTop: $(".upcoming").offset().top }, 300);
}
frappe.hide_msgprint();
}
})
</script>

View File

@@ -1,5 +1,7 @@
<h2>Course Outline</h2>
<div class="mt-5">
<h3> Course Outline </h3>
{% for chapter in course.get_chapters() %}
{{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, show_link=show_link, show_progress=show_progress)}}
{% endfor %}
{% for chapter in course.get_chapters() %}
{{ widgets.ChapterTeaser(index=loop.index, chapter=chapter, course=course, batch=batch, show_link=show_link, show_progress=show_progress)}}
{% endfor %}
</div>

View File

@@ -1,5 +1,6 @@
<h3>Instructor</h3>
<div class="instructor">
<div class="instructor-title">{{instructor.full_name}}</div>
<div class="instructor-subtitle">Created {{instructor.get_course_count()}} courses</div>
{{ widgets.Avatar(member=instructor, avatar_class="avatar-medium") }}
<a class="ml-1 instructor-title" href="/{{instructor.username}}">{{ instructor.full_name }}</a>
<div class="instructor-subtitle">Course Creator</div>
<!-- <div class="instructor-subtitle">Created {{instructor.get_course_count()}} courses</div> -->
</div>

View File

@@ -1,6 +1,6 @@
<div class="batch">
<div class="batch-details">
<div>Session every {{batch.sessions_on}}</div>
<div class="">Session every {{batch.sessions_on}}</div>
<div>{{frappe.utils.format_time(batch.start_time, "short")}} -
{{frappe.utils.format_time(batch.end_time, "short")}}
</div>
@@ -18,7 +18,8 @@
<div class="cta">
<div class="">
{% if can_manage %}
<a href="/courses/{{course.name}}/{{batch.name}}/home" class="btn btn-primary">Manage</a>
<a href="" class="btn btn-primary manage-batch" data-batch="{{ batch.name | urlencode }}"
data-course="{{ course.name | urlencode }}">Manage</a>
{% elif can_join %}
<button class="join-batch btn btn-primary" data-batch="{{ batch.name | urlencode }}"
data-course="{{ course.name | urlencode }}">Join this Batch</button>