feat: live class modal
This commit is contained in:
@@ -4,10 +4,10 @@
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint, format_date, format_datetime
|
||||||
import requests
|
import requests
|
||||||
import urllib
|
import base64
|
||||||
from requests.auth import HTTPBasicAuth
|
import json
|
||||||
|
|
||||||
|
|
||||||
class LMSClass(Document):
|
class LMSClass(Document):
|
||||||
@@ -91,8 +91,40 @@ def update_course(class_name, course, value):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_live_class(class_name):
|
def create_live_class(class_name, title, duration, date, time, description=None):
|
||||||
authenticate()
|
date = format_date(date, "yyyy-mm-dd")
|
||||||
|
payload = {
|
||||||
|
"topic": title,
|
||||||
|
"start_time": format_datetime(f"{date} {time}", "yyyy-MM-ddTHH:mm:ssZ"),
|
||||||
|
"duration": duration,
|
||||||
|
"timezone": "IN",
|
||||||
|
"agenda": description,
|
||||||
|
"private_meeting": True,
|
||||||
|
}
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Bearer " + authenticate(),
|
||||||
|
"content-type": "application/json",
|
||||||
|
}
|
||||||
|
response = requests.post(
|
||||||
|
"https://api.zoom.us/v2/users/me/meetings", headers=headers, data=json.dumps(payload)
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 201:
|
||||||
|
data = json.loads(response.text)
|
||||||
|
payload.update(
|
||||||
|
{
|
||||||
|
"doctype": "LMS Live Class",
|
||||||
|
"start_url": data.get("start_url"),
|
||||||
|
"join_url": data.get("join_url"),
|
||||||
|
"title": title,
|
||||||
|
"host": frappe.session.user,
|
||||||
|
"date": date,
|
||||||
|
"time": time,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
class_details = frappe.get_doc(payload)
|
||||||
|
class_details.save()
|
||||||
|
return class_details
|
||||||
|
|
||||||
|
|
||||||
def authenticate():
|
def authenticate():
|
||||||
@@ -100,10 +132,18 @@ def authenticate():
|
|||||||
if not zoom.enable:
|
if not zoom.enable:
|
||||||
frappe.throw(_("Please enable Zoom Settings to use this feature."))
|
frappe.throw(_("Please enable Zoom Settings to use this feature."))
|
||||||
|
|
||||||
authenticate_url = "https://zoom.us/oauth/token?grant_type=client_credentials"
|
authenticate_url = f"https://zoom.us/oauth/token?grant_type=account_credentials&account_id={zoom.account_id}"
|
||||||
print(authenticate_url)
|
|
||||||
breakpoint
|
headers = {
|
||||||
r = requests.get(
|
"Authorization": "Basic "
|
||||||
authenticate_url, auth=HTTPBasicAuth(zoom.client_id, zoom.client_secret)
|
+ base64.b64encode(
|
||||||
)
|
bytes(
|
||||||
return r
|
zoom.client_id
|
||||||
|
+ ":"
|
||||||
|
+ zoom.get_password(fieldname="client_secret", raise_exception=False),
|
||||||
|
encoding="utf8",
|
||||||
|
)
|
||||||
|
).decode()
|
||||||
|
}
|
||||||
|
response = requests.request("POST", authenticate_url, headers=headers)
|
||||||
|
return response.json()["access_token"]
|
||||||
|
|||||||
0
lms/lms/doctype/lms_live_class/__init__.py
Normal file
0
lms/lms/doctype/lms_live_class/__init__.py
Normal file
8
lms/lms/doctype/lms_live_class/lms_live_class.js
Normal file
8
lms/lms/doctype/lms_live_class/lms_live_class.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright (c) 2023, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
// frappe.ui.form.on("LMS Live Class", {
|
||||||
|
// refresh(frm) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// });
|
||||||
156
lms/lms/doctype/lms_live_class/lms_live_class.json
Normal file
156
lms/lms/doctype/lms_live_class/lms_live_class.json
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2023-03-02 10:59:01.741349",
|
||||||
|
"default_view": "List",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"host",
|
||||||
|
"class",
|
||||||
|
"password",
|
||||||
|
"column_break_astv",
|
||||||
|
"description",
|
||||||
|
"section_break_glxh",
|
||||||
|
"date",
|
||||||
|
"timezone",
|
||||||
|
"column_break_spvt",
|
||||||
|
"start_time",
|
||||||
|
"duration",
|
||||||
|
"section_break_yrpq",
|
||||||
|
"start_url",
|
||||||
|
"column_break_yokr",
|
||||||
|
"join_url"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Text",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Date",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "start_time",
|
||||||
|
"fieldtype": "Time",
|
||||||
|
"label": "Start Time",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "duration",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Duration",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "timezone",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Timezone",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "class",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Class",
|
||||||
|
"options": "LMS Class"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "host",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Host",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_astv",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_glxh",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Date and Time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_spvt",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_yrpq",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "start_url",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Start URL",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_yokr",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "join_url",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Join URL",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "password",
|
||||||
|
"fieldtype": "Password",
|
||||||
|
"label": "Password"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-03-02 17:47:07.807968",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "LMS Live Class",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Moderator",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
9
lms/lms/doctype/lms_live_class/lms_live_class.py
Normal file
9
lms/lms/doctype/lms_live_class/lms_live_class.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class LMSLiveClass(Document):
|
||||||
|
pass
|
||||||
9
lms/lms/doctype/lms_live_class/test_lms_live_class.py
Normal file
9
lms/lms/doctype/lms_live_class/test_lms_live_class.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2023, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestLMSLiveClass(FrappeTestCase):
|
||||||
|
pass
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"enable",
|
"enable",
|
||||||
"sb_00",
|
"sb_00",
|
||||||
|
"account_id",
|
||||||
"client_id",
|
"client_id",
|
||||||
"client_secret"
|
"client_secret"
|
||||||
],
|
],
|
||||||
@@ -36,11 +37,16 @@
|
|||||||
"fieldtype": "Password",
|
"fieldtype": "Password",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Client Secret"
|
"label": "Client Secret"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "account_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Account ID"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2023-02-27 14:30:28.696814",
|
"modified": "2023-03-01 17:15:59.722497",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Zoom Settings",
|
"name": "Zoom Settings",
|
||||||
|
|||||||
@@ -1776,7 +1776,7 @@ li {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
font-size: var(--text-base) !important;
|
font-size: var(--text-sm) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header, .modal-body {
|
.modal-header, .modal-body {
|
||||||
@@ -1790,11 +1790,14 @@ li {
|
|||||||
.modal-footer {
|
.modal-footer {
|
||||||
padding: 0.75rem 1.5rem !important;
|
padding: 0.75rem 1.5rem !important;
|
||||||
border-top: none !important;
|
border-top: none !important;
|
||||||
background-color: var(--gray-200) !important;
|
background-color: var(--gray-50) !important;
|
||||||
|
justify-content: flex-end !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header .modal-title {
|
.modal-header .modal-title {
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
|
line-height: 1.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frappe-chart .title {
|
.frappe-chart .title {
|
||||||
@@ -1977,3 +1980,7 @@ select {
|
|||||||
.common-page-style .tooltip-content {
|
.common-page-style .tooltip-content {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resize-none {
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|||||||
@@ -191,9 +191,41 @@
|
|||||||
{% macro LiveClassSection(class_info) %}
|
{% macro LiveClassSection(class_info) %}
|
||||||
<div>
|
<div>
|
||||||
{% if is_moderator %}
|
{% if is_moderator %}
|
||||||
<button class="btn btn-secondary btn-sm" id="create-live-class">
|
<button class="btn btn-secondary btn-sm" id="open-class-modal">
|
||||||
{{ _("Create a Live Class") }}
|
{{ _("Create a Live Class") }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="modal fade live-class-modal" id="live-class-modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<div class="modal-title">{{ _("Live Class Details") }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="live-class-form" id="live-class-form"></form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-secondary btn-sm mr-2" data-dismiss="modal" aria-label="Close">
|
||||||
|
{{ _("Discard") }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="btn btn-primary btn-sm" id="create-live-class">
|
||||||
|
{{ _("Submit") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{%- block script %}
|
||||||
|
{{ super() }}
|
||||||
|
{{ include_script('controls.bundle.js') }}
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -11,8 +11,16 @@ frappe.ready(() => {
|
|||||||
update_course(e);
|
update_course(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if ($("#live-class-form").length) {
|
||||||
|
make_live_class_form();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#open-class-modal").click((e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
$("#live-class-modal").modal("show");
|
||||||
|
});
|
||||||
|
|
||||||
$("#create-live-class").click((e) => {
|
$("#create-live-class").click((e) => {
|
||||||
console.log("call");
|
|
||||||
create_live_class(e);
|
create_live_class(e);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -75,14 +83,77 @@ const update_course = (e) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const create_live_class = (e) => {
|
const create_live_class = (e) => {
|
||||||
console.log("call");
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "lms.lms.doctype.lms_class.lms_class.create_live_class",
|
method: "lms.lms.doctype.lms_class.lms_class.create_live_class",
|
||||||
args: {
|
args: {
|
||||||
class_name: $(".class-details").data("class"),
|
class_name: $(".class-details").data("class"),
|
||||||
|
title: $("input[data-fieldname='meeting_title']").val(),
|
||||||
|
duration: $("input[data-fieldname='meeting_duration']").val(),
|
||||||
|
date: $("input[data-fieldname='meeting_date']").val(),
|
||||||
|
time: $("input[data-fieldname='meeting_time']").val(),
|
||||||
|
description: $(
|
||||||
|
"textarea[data-fieldname='meeting_description']"
|
||||||
|
).val(),
|
||||||
},
|
},
|
||||||
callback: (data) => {
|
callback: (data) => {
|
||||||
console.log(data);
|
$("#live-class-modal").modal("hide");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const make_live_class_form = (e) => {
|
||||||
|
this.field_group = new frappe.ui.FieldGroup({
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldname: "meeting_title",
|
||||||
|
fieldtype: "Data",
|
||||||
|
options: "",
|
||||||
|
label: "Title",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_time",
|
||||||
|
fieldtype: "Datetime",
|
||||||
|
options: "",
|
||||||
|
label: "Date and Time",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_col",
|
||||||
|
fieldtype: "Column Break",
|
||||||
|
options: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_date",
|
||||||
|
fieldtype: "Date",
|
||||||
|
options: "",
|
||||||
|
label: "Date",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_duration",
|
||||||
|
fieldtype: "Int",
|
||||||
|
options: "",
|
||||||
|
label: "Duration (in Minutes)",
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_sec",
|
||||||
|
fieldtype: "Section Break",
|
||||||
|
options: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldname: "meeting_description",
|
||||||
|
fieldtype: "Small Text",
|
||||||
|
options: "",
|
||||||
|
max_height: 100,
|
||||||
|
min_lines: 5,
|
||||||
|
label: "Description",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: $("#live-class-form").get(0),
|
||||||
|
});
|
||||||
|
this.field_group.make();
|
||||||
|
$("#live-class-form .form-section:last").removeClass("empty-section");
|
||||||
|
$("#live-class-form .frappe-control").removeClass("hide-control");
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user