Compare commits

..

17 Commits

Author SHA1 Message Date
pateljannat
edddd830c1 fix: added min and max width to container padding 2021-07-20 12:51:14 +05:30
Rushabh Mehta
276d64a66a fix(style): don't mess with global container styles 2021-07-20 09:30:58 +05:30
Rushabh Mehta
79eb381a41 Merge pull request #157 from rmehta/remove-old-styles
fix(cleanup): remove old styles
2021-07-19 17:19:54 +05:30
Rushabh Mehta
44f9c0dfd3 fix(minor): remove old styles 2021-07-19 17:14:52 +05:30
Jannat Patel
0ca4cd724e Merge pull request #156 from fossunited/fix-only-show-published-courses
fix: only show the published courses on All Courses page
2021-07-19 17:11:03 +05:30
Anand Chitipothu
8a3e31f021 fix: only show the published courses on All Courses page
Closes #155
2021-07-19 17:03:38 +05:30
Jannat Patel
9be8a1af0b Merge pull request #154 from fossunited/fix-invite-email
Fix invite email
2021-07-19 13:43:36 +05:30
Anand Chitipothu
b9cac20613 fix: the delay in sending signup email 2021-07-19 13:32:18 +05:30
Anand Chitipothu
e6d5e6d37b fix: "Hello None" in the signup email
We were trying to show the full_name, but invite request only knows the email.
2021-07-19 13:31:44 +05:30
Jannat Patel
0abfcac7da Merge pull request #153 from fossunited/improve-email-templates
fix: fixed the email message sent out on signup
2021-07-19 13:23:55 +05:30
Anand Chitipothu
b70e8b9acc fix: fixed the email message sent out on signup
Currently updated keeping Mon.School in mind.
2021-07-19 13:14:13 +05:30
Jannat Patel
3b1e1aa3c3 Merge pull request #152 from fossunited/cleanup
fix: Cleanup
2021-07-19 12:49:22 +05:30
pateljannat
8f74c74d50 fix: removed unused styles and folders 2021-07-19 10:55:06 +05:30
pateljannat
d2f435016c fix: layout cleanup 2021-07-16 20:24:35 +05:30
pateljannat
389b35802b Merge branch 'main' of https://github.com/frappe/community into cleanup 2021-07-15 17:36:31 +05:30
Jannat Patel
a9192a74f9 Merge pull request #151 from fossunited/redesign-fixes
fix: Profile page, course card ratings, lesson completion tick
2021-07-15 17:36:17 +05:30
pateljannat
5ecae0df61 fix: removed unused pages 2021-07-15 17:01:15 +05:30
32 changed files with 108 additions and 836 deletions

View File

@@ -30,10 +30,13 @@ class InviteRequest(Document):
return user
def send_email(self):
subject = _("Your request has been approved.")
site_name = "Mon.School"
subject = _("Welcome to {0}!").format(site_name)
args = {
"full_name": self.full_name,
"signup_form_link": "/new-sign-up?invite_code={0}".format(self.name),
"site_name": site_name,
"site_url": frappe.utils.get_url()
}
frappe.sendmail(
@@ -42,7 +45,8 @@ class InviteRequest(Document):
subject=subject,
header=[subject, "green"],
template = "lms_invite_request_approved",
args=args)
args=args,
now=True)
@frappe.whitelist(allow_guest=True)
def create_invite_request(invite_email):

View File

@@ -1,4 +1,4 @@
<div class="course-instructor breadcrumb">
<div class="breadcrumb">
<a class="dark-links" href="/courses">All Courses</a>
<img class="ml-1 mr-1" src="/assets/community/icons/chevron-right.svg">

View File

@@ -1,5 +1,5 @@
{% if course.get_reviews() | length %}
<div class="reviews-parent col">
<div class="reviews-parent">
<div class="reviews-heading">
<div class="course-home-headings">Student Review</div>
{% if course.is_eligible_to_review(membership) %}

View File

@@ -1,4 +1,3 @@
@import "./style.css";
@import "./vars.css";
@import "./style.less";

View File

@@ -259,11 +259,11 @@ input[type=checkbox] {
.common-page-style {
background: #F4F5F6;
padding-bottom: 2rem;
}
.common-card-style {
display: flex;
align-items: flex-start;
background: #FFFFFF;
border-radius: 8px;
position: relative;
@@ -292,6 +292,12 @@ input[type=checkbox] {
padding: 0px 24px 20px;
}
@media (max-width: 350px) {
.course-card-content {
padding: 0px 10px 20px;
}
}
.course-card-title {
font-weight: 600;
font-size: 18px;
@@ -308,6 +314,7 @@ input[type=checkbox] {
font-size: 14px;
}
}
.card-divider {
border: 1px solid #F4F5F6;
margin-bottom: 16px;
@@ -358,7 +365,7 @@ input[type=checkbox] {
@media (max-width: 767px) {
.cards-parent {
grid-template-columns: repeat(auto-fill, minmax(300px, 336px));
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
-moz-column-gap: 16px;
column-gap: 16px;
row-gap: 16px;
@@ -367,7 +374,7 @@ input[type=checkbox] {
@media (max-width: 375px) {
.cards-parent {
grid-template-columns: repeat(auto-fill, minmax(100%, 312px));
grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
-moz-column-gap: 24px;
column-gap: 24px;
row-gap: 24px;
@@ -375,7 +382,7 @@ input[type=checkbox] {
}
.courses-header {
padding: 50px 30px 20px;
padding: 50px 20px 20px;
color: var(--text-color);
font-weight: 600;
font-size: 22px;
@@ -383,22 +390,10 @@ input[type=checkbox] {
letter-spacing: -0.0175em
}
.course-top-section {
float: none;
margin: 0 auto;
max-width: 1150px;
padding-bottom: 2rem;
}
@media (max-width: 768px) {
.course-top-section {
max-width: 720px;
}
}
@media (max-width: 600px) {
.course-top-section {
max-width: 342px;
@media (min-width: 576px) and (max-width: 992px) {
.container {
padding-left: 1rem;
padding-right: 1rem;
}
}
@@ -496,7 +491,6 @@ div.custom-checkbox>label>input:checked+img {
}
.course-card-wide {
height: fit-content;
display: flex;
flex-direction: row;
padding: 24px;
@@ -528,6 +522,7 @@ div.custom-checkbox>label>input:checked+img {
margin-right: 32px;
border-radius: 5px;
flex: 1;
align-self: center;
}
@media (max-width: 768px) {
@@ -546,23 +541,10 @@ div.custom-checkbox>label>input:checked+img {
}
}
.course-home-page {
max-width: 1150px;
margin: 0 auto;
padding-bottom: 2rem;
}
@media (max-width: 768px) {
@media (max-width: 500px) {
.course-home-page {
max-width: 688px;
}
}
@media (max-width: 600px) {
.course-home-page {
width: 100%;
padding-left: 0px;
padding-right: 0px;
padding-right: 0;
padding-left: 0;
}
}
@@ -570,6 +552,7 @@ div.custom-checkbox>label>input:checked+img {
display: flex;
flex-direction: column;
flex: 2;
justify-content: space-between;
}
@media (max-width: 768px) {
@@ -672,6 +655,7 @@ div.custom-checkbox>label>input:checked+img {
.course-home-outline {
margin-top: 3rem;
flex: 1;
}
.small-title {
@@ -703,7 +687,19 @@ div.custom-checkbox>label>input:checked+img {
@media (max-width: 600px) {
.course-outline-instructor-parent {
flex-direction: column;
padding: 0px 9px 0px;
padding: 0px 24px 0px;
}
}
.profile-parent-section {
display: flex;
justify-content: space-between;
}
@media (max-width: 600px) {
.profile-parent-section {
flex-direction: column;
}
}
@@ -735,6 +731,18 @@ div.custom-checkbox>label>input:checked+img {
margin-top: 3rem;
}
@media (min-width: 1024px) {
.course-overview-section {
margin-left: 2rem;
}
}
@media (min-width: 600px) {
.course-overview-section {
margin-left: 1rem;
}
}
.lesson-info {
font-size: 16px;
line-height: 250%;
@@ -897,7 +905,7 @@ div.custom-checkbox>label>input:checked+img {
}
.overview-card {
padding: 8px 24px 8px;
padding: 24px 26px 24px;
width: 256px;
flex-direction: column;
}
@@ -910,7 +918,7 @@ div.custom-checkbox>label>input:checked+img {
@media (max-width: 600px) {
.overview-card {
padding: 8px 9px 8px;
padding: 24px 26px 24px;
width: 100%;
}
}
@@ -995,20 +1003,17 @@ div.custom-checkbox>label>input:checked+img {
}
.breadcrumb {
padding: 1rem 0.5rem 0;
padding: 1rem 0 0;
display: flex;
align-items: center;
font-size: 12px;
line-height: 135%;
color: var(--text-color);
}
.course-details-outline {
width: 352px;
margin-top: 16px;
}
@media (max-width: 1025px) {
.course-details-outline {
width: 100%;
}
flex: 1;
}
.lesson-content-card {
@@ -1019,11 +1024,11 @@ div.custom-checkbox>label>input:checked+img {
.course-content-parent {
display: flex;
justify-content: space-between;
flex-wrap: wrap-reverse;
}
@media (max-width: 375px) {
@media (max-width: 768px) {
.course-content-parent {
flex-direction: column-reverse;
justify-content: center;
}
}
@@ -1039,13 +1044,13 @@ div.custom-checkbox>label>input:checked+img {
}
.lesson-pagination-parent {
width: 736px;
margin-top: 16px;
margin: 1rem 0px 0px 2rem;
flex: 3;
}
@media (max-width: 1025px) {
@media (max-width: 768px) {
.lesson-pagination-parent {
width: 100%;
margin-left: 0px;
}
}
@@ -1053,28 +1058,6 @@ div.custom-checkbox>label>input:checked+img {
width: 100%;
}
.course-details-page {
padding: 0px 0px 80px;
display: flex;
flex-direction: column;
max-width: 1120px;
margin: 0 auto;
}
@media (max-width: 1025px) {
.course-details-page {
padding: 24px 0px 24px;
width: 80%;
}
}
@media (max-width: 768px) {
.course-details-page {
padding: 24px 0px 24px;
width: 90%;
}
}
.active-lesson {
background-color: #EBF5FF;
border-radius: 4px;
@@ -1091,16 +1074,14 @@ div.custom-checkbox>label>input:checked+img {
}
.profile-page {
max-width: 1150px;
margin: 0 auto;
padding-top: 1rem;
padding-bottom: 1rem;
}
.profile-banner {
width: 100%;
height: 248px;
border-radius: 12px 12px 0px 0px;
background-size: cover;
background-position: center;
}
@media (max-width: 500px) {
@@ -1109,6 +1090,11 @@ div.custom-checkbox>label>input:checked+img {
}
}
.profile-about-section {
flex: 1;
margin-top: 3rem;
}
.profile-info {
height: 68px;
background: #ffffff;
@@ -1220,18 +1206,6 @@ div.custom-checkbox>label>input:checked+img {
margin-top: 3rem;
}
@media (max-width: 500px) {
.profile-courses {
padding: 0px 24px 0px;
}
}
@media (max-width: 360px) {
.profile-courses {
padding: 0px 10px 0px;
}
}
.progress-text {
font-size: 12px;
line-height: 165%;
@@ -1242,6 +1216,12 @@ div.custom-checkbox>label>input:checked+img {
flex-direction: column;
}
@media (min-width: 600px) {
.course-creator-progress-parent {
margin-left: 2rem;
}
}
.course-creator-section {
margin-top: 3rem;
}

View File

@@ -1,307 +0,0 @@
h2 {
margin: 20px 0px;
color: black;
}
.teaser {
background: white;
border-radius: 9px;
border: 1px solid #C4C4C4;
.teaser-body {
padding: 20px;
box-shadow: 0px 5px 10px rgb(0 0 0 / 10%)
}
.teaser-footer {
padding: 20px;
}
}
.sketch-teaser {
.teaser();
width: 220px;
margin-bottom: 30px;
margin-top: 30px;
svg {
width: 200px;
height: 200px;
}
.sketch-image {
padding: 10px;
}
.sketch-footer {
border-top: 1px solid#C4C4C4;
padding: 10px;
background: #F6F6F6;
border-radius: 0px 0px 10px 10px;
}
}
.course-teaser {
.teaser();
color: #444;
margin-bottom: 20px;
margin-top: 20px;
h3, h4 {
color: black;
font-weight: bold;
}
.course-body, .course-footer {
padding: 20px;
}
.course-body {
min-height: 8em;
}
.course-footer {
border-top: 1px solid #ddd;
}
a, a:hover {
color: inherit;
text-decoration: none;
}
}
.anchor_style {
color: inherit;
}
.anchor_style:hover {
text-decoration: none
}
section {
padding: 60px 0px;
}
section h2 {
margin-bottom: 40px;
font-size: 48px;
line-height: 58px;
font-weight: bold;
}
section.lightgray {
background: #F6F6F6;
}
#hero .jumbotron {
background: inherit;
}
.chapter-teaser {
.teaser();
color: #444;
margin: 20px 0px;
h3, h4 {
color: black;
font-weight: bold;
}
}
.field-width {
width: 40%;
display: inline-block;
}
.footer-grouped-links {
display: none;
}
.footer-info {
border-top: 0px;
margin-top: 0px;
.footer-col-right {
padding-top: 1.8rem;
}
}
.web-footer {
border-top: 1px solid #E2E6E9;
padding: 0px;
padding: 2rem 0px;
margin-top: 2rem;
}
.course-type {
text-transform: uppercase;
font-size: 1.0em;
font-weight: bold;
color: var(--tag-color);
}
.instructor-title {
color: black;
}
.instructor-subtitle {
font-size: 0.8em;
color: var(--text-color);
}
// .mentors-wrapper {
// .gray-section();
// }
.chapter-number {
background: var(--text-color);
color: white;
height: 24px;
min-width: 24px;
margin-right: 5px;
}
.sidebar {
background: var(--sidebar-bg);
border: 1px solid var(--sidebar-border);
margin: 20px 0px;
border-radius: 10px;
padding: 1px 20px 20px 20px;
}
.sidebar h3 {
margin-top: 20px;
color: black;
}
.sidebar-batch {
background: var(--sidebar-bg);
color: var(--text-color);
position: fixed;
left: 0;
height: 100%;
}
.sidebar-batch a {
padding: 16px 8px 8px 16px;
display: block;
}
.sidebar .notice {
margin-top: 10px;
padding: 10px;
border-radius: 10px;
border: 1px dashed var(--text-color);
}
.sidebar .notice a {
color: inherit;
text-decoration: underline;
}
// LiveCode editor
.livecode-editor {
.CodeMirror {
border: 1px solid #ddd;
background: #ffe;
height: auto;
}
.CodeMirror-scroll {
max-height: 310px;
min-height: 310px;
}
.controls {
padding: 10px 0px;
}
canvas {
border: 5px solid #ddd;
position: relative;
z-index: 0;
}
.output {
position: absolute;
z-index: 1;
width: 300px;
left: 0px;
top: 0px;
background-color: rgba(255, 255, 255, 0);
max-height: 300px;
white-space: pre-wrap;
word-wrap: break-word;
margin: 0px;
margin-left: 20px;
padding: 4px;
color: #888;
}
@media (max-width: 768px) {
.canvas-wrapper {
padding-top: 10px;
}
.code-wrapper {
min-height: 50px;
}
.CodeMirror {
min-height: 50px;
}
}
}
.sketch-header {
input#sketch-title {
font-weight: bold;
}
}
.lesson-teaser {
line-height: 40px;
}
#hero h1 {
color: black !important;
}
.lesson-page {
margin: 20px 0px;
}
.exercise-image svg {
width: 200px;
height: 200px;
border: 1px solid #ddd;
margin-bottom: 20px;
}
.svg-200 svg {
width: 200px;
height: 200px;
}
.livecode-editor-small .livecode-editor {
.CodeMirror-scroll {
max-height: 160px;
min-height: 160px;
}
canvas {
width: 150px;
height: 150px;
}
}
.mentor-dashboard {
margin-top: 20px;
.submission {
margin: 40px 0px 0px 20px;
}
}
.no-preview-message {
width: fit-content;
margin: 50px 0px 50px;
color: black;
}

View File

@@ -1,9 +1,8 @@
<div>
{% set site_link = "<a href='" + site_url + "'>" + site_url + "</a>" %}
<p>{{_("Dear Community Member,")}}</p>
<p>{{_("Your Invite Request to be a part of {0} has
been approved.").format(site_link)}}</p>
<p>Click on the link below to complete your Sign up and set a new password</p>
{% set site_link = "<a href='" + site_url + "'>" + site_name + "</a>" %}
<p>{{_("Hi,")}}</p>
<p>{{_("Welcome to {0}!").format(site_name)}}</p>
<p>Click on the link below to complete your sign up and set a new password</p>
<p style="margin: 15px 0px;">
<a href="{{ signup_form_link }}" rel="nofollow" class="btn btn-primary">{{ _("Complete Sign Up") }}</a>
</p>
@@ -14,5 +13,5 @@
</p>
<br>
<p>Thanks and Regards,</p>
<p>Your Community.</p>
</div>
<p>{{site_name}}</p>
</div>

View File

@@ -1,40 +0,0 @@
{% extends "templates/base.html" %}
{% block title %}About{% endblock %}
{% block head_include %}
<meta name="description" content="Courses" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
{% endblock %}
{% block content %}
<div class="container">
{{ widgets.BatchTabs(course=course, batch=batch) }}
<div class="tab-content" id="about">
{{ CourseBasicDetail(course)}}
<div class="d-flex align-items-center">
<div class="col-lg-4 col-md-12">
<div class="sidebar">
{{ widgets.InstructorSection(instructor=course.get_instructor()) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% macro CourseBasicDetail(course) %}
<h2>{{course.title}}</h2>
<div class="course-description">
{{course.short_introduction}}
</div>
{% if course.video_link %}
<div class="preview-video">
<iframe width="560" height="315" src="{{course.video_link}}" title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
</div>
{% endif %}
<h2>About the Course</h2>
<div>{{frappe.utils.md_to_html(course.description)}}</div>
{% endmacro %}

View File

@@ -1,5 +0,0 @@
import frappe
from . import utils
def get_context(context):
utils.get_common_context(context)

View File

@@ -1,61 +0,0 @@
{% extends "templates/base.html" %}
{% block title %} Batch {% endblock %}
{% block head_include %}
<meta name="description" content="Courses" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
{% endblock %}
{% block content %}
<div class="container mt-5">
{{ widgets.BatchTabs(course=course, membership=membership) }}
<div class="course-details mt-5">
{{ widgets.CourseOutline(course=course, batch=batch, show_link=membership, show_progress=True) }}
</div>
{% if batch %}
<div class="w-25">
<h3>Batch Schedule</h3>
{{ widgets.RenderBatch(course=course, batch=batch) }}
</div>
{% if batch.description %}
<div class="mt-5">
<h3>Batch Details</h3>
{{ frappe.utils.md_to_html(batch.description) }}
</div>
{% endif %}
{% endif %}
{% if course.is_mentor(frappe.session.user) %}
{% set invite_link = frappe.utils.get_url() + "/courses/" + course.name + "/join?batch=" + batch.name %}
<div class="">
<h3> Invite Members </h3>
<a href="" class="" id="invite-link" data-link="{{ invite_link }}">Get Batch Invitation
Link</a>
<small id="copy-message" class="text-muted" style="display: none;">Copied to Clipboard.</small>
</div>
{% endif %}
</div>
<script>
frappe.ready(() => {
$("#invite-link").click((e) => {
e.preventDefault();
var link_element = $("#invite-link");
var input_element = document.createElement("input");
input_element.value = link_element.attr("data-link")
document.body.appendChild(input_element);
input_element.select();
document.execCommand("copy");
input_element.remove();
$("#copy-message").slideDown(function () {
setTimeout(function () {
$("#copy-message").slideUp();
}, 2000);
});
})
})
</script>
{% endblock %}

View File

@@ -1,5 +0,0 @@
import frappe
from . import utils
def get_context(context):
utils.get_common_context(context)

View File

@@ -21,7 +21,7 @@
{% block content %}
<div class="common-page-style">
<div class="course-details-page">
<div class="container course-details-page">
{{ widgets.BreadCrumb(course=course, lesson=lesson) }}
<div class="course-content-parent">
<div class="course-details-outline">
@@ -47,7 +47,7 @@
</div>
{% if membership or lesson.include_in_preview %}
<div class="common-card-style lesson-content-card">{{ lesson.render_html() }}</div>
<div class="common-card-style lesson-content-card from-markdown">{{ lesson.render_html() }}</div>
{% else %}
<div class="no-preview-message">
<span>This lesson is not available for Preview. Please join the course to access this lesson.</span>

View File

@@ -1,40 +0,0 @@
{% extends "templates/base.html" %}
{% block title %}Members{% endblock %}
{% block head_include %}
<meta name="description" content="Courses" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
{% endblock %}
{% block content %}
<div class="container">
{{ widgets.BatchTabs(course=course, membership=membership) }}
{{ MembersList(members)}}
</div>
{% endblock %}
{% macro MembersList(members) %}
<div class="mt-5">
{% for member in members %}
<div class="d-flex align-items-center">
{{ widgets.Avatar(member=member, avatar_class="avatar-medium") }}
<div class="d-flex flex-column ml-2">
<div class="d-flex">
<a class="anchor_style ml-2" href="/{{member.username}}">
<div class="review-content">{{ member.full_name }}</div>
</a>
{% if course.is_mentor(member.name) %}
<div class="badge badge-success ml-2 align-self-start">Mentor</div>
{% endif %}
</div>
</div>
</div>
{% if loop.index != member_count %}
<hr>
{% endif %}
{% endfor %}
</div>
{% endmacro %}

View File

@@ -1,7 +0,0 @@
import frappe
from . import utils
def get_context(context):
utils.get_common_context(context)
if not context.membership:
utils.redirect_to_lesson(context.course)

View File

@@ -1,51 +0,0 @@
{% extends "templates/base.html" %}
{% from "www/macros/livecode.html" import LiveCodeEditorJS, LiveCodeEditor with context %}
{% block title %}{{ course.title }} - Batch Dashboard{% endblock %}
{% block head_include %}
<meta name="description" content="{{course.title}} - Batch Dashboard" />
<meta name="keywords" content="{{course.title}} - Batch Dashboard" />
<style>
</style>
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
<link rel="stylesheet" href="{{ livecode_url }}/static/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="/assets/css/lms.css">
<script src="{{ livecode_url }}/static/codemirror/lib/codemirror.js"></script>
<script src="{{ livecode_url }}/static/codemirror/mode/python/python.js"></script>
<script src="{{ livecode_url }}/static/codemirror/keymap/sublime.js"></script>
<script src="{{ livecode_url }}/static/codemirror/addon/edit/matchbrackets.js"></script>
<script src="{{ livecode_url }}/static/codemirror/addon/comment/comment.js"></script>
{% endblock %}
{% block content %}
<div class="container">
{{ widgets.BatchTabs(course=course, membership=membership) }}
<div class="mentor-dashboard">
<h3>Batch Progress</h3>
{% for exercise in report.exercises %}
<div class="exercise-submissions">
<h2>Exercise {{exercise.index_label}}: {{exercise.title}}</h2>
{% for s in report.get_submissions_of_exercise(exercise.name) %}
<div class="submission">
<h4><a href="/{{s.owner.username}}">{{s.owner.full_name}}</a></h4>
<div class="livecode-editor-small">
{{ LiveCodeEditor(name=s.name, code=s.solution, reset_code=s.solution) }}
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{%- block script %}
{{ super() }}
{{ LiveCodeEditorJS() }}
{% endblock %}

View File

@@ -1,62 +0,0 @@
import frappe
from community.lms.models import Course
from collections import defaultdict
from . import utils
def get_context(context):
utils.get_common_context(context)
exercise_name = frappe.form_dict.get("exercise")
if exercise_name:
exercise = frappe.get_doc("Exercise", exercise_name)
else:
exercise = None
context.exercise = exercise
context.report = BatchReport(context.course, context.batch)
class BatchReport:
def __init__(self, course, batch):
self.submissions = get_submissions(course, batch)
self.exercises = self.get_exercises(course.name)
self.submissions_by_exercise = defaultdict(list)
for s in self.submissions:
self.submissions_by_exercise[s.exercise].append(s)
def get_exercises(self, course_name):
return frappe.get_all("Exercise", {"course": course_name, "lesson": ["!=", ""]}, ["name", "title", "index_label"], order_by="index_label")
def get_submissions_of_exercise(self, exercise_name):
return self.submissions_by_exercise[exercise_name]
def get_submissions(course, batch):
students = course.get_students(batch.name)
if not len(students):
return []
students_map = {s.email: s for s in students}
names, values = nparams("s", students_map.keys())
sql = """
select owner, exercise, name, solution, creation, image
from (
select owner, exercise, name, solution, creation, image,
row_number() over (partition by owner, exercise order by creation desc) as ix
from `tabExercise Submission`) as t
where t.ix=1 and owner IN {}
""".format(names)
data = frappe.db.sql(sql, values=values, as_dict=True)
for row in data:
row['owner'] = students_map[row['owner']]
return data
def nparams(name, values):
"""Creates n paramters from a list of values for a db query.
>>> nparams("name", ["a", "b])
("(%(name_1)s, %(name_2)s)", {"name_1": "a", "name_2": "b"})
"""
keys = [f"{name}_{i}" for i, _ in enumerate(values, start=1)]
param_names = [f"%({k})s" for k in keys]
param_values = dict(zip(keys, values))
joined_names = "(" + ", ".join(param_names) + ")"
return joined_names, param_values

View File

@@ -1,17 +0,0 @@
{% extends "templates/base.html" %}
{% block title %}Schedule{% endblock %}
{% block head_include %}
<meta name="description" content="Courses" />
<meta name="keywords" content="" />
<link rel="stylesheet" href="/assets/frappe/css/font-awesome.css">
{% endblock %}
{% block content %}
<div class="container">
{{ widgets.BatchTabs(course=course, batch=batch) }}
<h3>
Schedule
</h3>
</div>
{% endblock %}

View File

@@ -1,20 +0,0 @@
import frappe
from community.lms.models import Course
def get_context(context):
context.no_cache = 1
course_name = frappe.form_dict["course"]
batch_name = frappe.form_dict["batch"]
course = Course.find(course_name)
if not course:
context.template = "www/404.html"
return
batch = course.get_batch(batch_name)
if not batch:
frappe.local.flags.redirect_location = "/courses/" + course_name
raise frappe.Redirect
context.course = course
context.batch = batch

View File

@@ -9,7 +9,7 @@
{% endblock %}
{% block content %}
<div class="common-page-style">
<div class="col course-home-page">
<div class="container course-home-page">
{{ widgets.BreadCrumb(course=course) }}
{{ CourseCardWide(course) }}
{{ CourseOutlineAndCreator(course) }}
@@ -32,11 +32,13 @@
</div>
</div>
<div class="course-card-wide-content">
<div class="course-card-wide-title">
{{ course.title }}
</div>
<div class="course-card-wide-intro">
{{ course.short_introduction }}
<div class="course-info">
<div class="course-card-wide-title">
{{ course.title }}
</div>
<div class="course-card-wide-intro">
{{ course.short_introduction }}
</div>
</div>
<div class="course-buttons">
{% if not course.disable_self_learning and not membership %}
@@ -88,10 +90,10 @@
{% macro CourseOutlineAndCreator(course) %}
<div class="course-outline-instructor-parent">
<div class="course-home-outline col">
<div class="course-home-outline">
{{ widgets.CourseOutline(course=course, membership=membership) }}
</div>
<div class="course-creator-progress-parent col-sm-auto">
<div class="course-creator-progress-parent">
<div class="course-creator-section">
<div class="course-home-headings">
Creator
@@ -151,7 +153,7 @@
{% macro CourseDescriptionAndOverview(course) %}
<div class="course-outline-instructor-parent">
<div class="course-description-section col">
<div class="course-description-section">
<div class="course-home-headings">
Course Description
</div>
@@ -161,7 +163,7 @@
</div>
{% set avg_rating = course.get_average_rating() %}
{% if course.get_students() | length or avg_rating %}
<div class="course-overview-section col-sm-auto">
<div class="course-overview-section">
<div class="course-home-headings">
Overview
</div>

View File

@@ -10,7 +10,7 @@
{% block content %}
<div class="common-page-style">
<div class="col course-top-section">
<div class="container">
<div class="courses-header">
{{ 'All Courses' }}
</div>

View File

@@ -5,7 +5,7 @@ def get_context(context):
context.courses = get_courses()
def get_courses():
course_names = frappe.get_all("LMS Course", pluck="name")
course_names = frappe.get_all("LMS Course", filters={"is_published": True}, pluck="name")
courses = []
for course in course_names:
courses.append(frappe.get_doc("LMS Course", course))

View File

@@ -1,82 +0,0 @@
{% extends "templates/base.html" %}
{% from "www/hackathons/macros/card.html" import null_card %}
{% block title %}{{ 'My Courses' }}{% endblock %}
{% block head_include %}
<meta name="description" content="My Courses" />
<meta name="keywords" content="" />
<style>
div.card-hero-img {
height: 220px;
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-color: rgb(250, 251, 252);
}
.card-image-wrapper {
display: flex;
overflow: hidden;
height: 220px;
background-color: rgb(250, 251, 252);
justify-content: center;
}
.image-body {
align-self: center;
color: #d1d8dd;
font-size: 24px;
font-weight: 600;
line-height: 1;
padding: 20px;
}
.no-courses {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
section {
padding: 5rem 0 5rem 0;
}
</style>
{% endblock %}
{% macro card(course) %}
<div class="col-sm-4 mb-4 text-left">
<a href="//courses/course?course={{course.name}}" class="no-decoration no-underline">
<div class="card h-100">
<div class='card-body'>
<h5 class='card-title'>{{ course.title }}</h5>
</div>
</div>
</a>
</div>
{% endmacro %}
{% block content %}
<section class="section">
<div class='container'>
{% if frappe.session.user != "Guest" %}
{% for course in my_courses %}
{{ card(course) }}
{% endfor %}
{% if my_courses %}
{% for n in range( (3 - (my_courses|length)) %3) %}
{{ null_card() }}
{% endfor %}
{% else %}
<div class="no-courses">You haven't enrolled in any Course yet. <a href="/courses">Check out the availabe
courses.</a></div>
{% endif %}
{% else %}
<div class="no-courses">
<p>Please sign up to access this page.</p>
<a id="signup" class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
</div>
{% endif %}
</div>
</section>
{% endblock %}

View File

@@ -1,15 +0,0 @@
import frappe
def get_context(context):
context.no_cache = 1
context.my_courses = get_my_courses()
def get_my_courses():
my_courses = []
courses = frappe.get_all("LMS Course Enrollment", {"owner": frappe.session.user}, ["course"])
for course in courses:
my_courses.append({
"name": course.course,
"title": frappe.db.get_value("LMS Course", course.course, ["title"])
})
return my_courses

View File

@@ -6,7 +6,7 @@
{% block content %}
<div class="common-page-style">
<div class="col profile-page">
<div class="container profile-page">
{{ ProfileBanner(member) }}
{{ AboutOverviewSection(member) }}
{{ CoursesCreated(member) }}
@@ -18,8 +18,8 @@
{% endblock %}
{% macro ProfileBanner(member) %}
<div class="col">
<div class="profile-banner" style="background: url(/assets/community/images/profile-banner.png)">
<div class="">
<div class="profile-banner" style="background-image: url(/assets/community/images/profile-banner.png)">
<div class="profile-avatar">
{{ widgets.Avatar(member=member, avatar_class="avatar-xl") }}
<div class="profile-name"> {{ member.full_name }} </div>
@@ -54,9 +54,9 @@
{% endmacro %}
{% macro AboutOverviewSection(member) %}
<div class="course-outline-instructor-parent">
<div class="profile-parent-section">
{% if member.bio %}
<div class="course-overview-section col">
<div class="profile-about-section">
<div class="course-home-headings">
About
</div>
@@ -65,7 +65,7 @@
</div>
</div>
{% endif %}
<div class="course-overview-section col-sm-auto">
<div class="course-overview-section">
<div class="course-home-headings">
Overview
</div>