Compare commits
1170 Commits
version-13
...
version-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
919a5265c2 | ||
|
|
507d08f37c | ||
|
|
40c295aa37 | ||
|
|
2905a6af1a | ||
|
|
4cc27adb8b | ||
|
|
a5d000f702 | ||
|
|
39aa1d443d | ||
|
|
4b4086afb3 | ||
|
|
b0bb7d32ca | ||
|
|
ff1bd91223 | ||
|
|
3dad3580bb | ||
|
|
8f687145be | ||
|
|
9c405edd09 | ||
|
|
fe791dc478 | ||
|
|
42417621fa | ||
|
|
d3b3d85c84 | ||
|
|
b700013704 | ||
|
|
bac229c731 | ||
|
|
28043e634b | ||
|
|
b672108155 | ||
|
|
5e569ab0e6 | ||
|
|
43a07e53a6 | ||
|
|
fbd83196fc | ||
|
|
465f4e1e96 | ||
|
|
43d409ce64 | ||
|
|
a5fc52ec29 | ||
|
|
a9b06575d0 | ||
|
|
3070cbed3c | ||
|
|
d712881e16 | ||
|
|
991dc7f8c8 | ||
|
|
6b6c8da785 | ||
|
|
f40fbaed3e | ||
|
|
4973386dd0 | ||
|
|
13536b8bad | ||
|
|
caea7e334c | ||
|
|
b248774774 | ||
|
|
7a9d6325d5 | ||
|
|
b0d0b41502 | ||
|
|
30c89cb13c | ||
|
|
9175737b9c | ||
|
|
7ae772205a | ||
|
|
00b0a20c83 | ||
|
|
6604866342 | ||
|
|
881c3d943a | ||
|
|
d5118cc91f | ||
|
|
ac74cbdf72 | ||
|
|
01f7fc3cff | ||
|
|
85c850e5bf | ||
|
|
67dfffdd58 | ||
|
|
ae4aadb8d3 | ||
|
|
e5dc2bad6a | ||
|
|
0e2fabf139 | ||
|
|
c45a372e83 | ||
|
|
98ecb4c27c | ||
|
|
9023094326 | ||
|
|
497de05db2 | ||
|
|
cb3224664e | ||
|
|
9b532a5470 | ||
|
|
f1f9d9790b | ||
|
|
96190910a7 | ||
|
|
6484763d37 | ||
|
|
6f1e7624ec | ||
|
|
eef5bd6062 | ||
|
|
de60fbb25a | ||
|
|
fd9a638879 | ||
|
|
ddcb718a3a | ||
|
|
a17a7453e7 | ||
|
|
479be0b8ee | ||
|
|
6f40c357b3 | ||
|
|
81db6c544d | ||
|
|
be4e3aa963 | ||
|
|
6da0c07a3d | ||
|
|
b4ad10ca35 | ||
|
|
2388b878dc | ||
|
|
8cdaa7877a | ||
|
|
d314287883 | ||
|
|
b70dfc8e82 | ||
|
|
a5a7184f9a | ||
|
|
4e019d0a43 | ||
|
|
8453b54360 | ||
|
|
9f9dfdb26d | ||
|
|
9fd4984247 | ||
|
|
9ebd64f47d | ||
|
|
4316a37ed6 | ||
|
|
2d745460e8 | ||
|
|
b5258b6d9f | ||
|
|
41b076c0db | ||
|
|
9d65e5e398 | ||
|
|
7250bf7d65 | ||
|
|
4d7b247378 | ||
|
|
0aaa58cd54 | ||
|
|
014b85f12c | ||
|
|
929f97cb72 | ||
|
|
de9cb935ee | ||
|
|
9aafc176e4 | ||
|
|
0488ae8305 | ||
|
|
60fd317d98 | ||
|
|
e54435d85d | ||
|
|
3a23b91c90 | ||
|
|
69591577bf | ||
|
|
e56afba6d3 | ||
|
|
98536ce4c7 | ||
|
|
05282178dd | ||
|
|
1af547288c | ||
|
|
b4af82acbc | ||
|
|
50fbe00d23 | ||
|
|
b44428677e | ||
|
|
d67faa1610 | ||
|
|
7b3f4c29d8 | ||
|
|
a49871c5b1 | ||
|
|
e4005792af | ||
|
|
8c0c09a21b | ||
|
|
a9b05f4256 | ||
|
|
cb6013a7a6 | ||
|
|
bb23b78a4f | ||
|
|
243277012f | ||
|
|
c9ed8a4b03 | ||
|
|
d413acaef3 | ||
|
|
d6aad6cd74 | ||
|
|
ca45e43003 | ||
|
|
ad39530705 | ||
|
|
a6c2378b56 | ||
|
|
c073d2201d | ||
|
|
6d70de2eb1 | ||
|
|
48982e8f4a | ||
|
|
397128f980 | ||
|
|
1d77fd3f94 | ||
|
|
60e78e8e74 | ||
|
|
4a9ccc6fde | ||
|
|
a707095fae | ||
|
|
d4f662f65e | ||
|
|
509b1365d9 | ||
|
|
d0b236e381 | ||
|
|
fe98265636 | ||
|
|
3f7d1b1e83 | ||
|
|
52cde329c1 | ||
|
|
68b2dd6147 | ||
|
|
5fa0d022dc | ||
|
|
d996a5c53f | ||
|
|
b6dfc6ed4d | ||
|
|
c7c2ba83f3 | ||
|
|
2bffabff05 | ||
|
|
697e81df10 | ||
|
|
f1b791845b | ||
|
|
6310845cdd | ||
|
|
230cca63f3 | ||
|
|
af9f4d4b1e | ||
|
|
0111ff9c99 | ||
|
|
12bec14c92 | ||
|
|
174ea1ddd4 | ||
|
|
038a7463e1 | ||
|
|
a702909216 | ||
|
|
8effd5614f | ||
|
|
a02365c223 | ||
|
|
2d589aefa2 | ||
|
|
bc2dc679a8 | ||
|
|
f2432d78ee | ||
|
|
f27eecce1f | ||
|
|
caf967f2e2 | ||
|
|
eecc9b53df | ||
|
|
8e12cae91f | ||
|
|
12c5ad54e7 | ||
|
|
c20fa7e093 | ||
|
|
4c83264c4a | ||
|
|
f592cf08d8 | ||
|
|
bf0cb25a88 | ||
|
|
2ff3d83d8f | ||
|
|
3f5c3e89c8 | ||
|
|
a1bb7962bc | ||
|
|
bf5cc5e1d1 | ||
|
|
d840d2fc18 | ||
|
|
1e458921e8 | ||
|
|
55feb41998 | ||
|
|
a7dbdd844b | ||
|
|
f3d6ad6c84 | ||
|
|
a0255e1743 | ||
|
|
1046d28092 | ||
|
|
affd2b47bd | ||
|
|
814870fd69 | ||
|
|
50c1a566a8 | ||
|
|
47783997c6 | ||
|
|
70d8505596 | ||
|
|
6e8cf9ca25 | ||
|
|
c9cda6c6f5 | ||
|
|
6c4d3ea37e | ||
|
|
8ad0e99b3c | ||
|
|
60277ed6e9 | ||
|
|
7b570420ca | ||
|
|
9205b59e29 | ||
|
|
685c5babe5 | ||
|
|
681dc8fbc1 | ||
|
|
7c623b1a8d | ||
|
|
277c089adc | ||
|
|
cfc3c231ff | ||
|
|
1000a22490 | ||
|
|
65c0ebac50 | ||
|
|
80843ec44b | ||
|
|
be23220e01 | ||
|
|
c82c10d17e | ||
|
|
5918b8be60 | ||
|
|
647a0a8ff1 | ||
|
|
bf3c6bc6be | ||
|
|
cc7832614b | ||
|
|
d5387a0d1a | ||
|
|
5d7ad973a2 | ||
|
|
0fcea692c7 | ||
|
|
db71f1271b | ||
|
|
3fde923190 | ||
|
|
4f97760e8a | ||
|
|
5614a6203f | ||
|
|
5727b7cd73 | ||
|
|
1c0644aa7a | ||
|
|
23e6ebe8ee | ||
|
|
5602c0b6c3 | ||
|
|
8e59c10b90 | ||
|
|
90587b0508 | ||
|
|
153a8428f7 | ||
|
|
3d00d96716 | ||
|
|
fb9824301f | ||
|
|
33f4e82399 | ||
|
|
0d99269109 | ||
|
|
8098532215 | ||
|
|
24e9f46e2f | ||
|
|
7c3b40f9d5 | ||
|
|
29860583f4 | ||
|
|
9c00a5561a | ||
|
|
82b8853f39 | ||
|
|
c4ab91a565 | ||
|
|
87e5096f5d | ||
|
|
1a07021bbf | ||
|
|
6ab4f15d0c | ||
|
|
f137f8e048 | ||
|
|
04501143ec | ||
|
|
07276f5c17 | ||
|
|
a9bd01b34e | ||
|
|
93db82305f | ||
|
|
ce09f27373 | ||
|
|
ffd9d56896 | ||
|
|
833e714a1f | ||
|
|
e402f322f6 | ||
|
|
2a0636b32b | ||
|
|
677dc59399 | ||
|
|
db408b21d2 | ||
|
|
fe08f4cf09 | ||
|
|
ccb7c1485b | ||
|
|
4a76f42e35 | ||
|
|
6f31d50a27 | ||
|
|
018c26b885 | ||
|
|
f9f70f208f | ||
|
|
b940ddca25 | ||
|
|
cd82527ef3 | ||
|
|
f600f016ae | ||
|
|
27101ffd31 | ||
|
|
9376b0b010 | ||
|
|
e2c1f6aa2d | ||
|
|
e2287cc73c | ||
|
|
c350ce1b60 | ||
|
|
09dbe0fed7 | ||
|
|
12b3d16662 | ||
|
|
1676329eb2 | ||
|
|
86434ea320 | ||
|
|
de6b4f7fb2 | ||
|
|
6e488cba3e | ||
|
|
03a6cc85fe | ||
|
|
b8b32681bf | ||
|
|
7f67c6c6d9 | ||
|
|
0487b2c987 | ||
|
|
d5f10db250 | ||
|
|
74df0f19cf | ||
|
|
47c19b4e3d | ||
|
|
b197e36ba7 | ||
|
|
4b049dcf71 | ||
|
|
04ed7f412f | ||
|
|
ac64e59c43 | ||
|
|
d5524a8d67 | ||
|
|
bc350a2661 | ||
|
|
575fb623ba | ||
|
|
b7783659c9 | ||
|
|
bddd4743a7 | ||
|
|
ce8ac15faa | ||
|
|
46e7c83fae | ||
|
|
3dcb55f603 | ||
|
|
fb3e0832d6 | ||
|
|
982d6c9045 | ||
|
|
a061a89ee7 | ||
|
|
00bb71f714 | ||
|
|
e23cfac5a7 | ||
|
|
ed651959c1 | ||
|
|
01a1632a5a | ||
|
|
c2a6697f72 | ||
|
|
211775dba5 | ||
|
|
2ba85ba6a7 | ||
|
|
3b3f1d692f | ||
|
|
d90bb1e5ea | ||
|
|
0c14a1ab4c | ||
|
|
ea27acc683 | ||
|
|
b2122e7707 | ||
|
|
9cdc8a50f6 | ||
|
|
03620be7bb | ||
|
|
55296cd9cc | ||
|
|
5fefea1434 | ||
|
|
66dbe68a15 | ||
|
|
066e2ddc69 | ||
|
|
59f08ad4da | ||
|
|
551936e7c4 | ||
|
|
4660240395 | ||
|
|
5d0a50242e | ||
|
|
141a778c9a | ||
|
|
d83d6cf2d8 | ||
|
|
71dc32098e | ||
|
|
6e47b4a941 | ||
|
|
8479e90aeb | ||
|
|
24276b779d | ||
|
|
47e254ed9b | ||
|
|
9c021ef3b1 | ||
|
|
c284e95dc8 | ||
|
|
39663a872c | ||
|
|
14cefca735 | ||
|
|
55c56207c2 | ||
|
|
79d9f31db7 | ||
|
|
845b906851 | ||
|
|
5d2b19cc43 | ||
|
|
a5bc30f776 | ||
|
|
cce77a475a | ||
|
|
13a26321f5 | ||
|
|
e7a2eb7373 | ||
|
|
1cc168404a | ||
|
|
cef3d21ab3 | ||
|
|
d0ac0e4523 | ||
|
|
abaa7754a6 | ||
|
|
02e875cbdc | ||
|
|
a218257952 | ||
|
|
7dfcabde5e | ||
|
|
6573602dfc | ||
|
|
3c374f48b3 | ||
|
|
2412ef0260 | ||
|
|
d4dcfcdbc6 | ||
|
|
60aec7c801 | ||
|
|
1862d726ad | ||
|
|
3d27d5f755 | ||
|
|
0b3f76590f | ||
|
|
294832834c | ||
|
|
e3338e0236 | ||
|
|
5c7ae55775 | ||
|
|
bc8827547e | ||
|
|
7990675c5c | ||
|
|
0182db8030 | ||
|
|
295feccb49 | ||
|
|
b5005f41fe | ||
|
|
37f06a8ba4 | ||
|
|
71d421898d | ||
|
|
bf5a69cee4 | ||
|
|
11e6b8a372 | ||
|
|
d763dba204 | ||
|
|
9e5cd84214 | ||
|
|
14cf0c9ae1 | ||
|
|
7d410e9ec8 | ||
|
|
07c3d423aa | ||
|
|
5d8003549f | ||
|
|
b286daad16 | ||
|
|
caa9144a12 | ||
|
|
3606902753 | ||
|
|
1abb75a58e | ||
|
|
d35c15c384 | ||
|
|
1888209027 | ||
|
|
f3830bfdd5 | ||
|
|
0e1b91f1ec | ||
|
|
8353aa24f3 | ||
|
|
99f1a8dfc3 | ||
|
|
3d8237008f | ||
|
|
22199da7d4 | ||
|
|
d8e11f69cc | ||
|
|
c9a5c0801e | ||
|
|
bb0abe27cd | ||
|
|
7d18e1d928 | ||
|
|
da72513f6a | ||
|
|
6f8c161e03 | ||
|
|
ac1f02971f | ||
|
|
d19538abd2 | ||
|
|
b1ab7e7783 | ||
|
|
35c080fcc2 | ||
|
|
43e89d9dc2 | ||
|
|
547b69dd31 | ||
|
|
1ff8514b22 | ||
|
|
5fffe51c4e | ||
|
|
af9aa3e37b | ||
|
|
d644ee7ccd | ||
|
|
08e3278ca0 | ||
|
|
85feaa00fc | ||
|
|
76ba5c188e | ||
|
|
9941e0e936 | ||
|
|
89206f94f0 | ||
|
|
43128d7ea3 | ||
|
|
6f1026434d | ||
|
|
db35e3e425 | ||
|
|
1d8de792a5 | ||
|
|
0db47dfee1 | ||
|
|
fc086fdbc3 | ||
|
|
8a86d19b79 | ||
|
|
9198302f7e | ||
|
|
d076451ea8 | ||
|
|
c2e9ef59d6 | ||
|
|
100f72de9d | ||
|
|
5a32109d5d | ||
|
|
f5e7934906 | ||
|
|
9800c24939 | ||
|
|
dcd81e0a3f | ||
|
|
6574b55440 | ||
|
|
8474d1c8c4 | ||
|
|
38ae9ab9f5 | ||
|
|
659b35f03e | ||
|
|
414f126f1e | ||
|
|
b336d769c8 | ||
|
|
c173953c6a | ||
|
|
afac45e65f | ||
|
|
f0aa5b8744 | ||
|
|
a101e7b089 | ||
|
|
85903d5385 | ||
|
|
fe80ef9b85 | ||
|
|
961f8c1627 | ||
|
|
6c7fc9b317 | ||
|
|
2afa14d68e | ||
|
|
f6cdac4826 | ||
|
|
bb39999b84 | ||
|
|
70a036e5a7 | ||
|
|
0432751050 | ||
|
|
36b3b1d086 | ||
|
|
6a783e540b | ||
|
|
daeeb693d6 | ||
|
|
a0b06be422 | ||
|
|
4b2ba96435 | ||
|
|
10510e204f | ||
|
|
032749dd01 | ||
|
|
dc65bff772 | ||
|
|
56266a3774 | ||
|
|
93f0f8ab44 | ||
|
|
611cc4d5a1 | ||
|
|
552b0c9616 | ||
|
|
1327b033e6 | ||
|
|
ae42828771 | ||
|
|
1df6319164 | ||
|
|
95012072fc | ||
|
|
c01c248202 | ||
|
|
0093025e5d | ||
|
|
20398cb934 | ||
|
|
2792eb53b7 | ||
|
|
b2f8f796b9 | ||
|
|
39bb3149c9 | ||
|
|
1e610f7fbb | ||
|
|
c760fd5776 | ||
|
|
2162963926 | ||
|
|
c3f3a110c0 | ||
|
|
0e444ab7d3 | ||
|
|
752fe5b4ba | ||
|
|
dce369638a | ||
|
|
911cfe9d2f | ||
|
|
f4e882ba3e | ||
|
|
211c69bb41 | ||
|
|
b592172b82 | ||
|
|
40445cbb94 | ||
|
|
20c93b3a6b | ||
|
|
e2bf324fb4 | ||
|
|
b47ff80e9d | ||
|
|
e10feb3c36 | ||
|
|
b8471dd753 | ||
|
|
125c06952a | ||
|
|
4336839932 | ||
|
|
bbdfaa32e9 | ||
|
|
4f52a73029 | ||
|
|
1a2f693fea | ||
|
|
ab8b76cada | ||
|
|
b5240f0eec | ||
|
|
7777bd02e3 | ||
|
|
fcdd70dcc7 | ||
|
|
4eb5390ad8 | ||
|
|
3b5c47222d | ||
|
|
f97ae4e4e2 | ||
|
|
33db16a1a2 | ||
|
|
6232f8703e | ||
|
|
e6621ad866 | ||
|
|
6a1533191a | ||
|
|
a0782c7bf7 | ||
|
|
2b6436915d | ||
|
|
4c220a67f2 | ||
|
|
110aab00d6 | ||
|
|
8e8111d272 | ||
|
|
53d2e288d4 | ||
|
|
262c1ea371 | ||
|
|
7be9eb09e8 | ||
|
|
5fb7e88318 | ||
|
|
d9c50714f4 | ||
|
|
0a91e5aa05 | ||
|
|
9b70b4212f | ||
|
|
9f525d69b6 | ||
|
|
b09c4753da | ||
|
|
e4b4556210 | ||
|
|
fafd132768 | ||
|
|
67dc6d1f29 | ||
|
|
969cb37cfe | ||
|
|
94c2be9919 | ||
|
|
5089285913 | ||
|
|
e80920ad6c | ||
|
|
d6606ab898 | ||
|
|
41599348c4 | ||
|
|
92a8ce6ef4 | ||
|
|
690a86bb69 | ||
|
|
cde4b61cea | ||
|
|
f361c42a30 | ||
|
|
065646ed5d | ||
|
|
de399166f1 | ||
|
|
a3a4d7fbd0 | ||
|
|
a7c1595978 | ||
|
|
1a8d113ad8 | ||
|
|
72cbbf147f | ||
|
|
a0e6462c13 | ||
|
|
b37f259804 | ||
|
|
2fbe5dacb2 | ||
|
|
3150cf2510 | ||
|
|
3b1b375d5b | ||
|
|
3c0a29d4c7 | ||
|
|
817bc4441f | ||
|
|
07e1aaaa66 | ||
|
|
35b77a8908 | ||
|
|
d5b4af95ff | ||
|
|
fc6a50b13f | ||
|
|
8415abec6e | ||
|
|
ea9ca67d1e | ||
|
|
8201506c5f | ||
|
|
5fc879b0ef | ||
|
|
bfde847045 | ||
|
|
d96e3f4f9f | ||
|
|
0593a9fb30 | ||
|
|
c03cca21e8 | ||
|
|
170b1b0dcc | ||
|
|
cb9c7966d9 | ||
|
|
b9c2222951 | ||
|
|
dfef5ca26c | ||
|
|
bce60a8657 | ||
|
|
2b244bb4f4 | ||
|
|
31b08eb545 | ||
|
|
9b7817a57f | ||
|
|
bb0f3d5962 | ||
|
|
23c78d5801 | ||
|
|
cd3236976f | ||
|
|
e6096bf9ed | ||
|
|
a502603915 | ||
|
|
7566565f55 | ||
|
|
f56f0b5366 | ||
|
|
34870b4625 | ||
|
|
3ee592a989 | ||
|
|
d6d7e05b51 | ||
|
|
07eaec2ded | ||
|
|
296a7e6023 | ||
|
|
54827edd7e | ||
|
|
d87fb81cf3 | ||
|
|
99a7c47798 | ||
|
|
080a02589c | ||
|
|
d1e7549da9 | ||
|
|
8e1ef1dc77 | ||
|
|
619a2f9d80 | ||
|
|
926444767b | ||
|
|
6bf4020ad1 | ||
|
|
cb63ad8ed2 | ||
|
|
458ed9ad95 | ||
|
|
a11df1a237 | ||
|
|
352d4b9ab9 | ||
|
|
275ded0658 | ||
|
|
c8f3350761 | ||
|
|
e3112d8dcf | ||
|
|
5009900c0e | ||
|
|
0f60f1a58b | ||
|
|
8f88518187 | ||
|
|
05bcead7d1 | ||
|
|
6268989306 | ||
|
|
43ba835b52 | ||
|
|
9240bc9130 | ||
|
|
fb70aee055 | ||
|
|
7bf69eb77d | ||
|
|
7ded9a23be | ||
|
|
281af15d65 | ||
|
|
ec31c96120 | ||
|
|
b970eb1541 | ||
|
|
7f6b90d5f4 | ||
|
|
d28096ede6 | ||
|
|
12b2b0d0eb | ||
|
|
a0e281fb30 | ||
|
|
37e8c3ab84 | ||
|
|
16cb564a6a | ||
|
|
cd88657bc9 | ||
|
|
d82a32b06a | ||
|
|
094bd943ee | ||
|
|
1564de346d | ||
|
|
a5268af269 | ||
|
|
565d9e093c | ||
|
|
cb75606b05 | ||
|
|
7c36294359 | ||
|
|
ad084389cc | ||
|
|
aa8c65a00a | ||
|
|
95ec31e238 | ||
|
|
b5f3a0fa98 | ||
|
|
36bf0d7fb9 | ||
|
|
1719887ac4 | ||
|
|
7f149bf3b1 | ||
|
|
f592e6655f | ||
|
|
46399c7b13 | ||
|
|
0ec66f0f2d | ||
|
|
96c8b64e26 | ||
|
|
bd0b7a8286 | ||
|
|
263b3ec476 | ||
|
|
46075130ab | ||
|
|
0f314bc4e3 | ||
|
|
2b45e7d77c | ||
|
|
987c3e5fe2 | ||
|
|
1e1580d58a | ||
|
|
da9667e1f7 | ||
|
|
9e42fdd786 | ||
|
|
de19abbc5e | ||
|
|
5448a99177 | ||
|
|
f93d7b0193 | ||
|
|
d3bda1b5d0 | ||
|
|
1c49e78b13 | ||
|
|
9d373d4091 | ||
|
|
26edfc7bb9 | ||
|
|
b527c9b160 | ||
|
|
0356b8e2d2 | ||
|
|
1bf093d1b9 | ||
|
|
a78d127702 | ||
|
|
028c0725c3 | ||
|
|
424d35bf42 | ||
|
|
1effdd8482 | ||
|
|
a780b7520f | ||
|
|
233bf36ef6 | ||
|
|
616a45ebc5 | ||
|
|
136f16c9cc | ||
|
|
609628b9cf | ||
|
|
1e0d2e0f2a | ||
|
|
da7e52f85f | ||
|
|
4640ce82f6 | ||
|
|
03ff9e0521 | ||
|
|
f566211d82 | ||
|
|
66ddea2031 | ||
|
|
8b5a9e1639 | ||
|
|
9a183d2ef1 | ||
|
|
807f77755e | ||
|
|
c0a851e045 | ||
|
|
96849afcba | ||
|
|
91e71091ef | ||
|
|
f691ebd146 | ||
|
|
f620c431a1 | ||
|
|
019743b245 | ||
|
|
1d8e3de136 | ||
|
|
0f46f6a1c1 | ||
|
|
b5078c1f1f | ||
|
|
88f9114dd6 | ||
|
|
cfcc1b3162 | ||
|
|
ed2fababff | ||
|
|
74210245cf | ||
|
|
a4ec058a81 | ||
|
|
ae2c15fe8c | ||
|
|
d9e44e0f91 | ||
|
|
3e1f29af48 | ||
|
|
7ac3f6d8ef | ||
|
|
e0f2b9c1ab | ||
|
|
3785e7066e | ||
|
|
157f6b45e9 | ||
|
|
c8dbc18179 | ||
|
|
1d5d86ef6c | ||
|
|
f1746978b2 | ||
|
|
e1f4dae832 | ||
|
|
94e7f6a1f4 | ||
|
|
d3ea923da7 | ||
|
|
dc27dd5d7e | ||
|
|
26f76acdd2 | ||
|
|
719a32e4a5 | ||
|
|
cdf004cdec | ||
|
|
9d5f495842 | ||
|
|
8204fa8ee7 | ||
|
|
9f9dd25f91 | ||
|
|
bd929bfa3f | ||
|
|
5916695d30 | ||
|
|
cca42dca49 | ||
|
|
33065c0ed3 | ||
|
|
603eddf878 | ||
|
|
cda26ab248 | ||
|
|
e73d59d538 | ||
|
|
893fac348d | ||
|
|
642b28bad9 | ||
|
|
b9db14ad44 | ||
|
|
0c23ead398 | ||
|
|
e9009dcb9c | ||
|
|
0d0e64f930 | ||
|
|
b1bdf6516e | ||
|
|
eca4f89d8b | ||
|
|
199cbcf33f | ||
|
|
1f4c7fcb0f | ||
|
|
59b8c781e3 | ||
|
|
9133a3f50b | ||
|
|
b9f4ba6527 | ||
|
|
7b5fe82de9 | ||
|
|
d74db85b82 | ||
|
|
915bbdf247 | ||
|
|
b3412b42b7 | ||
|
|
c6b917fa83 | ||
|
|
c286afa45f | ||
|
|
c74faa3340 | ||
|
|
fa4e8a1ea6 | ||
|
|
42c664a379 | ||
|
|
b84a955ee1 | ||
|
|
211ca3111f | ||
|
|
935e2a976e | ||
|
|
5d38b4dfa0 | ||
|
|
cbf852828c | ||
|
|
7d8b366a73 | ||
|
|
4ad457d568 | ||
|
|
2ab0cb2787 | ||
|
|
4feab27c59 | ||
|
|
3c1449c898 | ||
|
|
7d029c5305 | ||
|
|
13274c01d7 | ||
|
|
f37229c202 | ||
|
|
61f9ff6892 | ||
|
|
3df08b99b3 | ||
|
|
f9748cb723 | ||
|
|
437f637be9 | ||
|
|
7d2b86c548 | ||
|
|
c10ff68ee7 | ||
|
|
b2a8dd13f6 | ||
|
|
fb8ab132e9 | ||
|
|
7ce2e45719 | ||
|
|
c6d15d2605 | ||
|
|
8d57ea4216 | ||
|
|
fdf1bbd1e2 | ||
|
|
662813b378 | ||
|
|
8709ae7113 | ||
|
|
ac22b71171 | ||
|
|
328d1d6546 | ||
|
|
11277d2ef3 | ||
|
|
54dc6102a6 | ||
|
|
c0c9b50a5f | ||
|
|
e780a6fc7c | ||
|
|
fd89b288e5 | ||
|
|
a05c3a5e94 | ||
|
|
43e91c9275 | ||
|
|
26dd2935d7 | ||
|
|
f653871e3a | ||
|
|
89b91a081c | ||
|
|
246b26079b | ||
|
|
41a9e422d5 | ||
|
|
0704d7f4a4 | ||
|
|
1e66742d02 | ||
|
|
d55f827d93 | ||
|
|
40c68d4d63 | ||
|
|
5c58ddc800 | ||
|
|
94171cbc0e | ||
|
|
cf8549bb28 | ||
|
|
a291d73828 | ||
|
|
a6156ec863 | ||
|
|
be583cc86d | ||
|
|
00c55f5e82 | ||
|
|
c9fd1f5252 | ||
|
|
992d1065ac | ||
|
|
247017d19a | ||
|
|
b9fe05c27f | ||
|
|
b01a2cfd79 | ||
|
|
37f2e7eeda | ||
|
|
4ba6ea9190 | ||
|
|
7c1c061681 | ||
|
|
47e4175c9a | ||
|
|
023fd73b86 | ||
|
|
f7ba72217b | ||
|
|
47b9b1dcd6 | ||
|
|
f3adbfef55 | ||
|
|
bc9225f8ed | ||
|
|
62ac0089c9 | ||
|
|
af059141f0 | ||
|
|
efe73a21d9 | ||
|
|
adad299724 | ||
|
|
15b832eefb | ||
|
|
434b6fdc8f | ||
|
|
99c886a9cb | ||
|
|
7d18215bc9 | ||
|
|
7d7f674d9e | ||
|
|
46861486c9 | ||
|
|
d959cacd15 | ||
|
|
d8d0ce5b45 | ||
|
|
b94a4ef9d6 | ||
|
|
212ed8e428 | ||
|
|
6d14bc2f54 | ||
|
|
9dc10c3756 | ||
|
|
316e7be51e | ||
|
|
14842a6d56 | ||
|
|
ce7f3ffca9 | ||
|
|
4c969420f3 | ||
|
|
36aca1e664 | ||
|
|
3a08f57fb1 | ||
|
|
8f86a8aba6 | ||
|
|
e84e02ff07 | ||
|
|
7ef625c598 | ||
|
|
2819e55a61 | ||
|
|
25d9b08dd5 | ||
|
|
6a760c3019 | ||
|
|
8088a464ad | ||
|
|
e41c83a68c | ||
|
|
2d5b8430bd | ||
|
|
eabbe3e3ed | ||
|
|
c37cdbdc5f | ||
|
|
8a0df559c3 | ||
|
|
dfc1c9c228 | ||
|
|
ad20c1cbf1 | ||
|
|
6f4677b36b | ||
|
|
b3a9274cd3 | ||
|
|
7a7bc3e0db | ||
|
|
425bb54057 | ||
|
|
d82df84cf6 | ||
|
|
7d697f068f | ||
|
|
4499b34b51 | ||
|
|
9c6cc8ecd4 | ||
|
|
668130d443 | ||
|
|
5aba690318 | ||
|
|
7397bf389e | ||
|
|
e1b16e9ae3 | ||
|
|
eb50f6fd8f | ||
|
|
5d6dc16a19 | ||
|
|
64dc76d616 | ||
|
|
301664ce93 | ||
|
|
a4534d8f3e | ||
|
|
f161173cb6 | ||
|
|
c58f5efcdd | ||
|
|
65edd2ce22 | ||
|
|
0723f3091c | ||
|
|
a95204f02e | ||
|
|
6dd12e111d | ||
|
|
6e741bd800 | ||
|
|
d37e53bd8e | ||
|
|
fd3585697b | ||
|
|
e92e6ec1c6 | ||
|
|
f0c89cbbba | ||
|
|
d20f932316 | ||
|
|
e011ef469f | ||
|
|
384850b452 | ||
|
|
e467aba32f | ||
|
|
56c51b73b0 | ||
|
|
1aa82dd03e | ||
|
|
a3b7c67ef9 | ||
|
|
de0200f352 | ||
|
|
999c267dfe | ||
|
|
c9a6c2948e | ||
|
|
041157a85f | ||
|
|
cd009936e2 | ||
|
|
f43dfd47d0 | ||
|
|
7082467352 | ||
|
|
f05d3581c1 | ||
|
|
02e560cb18 | ||
|
|
b2d38ad9d6 | ||
|
|
2743f1adc9 | ||
|
|
5e875b6d71 | ||
|
|
6ccb5e8dbf | ||
|
|
ad42a3d41c | ||
|
|
b40e0068ab | ||
|
|
f8dbbc1d11 | ||
|
|
951070757c | ||
|
|
c2a93c6c02 | ||
|
|
9dc82963c4 | ||
|
|
148fce42ba | ||
|
|
92f1263314 | ||
|
|
5fad44369c | ||
|
|
eb3d7c9615 | ||
|
|
5437a98539 | ||
|
|
d9b26f31f8 | ||
|
|
9916ec132b | ||
|
|
1f3806a7bd | ||
|
|
55a770f129 | ||
|
|
d742d595b2 | ||
|
|
9f575902b0 | ||
|
|
6e49cf14fa | ||
|
|
4d96abb786 | ||
|
|
b6d9470a4e | ||
|
|
6ef86dce8c | ||
|
|
5a33f4c707 | ||
|
|
119c1cbb35 | ||
|
|
2be2646d95 | ||
|
|
0609174a14 | ||
|
|
227ca0ed41 | ||
|
|
cc90ccece2 | ||
|
|
9ab92165c8 | ||
|
|
e1fc0d08b7 | ||
|
|
21dad2d72e | ||
|
|
87fe3e72a2 | ||
|
|
c92220f1e5 | ||
|
|
eabeb612b4 | ||
|
|
900e61291a | ||
|
|
51d9f1943b | ||
|
|
282b07dffc | ||
|
|
6521415ae0 | ||
|
|
2819be4508 | ||
|
|
a60805a971 | ||
|
|
af21d33165 | ||
|
|
e465ad58ca | ||
|
|
cd56fc6179 | ||
|
|
1ef70dd4e4 | ||
|
|
a460ea5194 | ||
|
|
5ebf7b7992 | ||
|
|
c9b50e7db6 | ||
|
|
b9993796a0 | ||
|
|
4fb2241621 | ||
|
|
fa49dc08a3 | ||
|
|
3d939f18df | ||
|
|
0d38b9aab1 | ||
|
|
28207f45cd | ||
|
|
72a5e9eb05 | ||
|
|
91dc99d8ef | ||
|
|
305a34b26d | ||
|
|
67349bbce6 | ||
|
|
242ba89b95 | ||
|
|
295280f8cf | ||
|
|
eb0f4728c4 | ||
|
|
8017715bc7 | ||
|
|
503c74c488 | ||
|
|
32cdaa8baa | ||
|
|
83176a0293 | ||
|
|
5db3d14b17 | ||
|
|
bc6ae25aa6 | ||
|
|
8e3900fab7 | ||
|
|
51b3d59094 | ||
|
|
3585645580 | ||
|
|
1e3c20d118 | ||
|
|
f83f13af59 | ||
|
|
54d0bc56a4 | ||
|
|
c722673dbd | ||
|
|
88d34a9daf | ||
|
|
e91237ec05 | ||
|
|
430a2bc08d | ||
|
|
3bbe5d33d3 | ||
|
|
674c6a1684 | ||
|
|
f34519e3ff | ||
|
|
f047be816a | ||
|
|
593c20d62b | ||
|
|
a4bfad6138 | ||
|
|
89de3c5f7a | ||
|
|
81fb664ad9 | ||
|
|
b65597b884 | ||
|
|
d5423947f3 | ||
|
|
f80db23642 | ||
|
|
935906e328 | ||
|
|
3ed7f68054 | ||
|
|
c894b75f4b | ||
|
|
ef43d6905f | ||
|
|
ab7ebedcd5 | ||
|
|
8ca19afd87 | ||
|
|
70e1e550e3 | ||
|
|
cb0087c433 | ||
|
|
d867f86c08 | ||
|
|
1891029eed | ||
|
|
157090a122 | ||
|
|
0ddf2aad79 | ||
|
|
4f47a5032c | ||
|
|
ae957ab642 | ||
|
|
b69a7022b5 | ||
|
|
c19bd88a8e | ||
|
|
4967b70e7c | ||
|
|
cdfad8cc67 | ||
|
|
dcaccaca35 | ||
|
|
d954262184 | ||
|
|
208720545e | ||
|
|
2cbadc6946 | ||
|
|
ebfa49ed8e | ||
|
|
e70c3ef939 | ||
|
|
0523f701c2 | ||
|
|
c6b46295bc | ||
|
|
16ddfb2a20 | ||
|
|
ca5ea6032c | ||
|
|
b4eda0e7cd | ||
|
|
9d2a9bbd9c | ||
|
|
ef13c8fe8d | ||
|
|
aa6932f547 | ||
|
|
5d3c132e83 | ||
|
|
56f4f6c977 | ||
|
|
77df068fea | ||
|
|
b9a1bf9f24 | ||
|
|
2b348916f4 | ||
|
|
70bc5ea2ec | ||
|
|
eff9e553a1 | ||
|
|
e1bfca1883 | ||
|
|
190dbab3d0 | ||
|
|
a1895b1f78 | ||
|
|
1091774003 | ||
|
|
ae29eb35fd | ||
|
|
5ca0a89789 | ||
|
|
6c6e836f0d | ||
|
|
c971d34d67 | ||
|
|
1b04615bff | ||
|
|
40eb8dec68 | ||
|
|
2556e98c26 | ||
|
|
2204da25ed | ||
|
|
ab1eb6de6b | ||
|
|
16278a72e9 | ||
|
|
573a1f22dc | ||
|
|
da370a7dd2 | ||
|
|
6567660639 | ||
|
|
984054f00f | ||
|
|
9351edebe0 | ||
|
|
36465f0859 | ||
|
|
ef5cf4bd96 | ||
|
|
27e1aec001 | ||
|
|
aa9ef65375 | ||
|
|
c8af6d3672 | ||
|
|
ffe5370264 | ||
|
|
ef0156c068 | ||
|
|
00883170f3 | ||
|
|
fb72704a33 | ||
|
|
8f9cd72975 | ||
|
|
913e87c4f5 | ||
|
|
3bdf6df70d | ||
|
|
5e973b21ae | ||
|
|
23e2611725 | ||
|
|
f8784afd72 | ||
|
|
46a73c8ef1 | ||
|
|
bcd80b616d | ||
|
|
b5d870d885 | ||
|
|
f9ac77606e | ||
|
|
ce554ed53f | ||
|
|
27d48f85a9 | ||
|
|
059ff14e6d | ||
|
|
8cfb3569fe | ||
|
|
1ac3e9b364 | ||
|
|
22c6a3da4f | ||
|
|
737405d449 | ||
|
|
e805ae7757 | ||
|
|
d5cf0fee64 | ||
|
|
48e2ae6e52 | ||
|
|
351e784733 | ||
|
|
6c64181bf4 | ||
|
|
d64e6a12a7 | ||
|
|
ccddc9a404 | ||
|
|
aee35aa66d | ||
|
|
0cf953b3eb | ||
|
|
eb073ebe87 | ||
|
|
c3fac64280 | ||
|
|
28ef8e7db7 | ||
|
|
e3eda95b35 | ||
|
|
894a68823f | ||
|
|
dc3843087e | ||
|
|
aaae9fe8bc | ||
|
|
3dc9fc52a7 | ||
|
|
2d373ba509 | ||
|
|
f200e79dc8 | ||
|
|
ae9518a006 | ||
|
|
96c24e8e5f | ||
|
|
cc0c1cc320 | ||
|
|
9834c83126 | ||
|
|
4b8cb5a4e4 | ||
|
|
7e2f473e22 | ||
|
|
ee9f858933 | ||
|
|
69f322d34c | ||
|
|
91099188f2 | ||
|
|
1e7209bafa | ||
|
|
a23a356bf6 | ||
|
|
5218e134a9 | ||
|
|
55a87999e4 | ||
|
|
6cd7861b2c | ||
|
|
6dfaeddc0a | ||
|
|
4846549a90 | ||
|
|
942837b4d9 | ||
|
|
259b4aabcc | ||
|
|
6200b6a4b0 | ||
|
|
6ee1413996 | ||
|
|
08fff1700f | ||
|
|
fd5b7976e7 | ||
|
|
e17637d27b | ||
|
|
57c69a7d6c | ||
|
|
bf3a496ea3 | ||
|
|
3e46db9c11 | ||
|
|
50c624e305 | ||
|
|
6d4e64059a | ||
|
|
ec879b12b3 | ||
|
|
5ace8f5570 | ||
|
|
2fe5a6b248 | ||
|
|
19f5251736 | ||
|
|
a87e3d66a6 | ||
|
|
62acdde68f | ||
|
|
e214573cd1 | ||
|
|
8fff1cd817 | ||
|
|
20eb0c4d64 | ||
|
|
ff05292bdb | ||
|
|
326bc88f19 | ||
|
|
f0a7612369 | ||
|
|
515be5de33 | ||
|
|
e1e7354d85 | ||
|
|
2ec6a06204 | ||
|
|
9150def93e | ||
|
|
730f1a6c47 | ||
|
|
7c12d094c5 | ||
|
|
8aceaf0595 | ||
|
|
5a1bdc156d | ||
|
|
aa81426189 | ||
|
|
7a5d39cc94 | ||
|
|
b1b8f202c9 | ||
|
|
fcfda68d88 | ||
|
|
e966e4c7ed | ||
|
|
42455c72f9 | ||
|
|
ef5f49016e | ||
|
|
6417dfb4a1 | ||
|
|
40985da9ca | ||
|
|
e07c26c17d | ||
|
|
9f2cba49ee | ||
|
|
6e3de21f6e | ||
|
|
a6bdbd7cf1 | ||
|
|
b822428d52 | ||
|
|
03a7df1c98 | ||
|
|
08b7290070 | ||
|
|
6188c698e3 | ||
|
|
7d9b60c16a | ||
|
|
df3c5060e9 | ||
|
|
ff702c9d14 | ||
|
|
ea06fe8cf8 | ||
|
|
f8b0d9b180 | ||
|
|
d3a3e7066d | ||
|
|
45e3964285 | ||
|
|
4edf4936c1 | ||
|
|
067be4973d | ||
|
|
6a3442d058 | ||
|
|
0b83307f7d | ||
|
|
f36042f537 | ||
|
|
668a5d6334 | ||
|
|
eb59713b65 | ||
|
|
fbc6187062 | ||
|
|
274ecaa222 | ||
|
|
051196179d | ||
|
|
cddfc4550d | ||
|
|
465bc3b24a | ||
|
|
38087f0b02 | ||
|
|
3237a539f7 | ||
|
|
828d5764d0 | ||
|
|
15fc3742d2 | ||
|
|
8489b1d943 | ||
|
|
682188540c | ||
|
|
8f740d70e0 | ||
|
|
bc80c2200d | ||
|
|
fd524f627e | ||
|
|
59b3b68bde | ||
|
|
943c8eabbf | ||
|
|
b83918c2aa | ||
|
|
5b4db8fd54 | ||
|
|
c8d7ac48ea | ||
|
|
3cd4e64957 | ||
|
|
2f994628c3 | ||
|
|
f68fc02e57 | ||
|
|
0637b9c8f8 | ||
|
|
7001ddc96f | ||
|
|
fe31a64175 | ||
|
|
cd0cc2b501 | ||
|
|
ebcb3c5466 | ||
|
|
a1d0f3948a | ||
|
|
22f5508bea | ||
|
|
63c4f708c3 | ||
|
|
52fd891370 | ||
|
|
fb447a30e4 | ||
|
|
e0c73e26ee | ||
|
|
d5da5bd8aa | ||
|
|
d84302682e | ||
|
|
c963e93b52 | ||
|
|
c96e3ee2f9 | ||
|
|
e014c94446 | ||
|
|
6c747ff8b4 | ||
|
|
ffd9e9d48e | ||
|
|
3328359ba4 | ||
|
|
f1157895db | ||
|
|
1277cfed64 | ||
|
|
102fa9c0a8 | ||
|
|
7cd57cadb2 |
@@ -9,7 +9,7 @@ root = true
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = tab
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
@@ -26,4 +26,4 @@ indent_style = tab
|
|||||||
|
|
||||||
# HTML, CSS, javascript, JSON and YAML
|
# HTML, CSS, javascript, JSON and YAML
|
||||||
[*.{html,css,js,json,yml,yaml}]
|
[*.{html,css,js,json,yml,yaml}]
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
|
|||||||
37
.flake8
Normal file
37
.flake8
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
E121,
|
||||||
|
E126,
|
||||||
|
E127,
|
||||||
|
E128,
|
||||||
|
E203,
|
||||||
|
E225,
|
||||||
|
E226,
|
||||||
|
E231,
|
||||||
|
E241,
|
||||||
|
E251,
|
||||||
|
E261,
|
||||||
|
E265,
|
||||||
|
E302,
|
||||||
|
E303,
|
||||||
|
E305,
|
||||||
|
E402,
|
||||||
|
E501,
|
||||||
|
E741,
|
||||||
|
W291,
|
||||||
|
W292,
|
||||||
|
W293,
|
||||||
|
W391,
|
||||||
|
W503,
|
||||||
|
W504,
|
||||||
|
F403,
|
||||||
|
B007,
|
||||||
|
B950,
|
||||||
|
W191,
|
||||||
|
E124, # closing bracket, irritating while writing QB code
|
||||||
|
E131, # continuation line unaligned for hanging indent
|
||||||
|
E123, # closing bracket does not match indentation of opening bracket's line
|
||||||
|
E101, # ensured by use of black
|
||||||
|
|
||||||
|
max-line-length = 200
|
||||||
|
exclude=.github/helper/semgrep_rules
|
||||||
74
.github/helper/flake8.conf
vendored
Normal file
74
.github/helper/flake8.conf
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
B001,
|
||||||
|
B007,
|
||||||
|
B009,
|
||||||
|
B010,
|
||||||
|
B950,
|
||||||
|
E101,
|
||||||
|
E111,
|
||||||
|
E114,
|
||||||
|
E116,
|
||||||
|
E117,
|
||||||
|
E121,
|
||||||
|
E122,
|
||||||
|
E123,
|
||||||
|
E124,
|
||||||
|
E125,
|
||||||
|
E126,
|
||||||
|
E127,
|
||||||
|
E128,
|
||||||
|
E131,
|
||||||
|
E201,
|
||||||
|
E202,
|
||||||
|
E203,
|
||||||
|
E211,
|
||||||
|
E221,
|
||||||
|
E222,
|
||||||
|
E223,
|
||||||
|
E224,
|
||||||
|
E225,
|
||||||
|
E226,
|
||||||
|
E228,
|
||||||
|
E231,
|
||||||
|
E241,
|
||||||
|
E242,
|
||||||
|
E251,
|
||||||
|
E261,
|
||||||
|
E262,
|
||||||
|
E265,
|
||||||
|
E266,
|
||||||
|
E271,
|
||||||
|
E272,
|
||||||
|
E273,
|
||||||
|
E274,
|
||||||
|
E301,
|
||||||
|
E302,
|
||||||
|
E303,
|
||||||
|
E305,
|
||||||
|
E306,
|
||||||
|
E402,
|
||||||
|
E501,
|
||||||
|
E502,
|
||||||
|
E701,
|
||||||
|
E702,
|
||||||
|
E703,
|
||||||
|
E741,
|
||||||
|
F401,
|
||||||
|
F403,
|
||||||
|
F405,
|
||||||
|
W191,
|
||||||
|
W291,
|
||||||
|
W292,
|
||||||
|
W293,
|
||||||
|
W391,
|
||||||
|
W503,
|
||||||
|
W504,
|
||||||
|
E711,
|
||||||
|
E129,
|
||||||
|
F841,
|
||||||
|
E713,
|
||||||
|
E712,
|
||||||
|
|
||||||
|
|
||||||
|
max-line-length = 200
|
||||||
46
.github/helper/install.sh
vendored
Normal file
46
.github/helper/install.sh
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd ~ || exit
|
||||||
|
|
||||||
|
echo "Setting Up Bench..."
|
||||||
|
|
||||||
|
pip install frappe-bench
|
||||||
|
bench -v init frappe-bench --skip-assets --python "$(which python)"
|
||||||
|
cd ./frappe-bench || exit
|
||||||
|
|
||||||
|
bench -v setup requirements
|
||||||
|
|
||||||
|
echo "Setting Up LMS App..."
|
||||||
|
bench get-app lms "${GITHUB_WORKSPACE}"
|
||||||
|
|
||||||
|
echo "Setting Up Sites & Database..."
|
||||||
|
|
||||||
|
mkdir ~/frappe-bench/sites/lms.test
|
||||||
|
cp "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/lms.test/site_config.json
|
||||||
|
|
||||||
|
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "SET GLOBAL character_set_server = 'utf8mb4'";
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'";
|
||||||
|
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "CREATE DATABASE test_lms";
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "CREATE USER 'test_lms'@'localhost' IDENTIFIED BY 'test_lms'";
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "GRANT ALL PRIVILEGES ON \`test_lms\`.* TO 'test_lms'@'localhost'";
|
||||||
|
|
||||||
|
mariadb --host 127.0.0.1 --port 3306 -u root -p123 -e "FLUSH PRIVILEGES";
|
||||||
|
|
||||||
|
echo "Setting Up Procfile..."
|
||||||
|
|
||||||
|
sed -i 's/^watch:/# watch:/g' Procfile
|
||||||
|
sed -i 's/^schedule:/# schedule:/g' Procfile
|
||||||
|
|
||||||
|
echo "Starting Bench..."
|
||||||
|
|
||||||
|
bench start &> bench_start.log &
|
||||||
|
|
||||||
|
CI=Yes bench build &
|
||||||
|
build_pid=$!
|
||||||
|
|
||||||
|
bench --site lms.test reinstall --yes
|
||||||
|
bench --site lms.test install-app lms
|
||||||
|
|
||||||
|
wait $build_pid
|
||||||
14
.github/helper/install_dependencies.sh
vendored
Normal file
14
.github/helper/install_dependencies.sh
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Setting Up System Dependencies..."
|
||||||
|
|
||||||
|
sudo apt update
|
||||||
|
sudo apt remove mysql-server mysql-client
|
||||||
|
sudo apt install libcups2-dev redis-server mariadb-client-10.6
|
||||||
|
|
||||||
|
install_wkhtmltopdf() {
|
||||||
|
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||||
|
sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||||
|
}
|
||||||
|
install_wkhtmltopdf &
|
||||||
20
.github/helper/site_config.json
vendored
Normal file
20
.github/helper/site_config.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"db_host": "127.0.0.1",
|
||||||
|
"db_port": 3306,
|
||||||
|
"db_name": "test_lms",
|
||||||
|
"db_password": "test_lms",
|
||||||
|
"allow_tests": true,
|
||||||
|
"enable_ui_tests": true,
|
||||||
|
"db_type": "mariadb",
|
||||||
|
"auto_email_id": "test@example.com",
|
||||||
|
"mail_server": "smtp.example.com",
|
||||||
|
"mail_login": "test@example.com",
|
||||||
|
"mail_password": "test",
|
||||||
|
"admin_password": "admin",
|
||||||
|
"root_login": "root",
|
||||||
|
"root_password": "123",
|
||||||
|
"host_name": "http://lms.test:8000",
|
||||||
|
"monitor": 1,
|
||||||
|
"server_script_enabled": true,
|
||||||
|
"mute_emails": true
|
||||||
|
}
|
||||||
32
.github/try-on-f-cloud.svg
vendored
Normal file
32
.github/try-on-f-cloud.svg
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="4 2 193 52">
|
||||||
|
<g filter="url(#filter0_dd)">
|
||||||
|
<rect x="4" y="2" width="193" height="52" rx="6" fill="#2490EF"/>
|
||||||
|
<path d="M28 22.2891H32.8786V35.5H36.2088V22.2891H41.0874V19.5H28V22.2891Z" fill="white"/>
|
||||||
|
<path d="M41.6982 35.5H45.0129V28.7109C45.0129 27.2344 46.0866 26.2188 47.5494 26.2188C48.0085 26.2188 48.6388 26.2969 48.95 26.3984V23.4453C48.6543 23.375 48.2419 23.3281 47.9074 23.3281C46.5691 23.3281 45.472 24.1094 45.0362 25.5938H44.9117V23.5H41.6982V35.5Z" fill="white"/>
|
||||||
|
<path d="M52.8331 40C55.2996 40 56.6068 38.7344 57.2837 36.7969L61.9289 23.5156L58.4197 23.5L55.9221 32.3125H55.7976L53.3233 23.5H49.8374L54.1247 35.8437L53.9302 36.3516C53.4944 37.4766 52.6619 37.5312 51.4947 37.1719L50.7478 39.6562C51.2224 39.8594 51.9927 40 52.8331 40Z" fill="white"/>
|
||||||
|
<path d="M73.6142 35.7344C77.2401 35.7344 79.4966 33.2422 79.4966 29.5469C79.4966 25.8281 77.2401 23.3438 73.6142 23.3438C69.9883 23.3438 67.7319 25.8281 67.7319 29.5469C67.7319 33.2422 69.9883 35.7344 73.6142 35.7344ZM73.6298 33.1562C71.9569 33.1562 71.101 31.6171 71.101 29.5233C71.101 27.4296 71.9569 25.8827 73.6298 25.8827C75.2715 25.8827 76.1274 27.4296 76.1274 29.5233C76.1274 31.6171 75.2715 33.1562 73.6298 33.1562Z" fill="white"/>
|
||||||
|
<path d="M84.7253 28.5625C84.7331 27.0156 85.6512 26.1094 86.9895 26.1094C88.3201 26.1094 89.1215 26.9844 89.1137 28.4531V35.5H92.4284V27.8594C92.4284 25.0625 90.7945 23.3438 88.3046 23.3438C86.5306 23.3438 85.2466 24.2187 84.7097 25.6172H84.5697V23.5H81.4106V35.5H84.7253V28.5625Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M102.429 19.5H113.429V22.3141H102.429V19.5ZM102.429 35.5V26.6794H112.699V29.4982H105.94V35.5H102.429Z" fill="white"/>
|
||||||
|
<path d="M131.584 24.9625C131.09 21.5057 128.345 19.5 124.785 19.5C120.589 19.5 117.429 22.463 117.429 27.4924C117.429 32.5142 120.55 35.4848 124.785 35.4848C128.604 35.4848 131.137 33.0916 131.584 30.1211L128.651 30.1059C128.282 31.9293 126.745 32.9549 124.824 32.9549C122.22 32.9549 120.354 31.0632 120.354 27.4924C120.354 23.9824 122.204 22.0299 124.832 22.0299C126.784 22.0299 128.314 23.1011 128.651 24.9625H131.584Z" fill="white"/>
|
||||||
|
<path d="M136.409 19.7124H133.571V35.2718H136.409V19.7124Z" fill="white"/>
|
||||||
|
<path d="M144.031 35.5001C147.56 35.5001 149.803 33.0917 149.803 29.483C149.803 25.8667 147.56 23.4507 144.031 23.4507C140.502 23.4507 138.259 25.8667 138.259 29.483C138.259 33.0917 140.502 35.5001 144.031 35.5001ZM144.047 33.2969C142.094 33.2969 141.137 31.6103 141.137 29.4754C141.137 27.3406 142.094 25.6312 144.047 25.6312C145.968 25.6312 146.925 27.3406 146.925 29.4754C146.925 31.6103 145.968 33.2969 144.047 33.2969Z" fill="white"/>
|
||||||
|
<path d="M159.338 30.3641C159.338 32.1419 158.028 33.0232 156.773 33.0232C155.409 33.0232 154.499 32.0887 154.499 30.6072V23.6025H151.66V31.0327C151.66 33.8361 153.307 35.4239 155.675 35.4239C157.479 35.4239 158.749 34.5046 159.298 33.1979H159.424V35.272H162.176V23.6025H159.338V30.3641Z" fill="white"/>
|
||||||
|
<path d="M169.014 35.4769C171.084 35.4769 172.017 34.2841 172.464 33.4332H172.637V35.2718H175.429V19.7124H172.582V25.532H172.464C172.033 24.6887 171.147 23.4503 169.022 23.4503C166.238 23.4503 164.05 25.5624 164.05 29.4522C164.05 33.2965 166.175 35.4769 169.014 35.4769ZM169.806 33.2205C167.931 33.2205 166.943 31.6251 166.943 29.437C166.943 27.2642 167.916 25.7067 169.806 25.7067C171.633 25.7067 172.637 27.173 172.637 29.437C172.637 31.701 171.617 33.2205 169.806 33.2205Z" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_dd" x="0" y="0" width="201" height="60" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset/>
|
||||||
|
<feGaussianBlur stdDeviation="0.25"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="2"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.13 0"/>
|
||||||
|
<feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Run tests
|
name: Server Tests
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -32,11 +32,11 @@ jobs:
|
|||||||
- name: setup python
|
- name: setup python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.10'
|
||||||
- name: setup node
|
- name: setup node
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: '14'
|
node-version: '18'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: setup cache for bench
|
- name: setup cache for bench
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
run: bench new-site --mariadb-root-password root --admin-password admin frappe.local
|
run: bench new-site --mariadb-root-password root --admin-password admin frappe.local
|
||||||
- name: install lms app
|
- name: install lms app
|
||||||
working-directory: /home/runner/frappe-bench
|
working-directory: /home/runner/frappe-bench
|
||||||
run: bench --verbose --site frappe.local install-app lms
|
run: bench --site frappe.local install-app lms
|
||||||
- name: setup requirements
|
- name: setup requirements
|
||||||
working-directory: /home/runner/frappe-bench
|
working-directory: /home/runner/frappe-bench
|
||||||
run: bench setup requirements --dev
|
run: bench setup requirements --dev
|
||||||
@@ -78,4 +78,3 @@ jobs:
|
|||||||
- name: run tests
|
- name: run tests
|
||||||
working-directory: /home/runner/frappe-bench
|
working-directory: /home/runner/frappe-bench
|
||||||
run: bench --site frappe.local run-tests --app lms
|
run: bench --site frappe.local run-tests --app lms
|
||||||
|
|
||||||
|
|||||||
33
.github/workflows/linters.yml
vendored
Normal file
33
.github/workflows/linters.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
name: Linters
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
linters:
|
||||||
|
name: Semantic Commits
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.10'
|
||||||
|
|
||||||
|
- name: Install and Run Pre-commit
|
||||||
|
uses: pre-commit/action@v2.0.3
|
||||||
|
|
||||||
|
- name: Download Semgrep rules
|
||||||
|
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
||||||
|
|
||||||
|
- name: Download semgrep
|
||||||
|
run: pip install semgrep
|
||||||
|
|
||||||
|
- name: Run Semgrep rules
|
||||||
|
run: semgrep ci --config ./frappe-semgrep-rules/rules
|
||||||
19
.github/workflows/semantic.yml
vendored
Normal file
19
.github/workflows/semantic.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Semantic Pull Request
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# This workflow contains a single job called "build"
|
||||||
|
semantic:
|
||||||
|
name: Validate PR title
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
|
steps:
|
||||||
|
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- uses: zeke/semantic-pull-requests@main
|
||||||
121
.github/workflows/ui-tests.yml
vendored
Normal file
121
.github/workflows/ui-tests.yml
vendored
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
name: UI
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
# Do not change this as GITHUB_TOKEN is being used by roulette
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.repository_owner == 'frappe' }}
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
name: UI Tests (Cypress)
|
||||||
|
|
||||||
|
services:
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:10.6
|
||||||
|
env:
|
||||||
|
MARIADB_ROOT_PASSWORD: 123
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Check for valid Python & Merge Conflicts
|
||||||
|
run: |
|
||||||
|
python -m compileall -q -f "${GITHUB_WORKSPACE}"
|
||||||
|
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
|
||||||
|
then echo "Found merge conflicts"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
|
- name: Add to Hosts
|
||||||
|
run: |
|
||||||
|
echo "127.0.0.1 lms.test" | sudo tee -a /etc/hosts
|
||||||
|
|
||||||
|
- name: Cache pip
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-
|
||||||
|
${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/cache@v3
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-ui-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-ui-
|
||||||
|
|
||||||
|
- name: Cache cypress binary
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-cypress
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
bash ${GITHUB_WORKSPACE}/.github/helper/install_dependencies.sh
|
||||||
|
bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
|
env:
|
||||||
|
BEFORE: ${{ env.GITHUB_EVENT_PATH.before }}
|
||||||
|
AFTER: ${{ env.GITHUB_EVENT_PATH.after }}
|
||||||
|
TYPE: ui
|
||||||
|
DB: mariadb
|
||||||
|
|
||||||
|
- name: Site Setup
|
||||||
|
run: |
|
||||||
|
cd ~/frappe-bench/
|
||||||
|
bench --site lms.test execute frappe.utils.install.complete_setup_wizard
|
||||||
|
bench --site lms.test execute frappe.tests.ui_test_helpers.create_test_user
|
||||||
|
|
||||||
|
- name: cypress pre-requisites
|
||||||
|
run: |
|
||||||
|
cd ~/frappe-bench/apps/lms
|
||||||
|
yarn add cypress@^10 --no-lockfile
|
||||||
|
|
||||||
|
- name: UI Tests
|
||||||
|
run: cd ~/frappe-bench/ && bench --site lms.test run-ui-tests lms --headless
|
||||||
|
env:
|
||||||
|
CYPRESS_BASE_URL: http://lms.test:8000
|
||||||
|
CYPRESS_RECORD_KEY: 095366ec-7b9f-41bd-aeec-03bb76d627fe
|
||||||
|
|
||||||
|
- name: Stop server and wait for coverage file
|
||||||
|
run: |
|
||||||
|
ps -ef | grep "[f]rappe serve" | awk '{print $2}' | xargs kill -s SIGINT
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
- name: Show bench output
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: cat ~/frappe-bench/bench_start.log || true
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@ lms/public/dist
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
57
.pre-commit-config.yaml
Normal file
57
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
exclude: 'node_modules|.git'
|
||||||
|
default_stages: [commit]
|
||||||
|
fail_fast: false
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.3.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
files: "lms.*"
|
||||||
|
exclude: ".*json$|.*txt$|.*csv|.*md|.*svg"
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: check-ast
|
||||||
|
- id: check-json
|
||||||
|
- id: check-toml
|
||||||
|
- id: debug-statements
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.34.0
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: ['--py310-plus']
|
||||||
|
|
||||||
|
- repo: https://github.com/adityahase/black
|
||||||
|
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
additional_dependencies: ['click==8.0.4']
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
rev: v2.7.1
|
||||||
|
hooks:
|
||||||
|
- id: prettier
|
||||||
|
types_or: [javascript]
|
||||||
|
# Ignore any files that might contain jinja / bundles
|
||||||
|
exclude: |
|
||||||
|
(?x)^(
|
||||||
|
lms/public/dist/.*|
|
||||||
|
.*node_modules.*|
|
||||||
|
.*boilerplate.*|
|
||||||
|
lms/www/website_script.js|
|
||||||
|
lms/templates/includes/.*|
|
||||||
|
lms/public/js/lib/.*
|
||||||
|
)$
|
||||||
|
|
||||||
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: 5.0.4
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies: ['flake8-bugbear',]
|
||||||
|
args: ['--config', '.github/helper/flake8.conf']
|
||||||
|
|
||||||
|
ci:
|
||||||
|
autoupdate_schedule: weekly
|
||||||
|
skip: []
|
||||||
|
submodules: false
|
||||||
110
README.md
110
README.md
@@ -1,25 +1,109 @@
|
|||||||
## LMS
|
<p align="center">
|
||||||
|
<a href="https://www.frappelms.com/">
|
||||||
|
<img src="https://frappe.io/files/lms.png" alt="Frappe LMS" width="50px" height="50px">
|
||||||
|
</a>
|
||||||
|
<p align="center">Easy to use, open source, learning management system.</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
Create online courses without much hassle.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.producthunt.com/posts/frappe-lms?utm_source=badge-top-post-topic-badge&utm_medium=badge&utm_souce=badge-frappe-lms" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/top-post-topic-badge.svg?post_id=396079&theme=dark&period=weekly&topic_id=204" alt="Frappe LMS - Easy to use, 100% open source learning management system | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<div align="center" style="max-height: 40px;">
|
||||||
|
<a href="https://frappecloud.com/lms/signup">
|
||||||
|
<img src=".github/try-on-f-cloud.svg" height="40">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://dashboard.cypress.io/projects/vandxn/runs">
|
||||||
|
<img alt="cypress" src="https://img.shields.io/endpoint?url=https://dashboard.cypress.io/badge/simple/vandxn/main&style=flat&logo=cypress">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/frappe/lms/blob/main/LICENSE">
|
||||||
|
<img alt="license" src="https://img.shields.io/badge/license-AGPLv3-blue">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<img width="1402" alt="Lesson" src="https://frappelms.com/files/banner.png">
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Show more screenshots</summary>
|
||||||
|
<img width="1520" alt="ss1" src="https://user-images.githubusercontent.com/31363128/210056046-584bc8aa-d28c-4514-b031-73817012837d.png">
|
||||||
|
<img width="830" alt="ss2" src="https://user-images.githubusercontent.com/31363128/210056097-36849182-6db0-43a2-8c62-5333cd2aedf4.png">
|
||||||
|
<img width="941" alt="ss3" src="https://user-images.githubusercontent.com/31363128/210056134-01a7c429-1ef4-434e-9d43-128dda35d7e5.png">
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Frappe LMS is an easy-to-use, open-source learning management system. You can use it to create and share online courses. The app has a clear UI that helps students focus only on what's important and assists in distraction-free learning.
|
||||||
|
|
||||||
|
You can create courses and lessons through simple forms. Lessons can be in the form of text, videos, quizzes or a combination of all these. You can keep your students engaged with quizzes to help revise and test the concepts learned. Course Instructors and Students can reach out to each other through the discussions section available for each lesson and get queries resolved.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- Create online courses. 📚
|
||||||
|
- Add detailed descriptions and preview videos to the course. 🎬
|
||||||
|
- Add videos, quizzes, and assignments to your lessons and make them interesting and interactive 📝
|
||||||
|
- Discussions section below each lesson where instructors and students can interact with each other. 💬
|
||||||
|
- Create batches to group your students based on courses and track their progress 🏛
|
||||||
|
- Statistics dashboard that provides all important numbers at a glimpse. 📈
|
||||||
|
- Job Board where users can post and look for jobs. 💼
|
||||||
|
- People directory with each person's profile page 👨👩👧👦
|
||||||
|
- Set cover image, profile photo, short bio, and other professional information. 🦹🏼♀️
|
||||||
|
- Simple layout that optimizes readability 🤓
|
||||||
|
- Delightful user experience in overall usage ✨
|
||||||
|
|
||||||
1. Simple Backend Forms.
|
## Tech Stack
|
||||||
1. The UI is clean and minimal.
|
|
||||||
1. Lessons can be in the form of texts, videos, quizzes or a combination of all of these.
|
|
||||||
|
|
||||||
## Development Setup
|
Frappe LMS is built on [Frappe Framework](https://frappeframework.com) which is a batteries-included python web framework.
|
||||||
|
These are some of the tools it's built on:
|
||||||
|
- [Python](https://www.python.org)
|
||||||
|
- [Redis](https://redis.io/)
|
||||||
|
- [MariaDB](https://mariadb.org/)
|
||||||
|
- [Socket.io](https://socket.io/)
|
||||||
|
|
||||||
1. [Through Docker](docker-installation.md)
|
## Local Setup
|
||||||
1. [Direct install through bench](bench-installation.md)
|
|
||||||
|
### Docker
|
||||||
|
You need Docker, docker-compose, and git setup on your machine. Refer to [Docker documentation](https://docs.docker.com/). After that, run the following commands:
|
||||||
|
```
|
||||||
|
git clone https://github.com/frappe/lms
|
||||||
|
cd apps/lms/docker
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
Wait for some time until the setup script creates a site. After that, you can access `http://localhost:8000` in your browser and the app's login screen should show up.
|
||||||
|
|
||||||
|
### Frappe Bench
|
||||||
|
|
||||||
|
Currently, this app depends on the `develop` branch of [frappe](https://github.com/frappe/frappe).
|
||||||
|
|
||||||
|
1. Setup frappe-bench by following [this guide](https://frappeframework.com/docs/v14/user/en/installation)
|
||||||
|
1. In the frappe-bench directory, run `bench start` and keep it running. Open a new terminal session and cd into the `frappe-bench` directory.
|
||||||
|
1. Run the following commands:
|
||||||
|
```sh
|
||||||
|
bench new-site lms.test
|
||||||
|
bench get-app lms
|
||||||
|
bench --site lms.test install-app lms
|
||||||
|
bench --site lms.test add-to-hosts
|
||||||
|
|
||||||
|
1. Now, you can access the site at `http://lms.test:8000`
|
||||||
|
|
||||||
|
|
||||||
### Contributing
|
## Deployment
|
||||||
|
Frappe LMS is an app built on top of the Frappe Framework. So, you can follow any deployment guide for hosting a Frappe Framework-based site.
|
||||||
|
|
||||||
1. [Contribution Guidelines](Contribution.md)
|
### Managed Hosting
|
||||||
|
Frappe LMS can be deployed in a few clicks on [Frappe Cloud](https://frappecloud.com/marketplace/apps/lms).
|
||||||
|
|
||||||
|
### Self-hosting
|
||||||
|
If you want to self-host, you can follow official [Frappe Bench Installation](https://github.com/frappe/bench#installation) instructions.
|
||||||
|
|
||||||
|
## Bugs and Feature Requests
|
||||||
|
If you find any bugs or have a feature idea for the app, feel free to report them here on [GitHub Issues](https://github.com/frappe/lms/issues). Make sure you share enough information (app screenshots, browser console screenshots, stack traces, etc) for project maintainers.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
Distributed under [GNU AFFERO GENERAL PUBLIC LICENSE](license.txt)
|
||||||
[GNU AFFERO GENERAL PUBLIC LICENSE](license.txt)
|
|
||||||
|
|||||||
5
SECURITY.md
Normal file
5
SECURITY.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
The Frappe team and community take security issues seriously. To report a security issue, please go through the information mentioned [here](https://frappe.io/security).
|
||||||
|
|
||||||
|
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly and will keep you updated throughout the process.
|
||||||
@@ -3,7 +3,7 @@ To setup the repository locally follow the steps mentioned below:
|
|||||||
1. Install bench and setup a frappe-bench directory by following the [Installation Steps](https://frappeframework.com/docs/user/en/installation).
|
1. Install bench and setup a frappe-bench directory by following the [Installation Steps](https://frappeframework.com/docs/user/en/installation).
|
||||||
1. Start the server by running bench start.
|
1. Start the server by running bench start.
|
||||||
1. In a separate terminal window, create a new site by running bench new-site lms.test.
|
1. In a separate terminal window, create a new site by running bench new-site lms.test.
|
||||||
1. Fork the lms app
|
1. Fork the LMS app
|
||||||
1. Run bench get-app <url-of-your-form>.
|
1. Run bench get-app <url-of-your-form>.
|
||||||
1. Run bench --site lms.test install-app lms.
|
1. Run bench --site lms.test install-app lms.
|
||||||
1. Map your site to localhost with the command ```bench --site lms.test add-to-hosts```
|
1. Map your site to localhost with the command ```bench --site lms.test add-to-hosts```
|
||||||
|
|||||||
18
cypress.config.js
Normal file
18
cypress.config.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const { defineConfig } = require("cypress");
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
projectId: "vandxn",
|
||||||
|
adminPassword: "admin",
|
||||||
|
testUser: "frappe@example.com",
|
||||||
|
defaultCommandTimeout: 20000,
|
||||||
|
pageLoadTimeout: 15000,
|
||||||
|
video: true,
|
||||||
|
videoUploadOnPasses: false,
|
||||||
|
retries: {
|
||||||
|
runMode: 2,
|
||||||
|
openMode: 0,
|
||||||
|
},
|
||||||
|
e2e: {
|
||||||
|
baseUrl: "http://pyp:8000",
|
||||||
|
},
|
||||||
|
});
|
||||||
133
cypress/e2e/course_creation.cy.js
Normal file
133
cypress/e2e/course_creation.cy.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
describe("Course Creation", () => {
|
||||||
|
it("creates a new course", () => {
|
||||||
|
cy.login();
|
||||||
|
cy.visit("/courses");
|
||||||
|
// Create a course
|
||||||
|
cy.get("a.btn").contains("Create a Course").click();
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.url().should("include", "/courses/new-course/edit");
|
||||||
|
cy.get("#title").type("Test Course");
|
||||||
|
cy.get("#intro").type("Test Course Short Introduction");
|
||||||
|
cy.get("#description").type("Test Course Description");
|
||||||
|
cy.get("#video-link").type("-LPmw2Znl2c");
|
||||||
|
cy.get("#tags-input").type("Test");
|
||||||
|
cy.get("#published").check();
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.button("Save").click();
|
||||||
|
|
||||||
|
// Add Chapter
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.link("Course Outline").click();
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get(".edit-header .btn-add-chapter").click();
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get("#chapter-title").type("Test Chapter");
|
||||||
|
cy.get("#chapter-description").type("Test Chapter Description");
|
||||||
|
cy.button("Save").click();
|
||||||
|
|
||||||
|
// Add Lesson
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.link("Add Lesson").click();
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get("#lesson-title").type("Test Lesson");
|
||||||
|
|
||||||
|
// Content
|
||||||
|
cy.get(".collapse-section.collapsed:first").click();
|
||||||
|
cy.get("#lesson-content .ce-block")
|
||||||
|
.click()
|
||||||
|
.type(
|
||||||
|
"This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now. {enter}"
|
||||||
|
);
|
||||||
|
cy.get("#lesson-content .ce-toolbar__plus").click();
|
||||||
|
cy.get('#lesson-content [data-item-name="youtube"]').click();
|
||||||
|
cy.get('input[data-fieldname="youtube"]').type("GoDtyItReto");
|
||||||
|
cy.button("Insert").click();
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.button("Save").click();
|
||||||
|
|
||||||
|
// View Course
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.visit("/courses");
|
||||||
|
cy.get(".course-card-title:first").contains("Test Course");
|
||||||
|
cy.get(".course-card:first").click();
|
||||||
|
cy.url().should("include", "/courses/test-course");
|
||||||
|
cy.get("#title").contains("Test Course");
|
||||||
|
cy.get(".preview-video").should(
|
||||||
|
"have.attr",
|
||||||
|
"src",
|
||||||
|
"https://www.youtube.com/embed/-LPmw2Znl2c"
|
||||||
|
);
|
||||||
|
cy.get("#intro").contains("Test Course Short Introduction");
|
||||||
|
|
||||||
|
// View Chapter
|
||||||
|
cy.get(".chapter-title-main:first").contains("Test Chapter");
|
||||||
|
cy.get(".chapter-description:first").contains(
|
||||||
|
"Test Chapter Description"
|
||||||
|
);
|
||||||
|
cy.get(".lesson-info:first").contains("Test Lesson");
|
||||||
|
cy.get(".lesson-info:first").click();
|
||||||
|
|
||||||
|
// View Lesson
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.url().should("include", "learn/1.1");
|
||||||
|
cy.get("#title").contains("Test Lesson");
|
||||||
|
cy.get(".lesson-video iframe").should(
|
||||||
|
"have.attr",
|
||||||
|
"src",
|
||||||
|
"https://www.youtube.com/embed/GoDtyItReto"
|
||||||
|
);
|
||||||
|
cy.get(".lesson-content-card").contains(
|
||||||
|
"This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add Discussion
|
||||||
|
cy.get(".reply").click();
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(".discussion-modal").should("be.visible");
|
||||||
|
|
||||||
|
// Enter title
|
||||||
|
cy.get(".modal .topic-title")
|
||||||
|
.type("Discussion from tests")
|
||||||
|
.should("have.value", "Discussion from tests");
|
||||||
|
|
||||||
|
// Enter comment
|
||||||
|
cy.get(".modal .discussions-comment").type(
|
||||||
|
"This is a discussion from the cypress ui tests."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
cy.get(".modal .submit-discussion").click();
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
// Check if discussion is added to page and content is visible
|
||||||
|
cy.get(".sidebar-parent:first .discussion-topic-title").should(
|
||||||
|
"have.text",
|
||||||
|
"Discussion from tests"
|
||||||
|
);
|
||||||
|
cy.get(".sidebar-parent:first .discussion-topic-title").click();
|
||||||
|
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||||
|
cy.get(
|
||||||
|
".discussion-on-page:visible .reply-card .reply-text .ql-editor p"
|
||||||
|
).should(
|
||||||
|
"have.text",
|
||||||
|
"This is a discussion from the cypress ui tests."
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(".discussion-form:visible .discussions-comment").type(
|
||||||
|
"This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page."
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(".discussion-form:visible .submit-discussion").click();
|
||||||
|
cy.wait(3000);
|
||||||
|
cy.get(".discussion-on-page:visible").should("have.class", "show");
|
||||||
|
cy.get(".discussion-on-page:visible")
|
||||||
|
.children(".reply-card")
|
||||||
|
.eq(1)
|
||||||
|
.find(".reply-text")
|
||||||
|
.should(
|
||||||
|
"have.text",
|
||||||
|
"This is a discussion from the cypress ui tests. This comment was entered through the commentbox on the page.\n"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Using fixtures to represent data",
|
||||||
|
"email": "hello@cypress.io",
|
||||||
|
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||||
|
}
|
||||||
55
cypress/support/commands.js
Normal file
55
cypress/support/commands.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
// ***********************************************
|
||||||
|
// This example commands.js shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
|
Cypress.Commands.add("login", (email, password) => {
|
||||||
|
if (!email) {
|
||||||
|
email = Cypress.config("testUser") || "Administrator";
|
||||||
|
}
|
||||||
|
if (!password) {
|
||||||
|
password = Cypress.config("adminPassword");
|
||||||
|
}
|
||||||
|
cy.request({
|
||||||
|
url: "/api/method/login",
|
||||||
|
method: "POST",
|
||||||
|
body: { usr: email, pwd: password },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("button", (text) => {
|
||||||
|
return cy.get(`button:contains("${text}")`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("link", (text) => {
|
||||||
|
return cy.get(`a:contains("${text}")`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("iconButton", (text) => {
|
||||||
|
return cy.get(`button[aria-label="${text}"]`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("dialog", (selector) => {
|
||||||
|
return cy.get(`[role=dialog] ${selector}`);
|
||||||
|
});
|
||||||
20
cypress/support/e2e.js
Normal file
20
cypress/support/e2e.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// This example support/e2e.js is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.js using ES2015 syntax:
|
||||||
|
import "./commands";
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
mariadb:
|
|
||||||
image: mariadb
|
|
||||||
volumes:
|
|
||||||
- mariadb-storage:/var/lib/mysql
|
|
||||||
environment:
|
|
||||||
- MYSQL_ROOT_PASSWORD=root
|
|
||||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
|
||||||
bench:
|
|
||||||
image: anandology/frappe-bench:2021.10
|
|
||||||
volumes:
|
|
||||||
- .:/opt/frappe-bench/apps/lms
|
|
||||||
environment:
|
|
||||||
- FRAPPE_APPS=lms
|
|
||||||
- FRAPPE_ALLOW_TESTS=true
|
|
||||||
- FRAPPE_SITE_NAME=frappe.localhost
|
|
||||||
depends_on:
|
|
||||||
- mariadb
|
|
||||||
ports:
|
|
||||||
- 8000:8000
|
|
||||||
- 9000:9000
|
|
||||||
volumes:
|
|
||||||
mariadb-storage: {}
|
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
$ git clone https://github.com/frappe/lms.git
|
$ git clone https://github.com/frappe/lms.git
|
||||||
|
|
||||||
$ cd lms
|
$ cd lms
|
||||||
|
|
||||||
|
$ cd docker
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 2:** Run docker-compose
|
**Step 2:** Run docker-compose
|
||||||
|
|||||||
32
docker/docker-compose.yml
Normal file
32
docker/docker-compose.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
version: "3.7"
|
||||||
|
name: lms
|
||||||
|
services:
|
||||||
|
mariadb:
|
||||||
|
image: mariadb:10.6
|
||||||
|
command:
|
||||||
|
- --character-set-server=utf8mb4
|
||||||
|
- --collation-server=utf8mb4_unicode_ci
|
||||||
|
- --skip-character-set-client-handshake
|
||||||
|
- --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 123
|
||||||
|
volumes:
|
||||||
|
- mariadb-data:/var/lib/mysql
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
|
||||||
|
frappe:
|
||||||
|
image: frappe/bench:latest
|
||||||
|
command: bash /workspace/init.sh
|
||||||
|
environment:
|
||||||
|
- SHELL=/bin/bash
|
||||||
|
working_dir: /home/frappe
|
||||||
|
volumes:
|
||||||
|
- .:/workspace
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
- 9000:9000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mariadb-data:
|
||||||
41
docker/init.sh
Normal file
41
docker/init.sh
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#!bin/bash
|
||||||
|
|
||||||
|
if [ -d "/home/frappe/frappe-bench/apps/frappe" ]; then
|
||||||
|
echo "Bench already exists, skipping init"
|
||||||
|
cd frappe-bench
|
||||||
|
bench start
|
||||||
|
else
|
||||||
|
echo "Creating new bench..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PATH="${NVM_DIR}/versions/node/v${NODE_VERSION_DEVELOP}/bin/:${PATH}"
|
||||||
|
|
||||||
|
bench init --skip-redis-config-generation frappe-bench
|
||||||
|
|
||||||
|
cd frappe-bench
|
||||||
|
|
||||||
|
# Use containers instead of localhost
|
||||||
|
bench set-mariadb-host mariadb
|
||||||
|
bench set-redis-cache-host redis:6379
|
||||||
|
bench set-redis-queue-host redis:6379
|
||||||
|
bench set-redis-socketio-host redis:6379
|
||||||
|
|
||||||
|
# Remove redis, watch from Procfile
|
||||||
|
sed -i '/redis/d' ./Procfile
|
||||||
|
sed -i '/watch/d' ./Procfile
|
||||||
|
|
||||||
|
bench get-app lms
|
||||||
|
|
||||||
|
bench new-site lms.localhost \
|
||||||
|
--force \
|
||||||
|
--mariadb-root-password 123 \
|
||||||
|
--admin-password admin \
|
||||||
|
--no-mariadb-socket
|
||||||
|
|
||||||
|
bench --site lms.localhost install-app lms
|
||||||
|
bench --site lms.localhost set-config developer_mode 1
|
||||||
|
bench --site lms.localhost clear-cache
|
||||||
|
bench --site lms.localhost set-config mute_emails 1
|
||||||
|
bench use lms.localhost
|
||||||
|
|
||||||
|
bench start
|
||||||
1
frappe-ui
Submodule
1
frappe-ui
Submodule
Submodule frappe-ui added at 2898a0bdd1
@@ -1,7 +1 @@
|
|||||||
# -*- coding: utf-8 -*-
|
__version__ = "1.0.0"
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
__version__ = '0.0.1'
|
|
||||||
|
|
||||||
# load the methods from the lms api
|
|
||||||
from .lms import api # noqa
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -9,6 +8,6 @@ def get_data():
|
|||||||
"color": "grey",
|
"color": "grey",
|
||||||
"icon": "octicon octicon-file-directory",
|
"icon": "octicon octicon-file-directory",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"label": _("Community")
|
"label": _("Community"),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ Configuration for docs
|
|||||||
# headline = "App that does everything"
|
# headline = "App that does everything"
|
||||||
# sub_heading = "Yes, you got that right the first time, everything"
|
# sub_heading = "Yes, you got that right the first time, everything"
|
||||||
|
|
||||||
|
|
||||||
def get_context(context):
|
def get_context(context):
|
||||||
context.brand_html = "Community"
|
context.brand_html = "Community"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
114
lms/fixtures/function.json
Normal file
114
lms/fixtures/function.json
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Consulting",
|
||||||
|
"modified": "2021-12-14 15:16:26.189604",
|
||||||
|
"name": "Consulting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Data & Analytics",
|
||||||
|
"modified": "2021-12-14 15:16:26.341051",
|
||||||
|
"name": "Data & Analytics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Design & Creative",
|
||||||
|
"modified": "2021-12-14 15:16:26.343820",
|
||||||
|
"name": "Design & Creative"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Engineering (Non Software)",
|
||||||
|
"modified": "2021-12-14 15:16:26.346329",
|
||||||
|
"name": "Engineering (Non Software)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Finance, Investment & Accounting",
|
||||||
|
"modified": "2021-12-14 15:16:26.351518",
|
||||||
|
"name": "Finance, Investment & Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Human Resource & Recruiting",
|
||||||
|
"modified": "2021-12-14 15:16:26.354310",
|
||||||
|
"name": "Human Resource & Recruiting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Legal",
|
||||||
|
"modified": "2021-12-14 15:16:26.356844",
|
||||||
|
"name": "Legal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Marketing, Advertising & PR",
|
||||||
|
"modified": "2021-12-14 15:16:26.359511",
|
||||||
|
"name": "Marketing, Advertising & PR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Operations & Admin",
|
||||||
|
"modified": "2021-12-14 15:16:26.362571",
|
||||||
|
"name": "Operations & Admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Project Management",
|
||||||
|
"modified": "2021-12-14 15:16:26.365033",
|
||||||
|
"name": "Project Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Product",
|
||||||
|
"modified": "2021-12-14 15:16:26.367804",
|
||||||
|
"name": "Product"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Research, Training & Education",
|
||||||
|
"modified": "2021-12-14 15:16:26.370085",
|
||||||
|
"name": "Research, Training & Education"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Sales & Customer Service",
|
||||||
|
"modified": "2021-12-14 15:16:26.372635",
|
||||||
|
"name": "Sales & Customer Service"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Supply Chain, Logistics Strategy & Management",
|
||||||
|
"modified": "2021-12-14 15:16:26.375091",
|
||||||
|
"name": "Supply Chain, Logistics Strategy & Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Other",
|
||||||
|
"modified": "2021-12-14 15:16:26.377761",
|
||||||
|
"name": "Other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Function",
|
||||||
|
"function": "Engineering (Software & IT)",
|
||||||
|
"modified": "2021-12-14 15:16:26.348864",
|
||||||
|
"name": "Engineering (Software & IT)"
|
||||||
|
}
|
||||||
|
]
|
||||||
170
lms/fixtures/industry.json
Normal file
170
lms/fixtures/industry.json
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Accounting",
|
||||||
|
"modified": "2021-12-14 15:21:18.044741",
|
||||||
|
"name": "Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Ads, Marketing, PR & Events",
|
||||||
|
"modified": "2021-12-14 15:21:18.069794",
|
||||||
|
"name": "Ads, Marketing, PR & Events"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Agriculture, Fishing & Forestry",
|
||||||
|
"modified": "2021-12-14 15:21:18.074148",
|
||||||
|
"name": "Agriculture, Fishing & Forestry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Architecture",
|
||||||
|
"modified": "2021-12-14 15:21:18.078250",
|
||||||
|
"name": "Architecture"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Aviation & Aerospace",
|
||||||
|
"modified": "2021-12-14 15:21:18.082127",
|
||||||
|
"name": "Aviation & Aerospace"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Banking, Financial Services & Insurance",
|
||||||
|
"modified": "2021-12-14 15:21:18.087499",
|
||||||
|
"name": "Banking, Financial Services & Insurance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Biotech & Pharmaceuticals",
|
||||||
|
"modified": "2021-12-14 15:21:18.092094",
|
||||||
|
"name": "Biotech & Pharmaceuticals"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Construction & Real Estate",
|
||||||
|
"modified": "2021-12-14 15:21:18.095728",
|
||||||
|
"name": "Construction & Real Estate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Consulting & Professional Services",
|
||||||
|
"modified": "2021-12-14 15:21:18.099171",
|
||||||
|
"name": "Consulting & Professional Services"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Energy & Utilities",
|
||||||
|
"modified": "2021-12-14 15:21:18.114022",
|
||||||
|
"name": "Energy & Utilities"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Food & Beverages",
|
||||||
|
"modified": "2021-12-14 15:21:18.124655",
|
||||||
|
"name": "Food & Beverages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Health & Medical",
|
||||||
|
"modified": "2021-12-14 15:21:18.127914",
|
||||||
|
"name": "Health & Medical"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Hospitality & Tourism",
|
||||||
|
"modified": "2021-12-14 15:21:18.130783",
|
||||||
|
"name": "Hospitality & Tourism"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "IT / Ecommerce / Internet",
|
||||||
|
"modified": "2021-12-14 15:21:18.134069",
|
||||||
|
"name": "IT / Ecommerce / Internet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Manufacturing & Production",
|
||||||
|
"modified": "2021-12-14 15:21:18.137362",
|
||||||
|
"name": "Manufacturing & Production"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Media & Entertainment",
|
||||||
|
"modified": "2021-12-14 15:21:18.140180",
|
||||||
|
"name": "Media & Entertainment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Public Service & NGOs",
|
||||||
|
"modified": "2021-12-14 15:21:18.143125",
|
||||||
|
"name": "Public Service & NGOs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Retail, Fashion & FMCG",
|
||||||
|
"modified": "2021-12-14 15:21:18.146020",
|
||||||
|
"name": "Retail, Fashion & FMCG"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Staffing & Recruiting",
|
||||||
|
"modified": "2021-12-14 15:21:18.149084",
|
||||||
|
"name": "Staffing & Recruiting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Transportation & Logistics",
|
||||||
|
"modified": "2021-12-14 15:21:18.151952",
|
||||||
|
"name": "Transportation & Logistics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Security & Law Enforcement",
|
||||||
|
"modified": "2021-12-14 15:21:18.154929",
|
||||||
|
"name": "Security & Law Enforcement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Other",
|
||||||
|
"modified": "2021-12-14 15:21:18.157766",
|
||||||
|
"name": "Other"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Education & Training",
|
||||||
|
"modified": "2021-12-14 15:21:18.105864",
|
||||||
|
"name": "Education & Training"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Industry",
|
||||||
|
"industry": "Engineering",
|
||||||
|
"modified": "2021-12-14 15:21:18.119149",
|
||||||
|
"name": "Engineering"
|
||||||
|
}
|
||||||
|
]
|
||||||
202
lms/hooks.py
202
lms/hooks.py
@@ -1,11 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from . import __version__ as app_version
|
from . import __version__ as app_version
|
||||||
|
|
||||||
app_name = "lms"
|
app_name = "frappe_lms"
|
||||||
app_title = "LMS"
|
app_title = "Frappe LMS"
|
||||||
app_publisher = "Frappe"
|
app_publisher = "Frappe"
|
||||||
app_description = "LMS"
|
app_description = "Frappe LMS App"
|
||||||
app_icon = "octicon octicon-file-directory"
|
app_icon = "octicon octicon-file-directory"
|
||||||
app_color = "grey"
|
app_color = "grey"
|
||||||
app_email = "school@frappe.io"
|
app_email = "school@frappe.io"
|
||||||
@@ -19,9 +17,9 @@ app_license = "AGPL"
|
|||||||
# app_include_js = "/assets/lms/js/lms.js"
|
# app_include_js = "/assets/lms/js/lms.js"
|
||||||
|
|
||||||
# include js, css files in header of web template
|
# include js, css files in header of web template
|
||||||
web_include_css = "/assets/css/lms.css"
|
web_include_css = "lms.bundle.css"
|
||||||
# web_include_css = "/assets/lms/css/lms.css"
|
# web_include_css = "/assets/lms/css/lms.css"
|
||||||
#web_include_js = "website.bundle.js"
|
web_include_js = ["website.bundle.js"]
|
||||||
|
|
||||||
# include custom scss in every website theme (without file extension ".scss")
|
# include custom scss in every website theme (without file extension ".scss")
|
||||||
# website_theme_scss = "lms/public/scss/website"
|
# website_theme_scss = "lms/public/scss/website"
|
||||||
@@ -60,7 +58,12 @@ web_include_css = "/assets/css/lms.css"
|
|||||||
# ------------
|
# ------------
|
||||||
|
|
||||||
# before_install = "lms.install.before_install"
|
# before_install = "lms.install.before_install"
|
||||||
# after_install = "lms.install.after_install"
|
after_install = "lms.install.after_install"
|
||||||
|
after_sync = "lms.install.after_sync"
|
||||||
|
before_uninstall = "lms.install.before_uninstall"
|
||||||
|
|
||||||
|
|
||||||
|
setup_wizard_requires = "assets/lms/js/setup_wizard.js"
|
||||||
|
|
||||||
# Desk Notifications
|
# Desk Notifications
|
||||||
# ------------------
|
# ------------------
|
||||||
@@ -86,7 +89,7 @@ web_include_css = "/assets/css/lms.css"
|
|||||||
|
|
||||||
override_doctype_class = {
|
override_doctype_class = {
|
||||||
"User": "lms.overrides.user.CustomUser",
|
"User": "lms.overrides.user.CustomUser",
|
||||||
"Web Template": "lms.overrides.web_template.CustomWebTemplate"
|
"Web Template": "lms.overrides.web_template.CustomWebTemplate",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Document Events
|
# Document Events
|
||||||
@@ -94,18 +97,18 @@ override_doctype_class = {
|
|||||||
# Hook on document methods and events
|
# Hook on document methods and events
|
||||||
|
|
||||||
doc_events = {
|
doc_events = {
|
||||||
|
"Discussion Reply": {"after_insert": "lms.lms.utils.handle_notifications"},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Scheduled Tasks
|
# Scheduled Tasks
|
||||||
# ---------------
|
# ---------------
|
||||||
#scheduler_events = {
|
scheduler_events = {
|
||||||
# "daily": [
|
"hourly": [
|
||||||
# "erpnext.stock.reorder_item.reorder_item"
|
"lms.lms.doctype.lms_certificate_request.lms_certificate_request.schedule_evals"
|
||||||
# ]
|
]
|
||||||
#}
|
}
|
||||||
|
|
||||||
fixtures = ["Custom Field"]
|
fixtures = ["Custom Field", "Function", "Industry"]
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
# -------
|
# -------
|
||||||
@@ -115,9 +118,9 @@ fixtures = ["Custom Field"]
|
|||||||
# Overriding Methods
|
# Overriding Methods
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
#
|
#
|
||||||
# override_whitelisted_methods = {
|
override_whitelisted_methods = {
|
||||||
# "frappe.desk.doctype.event.event.get_events": "lms.event.get_events"
|
# "frappe.desk.search.get_names_for_mentions": "lms.lms.utils.get_names_for_mentions",
|
||||||
# }
|
}
|
||||||
#
|
#
|
||||||
# each overriding function accepts a `data` argument;
|
# each overriding function accepts a `data` argument;
|
||||||
# generated from the base implementation of the doctype dashboard,
|
# generated from the base implementation of the doctype dashboard,
|
||||||
@@ -134,24 +137,130 @@ fixtures = ["Custom Field"]
|
|||||||
website_route_rules = [
|
website_route_rules = [
|
||||||
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
|
{"from_route": "/sketches/<sketch>", "to_route": "sketches/sketch"},
|
||||||
{"from_route": "/courses/<course>", "to_route": "courses/course"},
|
{"from_route": "/courses/<course>", "to_route": "courses/course"},
|
||||||
|
{"from_route": "/courses/<course>/edit", "to_route": "courses/create"},
|
||||||
|
{"from_route": "/courses/<course>/outline", "to_route": "courses/outline"},
|
||||||
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
|
{"from_route": "/courses/<course>/<certificate>", "to_route": "courses/certificate"},
|
||||||
{"from_route": "/hackathons/<hackathon>", "to_route": "hackathons/hackathon"},
|
|
||||||
{"from_route": "/hackathons/<hackathon>/<project>", "to_route": "hackathons/project"},
|
|
||||||
{"from_route": "/courses/<course>/learn", "to_route": "batch/learn"},
|
{"from_route": "/courses/<course>/learn", "to_route": "batch/learn"},
|
||||||
{"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>", "to_route": "batch/learn"},
|
{
|
||||||
|
"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>",
|
||||||
|
"to_route": "batch/learn",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/courses/<course>/learn/<int:chapter>.<int:lesson>/edit",
|
||||||
|
"to_route": "batch/edit",
|
||||||
|
},
|
||||||
|
{"from_route": "/quizzes", "to_route": "batch/quiz_list"},
|
||||||
|
{"from_route": "/quizzes/<quizname>", "to_route": "batch/quiz"},
|
||||||
|
{"from_route": "/batches/<batchname>", "to_route": "batches/batch"},
|
||||||
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
{"from_route": "/courses/<course>/progress", "to_route": "batch/progress"},
|
||||||
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
{"from_route": "/courses/<course>/join", "to_route": "batch/join"},
|
||||||
{"from_route": "/users/<string(minlength=4):username>", "to_route": "profiles/profile"}
|
{"from_route": "/courses/<course>/manage", "to_route": "cohorts"},
|
||||||
|
{"from_route": "/courses/<course>/cohorts/<cohort>", "to_route": "cohorts/cohort"},
|
||||||
|
{
|
||||||
|
"from_route": "/courses/<course>/cohorts/<cohort>/<page>",
|
||||||
|
"to_route": "cohorts/cohort",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>",
|
||||||
|
"to_route": "cohorts/subgroup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/courses/<course>/subgroups/<cohort>/<subgroup>/<page>",
|
||||||
|
"to_route": "cohorts/subgroup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/courses/<course>/join/<cohort>/<subgroup>/<invite_code>",
|
||||||
|
"to_route": "cohorts/join",
|
||||||
|
},
|
||||||
|
{"from_route": "/users", "to_route": "profiles/profile"},
|
||||||
|
{"from_route": "/job-openings", "to_route": "jobs_openings/index"},
|
||||||
|
{"from_route": "/job-openings/<job>", "to_route": "jobs_openings/job"},
|
||||||
|
{
|
||||||
|
"from_route": "/batches/<batchname>/students/<username>",
|
||||||
|
"to_route": "/batches/progress",
|
||||||
|
},
|
||||||
|
{"from_route": "/assignments/<assignment>", "to_route": "assignments/assignment"},
|
||||||
|
{
|
||||||
|
"from_route": "/assignment-submission/<assignment>/<submission>",
|
||||||
|
"to_route": "assignment_submission/assignment_submission",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/quiz-submission/<quiz>/<submission>",
|
||||||
|
"to_route": "quiz_submission/quiz_submission",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/billing/<module>/<modulename>",
|
||||||
|
"to_route": "billing/billing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/batches/details/<batchname>",
|
||||||
|
"to_route": "batches/batch_details",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from_route": "/certified-participants",
|
||||||
|
"to_route": "certified_participants/certified_participants",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
website_redirects = [
|
website_redirects = [
|
||||||
{"source": "/update-profile", "target": "/edit-profile"},
|
{"source": "/update-profile", "target": "/edit-profile"},
|
||||||
|
{"source": "/dashboard", "target": "/courses"},
|
||||||
|
{"source": "/community", "target": "/people"},
|
||||||
]
|
]
|
||||||
|
|
||||||
update_website_context = [
|
update_website_context = [
|
||||||
'lms.widgets.update_website_context',
|
"lms.widgets.update_website_context",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
jinja = {
|
||||||
|
"methods": [
|
||||||
|
"lms.page_renderers.get_profile_url",
|
||||||
|
"lms.overrides.user.get_enrolled_courses",
|
||||||
|
"lms.overrides.user.get_course_membership",
|
||||||
|
"lms.overrides.user.get_authored_courses",
|
||||||
|
"lms.overrides.user.get_palette",
|
||||||
|
"lms.lms.utils.get_membership",
|
||||||
|
"lms.lms.utils.get_lessons",
|
||||||
|
"lms.lms.utils.get_tags",
|
||||||
|
"lms.lms.utils.get_instructors",
|
||||||
|
"lms.lms.utils.get_students",
|
||||||
|
"lms.lms.utils.get_average_rating",
|
||||||
|
"lms.lms.utils.is_certified",
|
||||||
|
"lms.lms.utils.get_lesson_index",
|
||||||
|
"lms.lms.utils.get_lesson_url",
|
||||||
|
"lms.lms.utils.get_chapters",
|
||||||
|
"lms.lms.utils.get_slugified_chapter_title",
|
||||||
|
"lms.lms.utils.get_progress",
|
||||||
|
"lms.lms.utils.render_html",
|
||||||
|
"lms.lms.utils.is_mentor",
|
||||||
|
"lms.lms.utils.is_cohort_staff",
|
||||||
|
"lms.lms.utils.get_mentors",
|
||||||
|
"lms.lms.utils.get_reviews",
|
||||||
|
"lms.lms.utils.is_eligible_to_review",
|
||||||
|
"lms.lms.utils.get_initial_members",
|
||||||
|
"lms.lms.utils.get_sorted_reviews",
|
||||||
|
"lms.lms.utils.is_instructor",
|
||||||
|
"lms.lms.utils.convert_number_to_character",
|
||||||
|
"lms.lms.utils.get_signup_optin_checks",
|
||||||
|
"lms.lms.utils.get_popular_courses",
|
||||||
|
"lms.lms.utils.format_amount",
|
||||||
|
"lms.lms.utils.first_lesson_exists",
|
||||||
|
"lms.lms.utils.get_courses_under_review",
|
||||||
|
"lms.lms.utils.has_course_instructor_role",
|
||||||
|
"lms.lms.utils.has_course_moderator_role",
|
||||||
|
"lms.lms.utils.get_certificates",
|
||||||
|
"lms.lms.utils.format_number",
|
||||||
|
"lms.lms.utils.get_lesson_count",
|
||||||
|
"lms.lms.utils.get_all_memberships",
|
||||||
|
"lms.lms.utils.get_filtered_membership",
|
||||||
|
"lms.lms.utils.show_start_learing_cta",
|
||||||
|
"lms.lms.utils.can_create_courses",
|
||||||
|
"lms.lms.utils.get_telemetry_boot_info",
|
||||||
|
"lms.lms.utils.is_onboarding_complete",
|
||||||
|
"lms.www.utils.is_student",
|
||||||
|
],
|
||||||
|
"filters": [],
|
||||||
|
}
|
||||||
## Specify the additional tabs to be included in the user profile page.
|
## Specify the additional tabs to be included in the user profile page.
|
||||||
## Each entry must be a subclass of lms.lms.plugins.ProfileTab
|
## Each entry must be a subclass of lms.lms.plugins.ProfileTab
|
||||||
# profile_tabs = []
|
# profile_tabs = []
|
||||||
@@ -159,19 +268,56 @@ update_website_context = [
|
|||||||
## Specify the extension to be used to control what scripts and stylesheets
|
## Specify the extension to be used to control what scripts and stylesheets
|
||||||
## to be included in lesson pages. The specified value must be be a
|
## to be included in lesson pages. The specified value must be be a
|
||||||
## subclass of lms.plugins.PageExtension
|
## subclass of lms.plugins.PageExtension
|
||||||
# lms = None
|
# lms_lesson_page_extension = None
|
||||||
|
|
||||||
#lms_lesson_page_extensions = [
|
# lms_lesson_page_extensions = [
|
||||||
# "lms.plugins.LiveCodeExtension"
|
# "lms.plugins.LiveCodeExtension"
|
||||||
#]
|
# ]
|
||||||
|
|
||||||
|
has_website_permission = {
|
||||||
|
"LMS Certificate Evaluation": "lms.lms.doctype.lms_certificate_evaluation.lms_certificate_evaluation.has_website_permission"
|
||||||
|
}
|
||||||
|
|
||||||
|
profile_mandatory_fields = [
|
||||||
|
"first_name",
|
||||||
|
"last_name",
|
||||||
|
"user_image",
|
||||||
|
"bio",
|
||||||
|
"linkedin",
|
||||||
|
"education",
|
||||||
|
"skill",
|
||||||
|
"preferred_functions",
|
||||||
|
"preferred_industries",
|
||||||
|
"dream_companies",
|
||||||
|
"attire",
|
||||||
|
"collaboration",
|
||||||
|
"role",
|
||||||
|
"location_preference",
|
||||||
|
"time",
|
||||||
|
"company_type",
|
||||||
|
]
|
||||||
|
|
||||||
## Markdown Macros for Lessons
|
## Markdown Macros for Lessons
|
||||||
lms_markdown_macro_renderers = {
|
lms_markdown_macro_renderers = {
|
||||||
"Exercise": "lms.plugins.exercise_renderer",
|
"Exercise": "lms.plugins.exercise_renderer",
|
||||||
"Quiz": "lms.plugins.quiz_renderer",
|
"Quiz": "lms.plugins.quiz_renderer",
|
||||||
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
|
"YouTubeVideo": "lms.plugins.youtube_video_renderer",
|
||||||
"Video": "lms.plugins.video_renderer"
|
"Video": "lms.plugins.video_renderer",
|
||||||
|
"Assignment": "lms.plugins.assignment_renderer",
|
||||||
|
"Embed": "lms.plugins.embed_renderer",
|
||||||
|
"Audio": "lms.plugins.audio_renderer",
|
||||||
|
"PDF": "lms.plugins.pdf_renderer",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# page_renderer to manage profile pages
|
||||||
|
page_renderer = [
|
||||||
|
"lms.page_renderers.ProfileRedirectPage",
|
||||||
|
"lms.page_renderers.ProfilePage",
|
||||||
|
]
|
||||||
|
|
||||||
# set this to "/" to have profiles on the top-level
|
# set this to "/" to have profiles on the top-level
|
||||||
profile_url_prefix = "/users/"
|
profile_url_prefix = "/users/"
|
||||||
|
|
||||||
|
signup_form_template = "lms.plugins.show_custom_signup"
|
||||||
|
|
||||||
|
on_session_creation = "lms.overrides.user.on_session_creation"
|
||||||
|
|||||||
197
lms/install.py
Normal file
197
lms/install.py
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
|
||||||
|
|
||||||
|
|
||||||
|
def after_install():
|
||||||
|
add_pages_to_nav()
|
||||||
|
create_batch_source()
|
||||||
|
|
||||||
|
|
||||||
|
def after_sync():
|
||||||
|
create_lms_roles()
|
||||||
|
set_default_certificate_print_format()
|
||||||
|
add_all_roles_to("Administrator")
|
||||||
|
|
||||||
|
|
||||||
|
def add_pages_to_nav():
|
||||||
|
pages = [
|
||||||
|
{"label": "Explore", "idx": 1},
|
||||||
|
{"label": "Courses", "url": "/courses", "parent": "Explore", "idx": 2},
|
||||||
|
{"label": "Batches", "url": "/batches", "parent": "Explore", "idx": 3},
|
||||||
|
{"label": "Statistics", "url": "/statistics", "parent": "Explore", "idx": 4},
|
||||||
|
{"label": "Jobs", "url": "/job-openings", "parent": "Explore", "idx": 5},
|
||||||
|
{"label": "People", "url": "/community", "parent": "Explore", "idx": 6},
|
||||||
|
]
|
||||||
|
|
||||||
|
for page in pages:
|
||||||
|
filters = frappe._dict()
|
||||||
|
if page.get("url"):
|
||||||
|
filters["url"] = ["like", "%" + page.get("url") + "%"]
|
||||||
|
else:
|
||||||
|
filters["label"] = page.get("label")
|
||||||
|
|
||||||
|
if not frappe.db.exists("Top Bar Item", filters):
|
||||||
|
frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Top Bar Item",
|
||||||
|
"label": page.get("label"),
|
||||||
|
"url": page.get("url"),
|
||||||
|
"parent_label": page.get("parent"),
|
||||||
|
"idx": page.get("idx"),
|
||||||
|
"parent": "Website Settings",
|
||||||
|
"parenttype": "Website Settings",
|
||||||
|
"parentfield": "top_bar_items",
|
||||||
|
}
|
||||||
|
).save()
|
||||||
|
|
||||||
|
|
||||||
|
def before_uninstall():
|
||||||
|
delete_custom_fields()
|
||||||
|
delete_lms_roles()
|
||||||
|
|
||||||
|
|
||||||
|
def create_lms_roles():
|
||||||
|
create_course_creator_role()
|
||||||
|
create_moderator_role()
|
||||||
|
create_evaluator_role()
|
||||||
|
create_lms_student_role()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_lms_roles():
|
||||||
|
roles = ["Course Creator", "Moderator"]
|
||||||
|
for role in roles:
|
||||||
|
if frappe.db.exists("Role", role):
|
||||||
|
frappe.db.delete("Role", role)
|
||||||
|
|
||||||
|
|
||||||
|
def create_course_creator_role():
|
||||||
|
if not frappe.db.exists("Role", "Course Creator"):
|
||||||
|
role = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Role",
|
||||||
|
"role_name": "Course Creator",
|
||||||
|
"home_page": "",
|
||||||
|
"desk_access": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
|
||||||
|
def create_moderator_role():
|
||||||
|
if not frappe.db.exists("Role", "Moderator"):
|
||||||
|
role = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Role",
|
||||||
|
"role_name": "Moderator",
|
||||||
|
"home_page": "",
|
||||||
|
"desk_access": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
|
||||||
|
def create_evaluator_role():
|
||||||
|
if not frappe.db.exists("Role", "Class Evaluator"):
|
||||||
|
role = frappe.new_doc("Role")
|
||||||
|
role.update(
|
||||||
|
{
|
||||||
|
"role_name": "Class Evaluator",
|
||||||
|
"home_page": "",
|
||||||
|
"desk_access": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
|
||||||
|
def create_lms_student_role():
|
||||||
|
if not frappe.db.exists("Role", "LMS Student"):
|
||||||
|
role = frappe.new_doc("Role")
|
||||||
|
role.update(
|
||||||
|
{
|
||||||
|
"role_name": "LMS Student",
|
||||||
|
"home_page": "",
|
||||||
|
"desk_access": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
role.save()
|
||||||
|
|
||||||
|
|
||||||
|
def set_default_certificate_print_format():
|
||||||
|
filters = {
|
||||||
|
"doc_type": "LMS Certificate",
|
||||||
|
"property": "default_print_format",
|
||||||
|
}
|
||||||
|
if not frappe.db.exists("Property Setter", filters):
|
||||||
|
filters.update(
|
||||||
|
{
|
||||||
|
"doctype_or_field": "DocType",
|
||||||
|
"property_type": "Data",
|
||||||
|
"value": "Certificate",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
doc = frappe.new_doc("Property Setter")
|
||||||
|
doc.update(filters)
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_custom_fields():
|
||||||
|
fields = [
|
||||||
|
"user_category",
|
||||||
|
"headline",
|
||||||
|
"college",
|
||||||
|
"city",
|
||||||
|
"verify_terms",
|
||||||
|
"country",
|
||||||
|
"preferred_location",
|
||||||
|
"preferred_functions",
|
||||||
|
"preferred_industries",
|
||||||
|
"work_environment_column",
|
||||||
|
"time",
|
||||||
|
"role",
|
||||||
|
"carrer_preference_details",
|
||||||
|
"skill",
|
||||||
|
"certification_details",
|
||||||
|
"internship",
|
||||||
|
"branch",
|
||||||
|
"github",
|
||||||
|
"medium",
|
||||||
|
"linkedin",
|
||||||
|
"profession",
|
||||||
|
"looking_for_job",
|
||||||
|
"cover_image" "work_environment",
|
||||||
|
"dream_companies",
|
||||||
|
"career_preference_column",
|
||||||
|
"attire",
|
||||||
|
"collaboration",
|
||||||
|
"location_preference",
|
||||||
|
"company_type",
|
||||||
|
"skill_details",
|
||||||
|
"certification",
|
||||||
|
"education",
|
||||||
|
"work_experience",
|
||||||
|
"education_details",
|
||||||
|
"hide_private",
|
||||||
|
"work_experience_details",
|
||||||
|
"profile_complete",
|
||||||
|
]
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
frappe.db.delete("Custom Field", {"fieldname": field})
|
||||||
|
|
||||||
|
|
||||||
|
def create_batch_source():
|
||||||
|
sources = [
|
||||||
|
"Newsletter",
|
||||||
|
"LinkedIn",
|
||||||
|
"Twitter",
|
||||||
|
"Website",
|
||||||
|
"Friend/Colleague/Connection",
|
||||||
|
"Google Search",
|
||||||
|
]
|
||||||
|
|
||||||
|
for source in sources:
|
||||||
|
if not frappe.db.exists("LMS Source", source):
|
||||||
|
doc = frappe.new_doc("LMS Source")
|
||||||
|
doc.source = source
|
||||||
|
doc.save()
|
||||||
9
lms/job/doctype/job_opportunity/job_opportunity.js
Normal file
9
lms/job/doctype/job_opportunity/job_opportunity.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Job Opportunity", {
|
||||||
|
refresh: (frm) => {
|
||||||
|
if (frm.doc.name)
|
||||||
|
frm.add_web_link(`/job-openings/${frm.doc.name}`, "See on Website");
|
||||||
|
},
|
||||||
|
});
|
||||||
157
lms/job/doctype/job_opportunity/job_opportunity.json
Normal file
157
lms/job/doctype/job_opportunity/job_opportunity.json
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_import": 1,
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "format: JOB-{#####}",
|
||||||
|
"creation": "2022-02-07 12:01:41.074418",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"job_title",
|
||||||
|
"location",
|
||||||
|
"disabled",
|
||||||
|
"column_break_5",
|
||||||
|
"type",
|
||||||
|
"status",
|
||||||
|
"section_break_6",
|
||||||
|
"description",
|
||||||
|
"company_details_section",
|
||||||
|
"company_name",
|
||||||
|
"company_website",
|
||||||
|
"column_break_11",
|
||||||
|
"company_logo",
|
||||||
|
"application_link"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "job_title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Job Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "location",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Location",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_5",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Full Time",
|
||||||
|
"fieldname": "type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Type",
|
||||||
|
"options": "Full Time\nPart Time\nFreelance\nContract",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Open",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Open\nClosed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Text Editor",
|
||||||
|
"label": "Description",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Company Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Company Name",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company_website",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Company Website",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "company_logo",
|
||||||
|
"fieldtype": "Attach Image",
|
||||||
|
"label": "Company Logo",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "application_link",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Application Form Link",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "disabled",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Disabled"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"make_attachments_public": 1,
|
||||||
|
"modified": "2023-09-29 17:03:30.825021",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Job",
|
||||||
|
"name": "Job Opportunity",
|
||||||
|
"naming_rule": "Expression",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"select": 1,
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "LMS Student",
|
||||||
|
"select": 1,
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
|
"title_field": "job_title"
|
||||||
|
}
|
||||||
41
lms/job/doctype/job_opportunity/job_opportunity.py
Normal file
41
lms/job/doctype/job_opportunity/job_opportunity.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Copyright (c) 2021, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import get_link_to_form
|
||||||
|
from frappe.utils.user import get_system_managers
|
||||||
|
|
||||||
|
from lms.lms.utils import validate_image
|
||||||
|
|
||||||
|
|
||||||
|
class JobOpportunity(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_urls()
|
||||||
|
self.company_logo = validate_image(self.company_logo)
|
||||||
|
|
||||||
|
def validate_urls(self):
|
||||||
|
frappe.utils.validate_url(self.company_website, True)
|
||||||
|
frappe.utils.validate_url(self.application_link, True)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def report(job, reason):
|
||||||
|
system_managers = get_system_managers(only_name=True)
|
||||||
|
user = frappe.db.get_value("User", frappe.session.user, "full_name")
|
||||||
|
subject = _("User {0} has reported the job post {1}").format(user, job)
|
||||||
|
args = {
|
||||||
|
"job": job,
|
||||||
|
"job_url": get_link_to_form("Job Opportunity", job),
|
||||||
|
"user": user,
|
||||||
|
"reason": reason,
|
||||||
|
}
|
||||||
|
frappe.sendmail(
|
||||||
|
recipients=system_managers,
|
||||||
|
subject=subject,
|
||||||
|
header=[subject, "green"],
|
||||||
|
template="job_report",
|
||||||
|
args=args,
|
||||||
|
now=True,
|
||||||
|
)
|
||||||
9
lms/job/doctype/job_opportunity/test_job_opportunity.py
Normal file
9
lms/job/doctype/job_opportunity/test_job_opportunity.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestJobOpportunity(unittest.TestCase):
|
||||||
|
pass
|
||||||
7
lms/job/doctype/job_settings/job_settings.js
Normal file
7
lms/job/doctype/job_settings/job_settings.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Job Settings", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
54
lms/job/doctype/job_settings/job_settings.json
Normal file
54
lms/job/doctype/job_settings/job_settings.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2022-02-07 12:01:41.422955",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"allow_posting",
|
||||||
|
"title",
|
||||||
|
"subtitle"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "allow_posting",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Allow Job Posting From Website"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Job Board Title"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "subtitle",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Job Board Subtitle"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"issingle": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2022-02-11 15:56:38.958317",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Job",
|
||||||
|
"name": "Job Settings",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
9
lms/job/doctype/job_settings/job_settings.py
Normal file
9
lms/job/doctype/job_settings/job_settings.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2022, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class JobSettings(Document):
|
||||||
|
pass
|
||||||
9
lms/job/doctype/job_settings/test_job_settings.py
Normal file
9
lms/job/doctype/job_settings/test_job_settings.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2022, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestJobSettings(unittest.TestCase):
|
||||||
|
pass
|
||||||
28
lms/job/notification/new_job_alert/new_job_alert.json
Normal file
28
lms/job/notification/new_job_alert/new_job_alert.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"attach_print": 0,
|
||||||
|
"channel": "Email",
|
||||||
|
"creation": "2021-12-30 12:29:56.533903",
|
||||||
|
"days_in_advance": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Notification",
|
||||||
|
"document_type": "Job Opportunity",
|
||||||
|
"enabled": 1,
|
||||||
|
"event": "New",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"message": "Hey,\n\nA new Job Opportunity has been created. \n\n<p>Company Name: {{ doc.company_name}}</p>\n<p>Job Title: {{ doc.job_title}}</p>\n<p>Job Location: {{ doc.location}}</p><br>\n<p>Job Description: {{ doc.description}}</p><br>\n\n<p>Find all the posted jobs <a href=\"{{ frappe.utils.get_url() }}/app/job-opportunity\">here</a>.</p><br>\n",
|
||||||
|
"modified": "2021-12-30 12:54:13.382512",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Job",
|
||||||
|
"name": "New job alert",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"recipients": [
|
||||||
|
{
|
||||||
|
"receiver_by_role": "System Manager"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"send_system_notification": 0,
|
||||||
|
"send_to_all_assignees": 0,
|
||||||
|
"sender_email": "",
|
||||||
|
"subject": "New job added by {{ doc.company_name}}"
|
||||||
|
}
|
||||||
10
lms/job/notification/new_job_alert/new_job_alert.md
Normal file
10
lms/job/notification/new_job_alert/new_job_alert.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
Hey,
|
||||||
|
|
||||||
|
A new Job Opportunity has been created.
|
||||||
|
|
||||||
|
<p>Company Name: {{ doc.company_name}}</p>
|
||||||
|
<p>Job Title: {{ doc.job_title}}</p>
|
||||||
|
<p>Job Location: {{ doc.location}}</p><br>
|
||||||
|
<p>Job Description: {{ doc.description}}</p><br>
|
||||||
|
|
||||||
|
<p>Find all the posted jobs <a href="{{ frappe.utils.get_url() }}/app/job-opportunity">here</a>.</p><br>
|
||||||
6
lms/job/notification/new_job_alert/new_job_alert.py
Normal file
6
lms/job/notification/new_job_alert/new_job_alert.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
# do your magic here
|
||||||
|
pass
|
||||||
0
lms/job/web_form/job_opportunity/__init__.py
Normal file
0
lms/job/web_form/job_opportunity/__init__.py
Normal file
7
lms/job/web_form/job_opportunity/job_opportunity.js
Normal file
7
lms/job/web_form/job_opportunity/job_opportunity.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
frappe.ready(function () {
|
||||||
|
frappe.web_form.after_save = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = `/job-openings`;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
151
lms/job/web_form/job_opportunity/job_opportunity.json
Normal file
151
lms/job/web_form/job_opportunity/job_opportunity.json
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
"accept_payment": 0,
|
||||||
|
"allow_comments": 0,
|
||||||
|
"allow_delete": 0,
|
||||||
|
"allow_edit": 1,
|
||||||
|
"allow_incomplete": 0,
|
||||||
|
"allow_multiple": 1,
|
||||||
|
"allow_print": 0,
|
||||||
|
"amount": 0.0,
|
||||||
|
"amount_based_on_field": 0,
|
||||||
|
"apply_document_permissions": 1,
|
||||||
|
"button_label": "Save",
|
||||||
|
"creation": "2021-12-27 17:02:12.461145",
|
||||||
|
"custom_css": "[data-doctype=\"Web Form\"] {\n max-width: 720px;\n margin: 6rem auto;\n}",
|
||||||
|
"doc_type": "Job Opportunity",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Web Form",
|
||||||
|
"idx": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"list_columns": [],
|
||||||
|
"login_required": 1,
|
||||||
|
"max_attachment_size": 0,
|
||||||
|
"modified": "2022-09-15 17:22:43.957185",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Job",
|
||||||
|
"name": "job-opportunity",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"payment_button_label": "Buy Now",
|
||||||
|
"published": 1,
|
||||||
|
"route": "job-opportunity",
|
||||||
|
"show_attachments": 0,
|
||||||
|
"show_list": 1,
|
||||||
|
"show_sidebar": 0,
|
||||||
|
"success_message": "",
|
||||||
|
"success_url": "/job-openings",
|
||||||
|
"title": "Job Opportunity",
|
||||||
|
"web_form_fields": [
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "job_title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Job Title",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "location",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Location",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"default": "Full Time",
|
||||||
|
"fieldname": "type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Type",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"options": "Full Time\nPart Time\nFreelance\nContract",
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"default": "Open",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Status",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"options": "Open\nClosed",
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "company_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Company Name",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "company_website",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Company Website",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "company_logo",
|
||||||
|
"fieldtype": "Attach Image",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Company Logo",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "application_link",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Application Form Link",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Text Editor",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Description",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
6
lms/job/web_form/job_opportunity/job_opportunity.py
Normal file
6
lms/job/web_form/job_opportunity/job_opportunity.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
|
def get_context(context):
|
||||||
|
# do your magic here
|
||||||
|
pass
|
||||||
128
lms/lms/api.py
128
lms/lms/api.py
@@ -3,18 +3,17 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def autosave_section(section, code):
|
def autosave_section(section, code):
|
||||||
"""Saves the code edited in one of the sections.
|
"""Saves the code edited in one of the sections."""
|
||||||
"""
|
|
||||||
doc = frappe.get_doc(
|
doc = frappe.get_doc(
|
||||||
doctype="Code Revision",
|
doctype="Code Revision", section=section, code=code, author=frappe.session.user
|
||||||
section=section,
|
)
|
||||||
code=code,
|
|
||||||
author=frappe.session.user)
|
|
||||||
doc.insert()
|
doc.insert()
|
||||||
return {"name": doc.name}
|
return {"name": doc.name}
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def submit_solution(exercise, code):
|
def submit_solution(exercise, code):
|
||||||
"""Submits a solution.
|
"""Submits a solution.
|
||||||
@@ -22,26 +21,121 @@ def submit_solution(exercise, code):
|
|||||||
@exerecise: name of the exercise to submit
|
@exerecise: name of the exercise to submit
|
||||||
@code: solution to the exercise
|
@code: solution to the exercise
|
||||||
"""
|
"""
|
||||||
ex = frappe.get_doc("Exercise", exercise)
|
ex = frappe.get_doc("LMS Exercise", exercise)
|
||||||
if not ex:
|
if not ex:
|
||||||
return
|
return
|
||||||
doc = ex.submit(code)
|
doc = ex.submit(code)
|
||||||
return {"name": doc.name, "creation": doc.creation}
|
return {"name": doc.name, "creation": doc.creation}
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def save_current_lesson(course_name, lesson_name):
|
def save_current_lesson(course_name, lesson_name):
|
||||||
"""Saves the current lesson for a student/mentor.
|
"""Saves the current lesson for a student/mentor."""
|
||||||
"""
|
|
||||||
name = frappe.get_value(
|
name = frappe.get_value(
|
||||||
doctype="LMS Batch Membership",
|
doctype="LMS Enrollment",
|
||||||
filters={
|
filters={"course": course_name, "member": frappe.session.user},
|
||||||
"course": course_name,
|
fieldname="name",
|
||||||
"member": frappe.session.user
|
)
|
||||||
},
|
|
||||||
fieldname="name")
|
|
||||||
if not name:
|
if not name:
|
||||||
return
|
return
|
||||||
doc = frappe.get_doc("LMS Batch Membership", name)
|
doc = frappe.get_doc("LMS Enrollment", name)
|
||||||
doc.current_lesson = lesson_name
|
doc.current_lesson = lesson_name
|
||||||
doc.save(ignore_permissions=True)
|
doc.save()
|
||||||
return {"current_lesson": doc.current_lesson}
|
return {"current_lesson": doc.current_lesson}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def join_cohort(course, cohort, subgroup, invite_code):
|
||||||
|
"""Creates a Cohort Join Request for given user."""
|
||||||
|
course_doc = frappe.get_doc("LMS Course", course)
|
||||||
|
cohort_doc = course_doc and course_doc.get_cohort(cohort)
|
||||||
|
subgroup_doc = cohort_doc and cohort_doc.get_subgroup(subgroup)
|
||||||
|
|
||||||
|
if not subgroup_doc or subgroup_doc.invite_code != invite_code:
|
||||||
|
return {"ok": False, "error": "Invalid join link"}
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"doctype": "Cohort Join Request",
|
||||||
|
"cohort": cohort_doc.name,
|
||||||
|
"subgroup": subgroup_doc.name,
|
||||||
|
"email": frappe.session.user,
|
||||||
|
"status": "Pending",
|
||||||
|
}
|
||||||
|
# Don't insert duplicate records
|
||||||
|
if frappe.db.exists(data):
|
||||||
|
return {"ok": True, "status": "record found"}
|
||||||
|
else:
|
||||||
|
doc = frappe.get_doc(data)
|
||||||
|
doc.insert()
|
||||||
|
return {"ok": True, "status": "record created"}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def approve_cohort_join_request(join_request):
|
||||||
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
|
if not sg or r.status not in ["Pending", "Accepted"]:
|
||||||
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
|
if (
|
||||||
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
|
):
|
||||||
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
|
|
||||||
|
r.status = "Accepted"
|
||||||
|
r.save()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def reject_cohort_join_request(join_request):
|
||||||
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
|
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||||
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
|
if (
|
||||||
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
|
):
|
||||||
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
|
|
||||||
|
r.status = "Rejected"
|
||||||
|
r.save()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def undo_reject_cohort_join_request(join_request):
|
||||||
|
r = frappe.get_doc("Cohort Join Request", join_request)
|
||||||
|
sg = r and frappe.get_doc("Cohort Subgroup", r.subgroup)
|
||||||
|
# keeping Pending as well to consider the case of duplicate requests
|
||||||
|
if not sg or r.status not in ["Pending", "Rejected"]:
|
||||||
|
return {"ok": False, "error": "Invalid Join Request"}
|
||||||
|
if (
|
||||||
|
not sg.is_manager(frappe.session.user) and "System Manager" not in frappe.get_roles()
|
||||||
|
):
|
||||||
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
|
|
||||||
|
r.status = "Pending"
|
||||||
|
r.save()
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def add_mentor_to_subgroup(subgroup, email):
|
||||||
|
try:
|
||||||
|
sg = frappe.get_doc("Cohort Subgroup", subgroup)
|
||||||
|
except frappe.DoesNotExistError:
|
||||||
|
return {"ok": False, "error": f"Invalid subgroup: {subgroup}"}
|
||||||
|
|
||||||
|
if (
|
||||||
|
not sg.get_cohort().is_admin(frappe.session.user)
|
||||||
|
and "System Manager" not in frappe.get_roles()
|
||||||
|
):
|
||||||
|
return {"ok": False, "error": "Permission Deined"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = frappe.get_doc("User", email)
|
||||||
|
except frappe.DoesNotExistError:
|
||||||
|
return {"ok": False, "error": f"Invalid user: {email}"}
|
||||||
|
|
||||||
|
sg.add_mentor(email)
|
||||||
|
return {"ok": True}
|
||||||
|
|||||||
@@ -2,23 +2,26 @@
|
|||||||
"based_on": "creation",
|
"based_on": "creation",
|
||||||
"chart_name": "Course Enrollments",
|
"chart_name": "Course Enrollments",
|
||||||
"chart_type": "Count",
|
"chart_type": "Count",
|
||||||
|
"color": "#4463F0",
|
||||||
"creation": "2021-09-30 12:23:09.414853",
|
"creation": "2021-09-30 12:23:09.414853",
|
||||||
|
"custom_options": "{\"type\": \"line\", \"axisOptions\": {\"xIsSeries\": 1}, \"lineOptions\": {\"regionFill\": 1}}",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Dashboard Chart",
|
"doctype": "Dashboard Chart",
|
||||||
"document_type": "LMS Batch Membership",
|
"document_type": "LMS Enrollment",
|
||||||
"dynamic_filters_json": "[]",
|
"dynamic_filters_json": "[]",
|
||||||
"filters_json": "[]",
|
"filters_json": "[]",
|
||||||
"group_by_type": "Count",
|
"group_by_type": "Count",
|
||||||
"idx": 0,
|
"idx": 1,
|
||||||
"is_public": 1,
|
"is_public": 1,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"last_synced_on": "2021-10-17 13:32:21.745498",
|
"last_synced_on": "2022-10-20 10:46:56.859520",
|
||||||
"modified": "2021-10-21 17:31:06.997133",
|
"modified": "2022-10-20 11:30:26.863009",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Course Enrollments",
|
"name": "Course Enrollments",
|
||||||
"number_of_groups": 0,
|
"number_of_groups": 0,
|
||||||
"owner": "basawaraj@erpnext.com",
|
"owner": "basawaraj@erpnext.com",
|
||||||
|
"roles": [],
|
||||||
"source": "",
|
"source": "",
|
||||||
"time_interval": "Daily",
|
"time_interval": "Daily",
|
||||||
"timeseries": 1,
|
"timeseries": 1,
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"based_on": "creation",
|
||||||
|
"chart_name": "Lesson Completion",
|
||||||
|
"chart_type": "Count",
|
||||||
|
"color": "#4463F0",
|
||||||
|
"creation": "2022-11-09 16:52:19.021695",
|
||||||
|
"custom_options": "{\"type\": \"line\", \"axisOptions\": {\"xIsSeries\": 1}, \"lineOptions\": {\"regionFill\": 1}}",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Dashboard Chart",
|
||||||
|
"document_type": "LMS Course Progress",
|
||||||
|
"dynamic_filters_json": "[]",
|
||||||
|
"filters_json": "[]",
|
||||||
|
"group_by_type": "Count",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"modified": "2022-11-09 16:52:52.241756",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Lesson Completion",
|
||||||
|
"number_of_groups": 0,
|
||||||
|
"owner": "Administrator",
|
||||||
|
"parent_document_type": "",
|
||||||
|
"roles": [],
|
||||||
|
"source": "",
|
||||||
|
"time_interval": "Daily",
|
||||||
|
"timeseries": 1,
|
||||||
|
"timespan": "Last Month",
|
||||||
|
"type": "Line",
|
||||||
|
"use_report_chart": 0,
|
||||||
|
"value_based_on": "",
|
||||||
|
"y_axis": []
|
||||||
|
}
|
||||||
33
lms/lms/dashboard_chart/new_signups/new_signups.json
Normal file
33
lms/lms/dashboard_chart/new_signups/new_signups.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"based_on": "creation",
|
||||||
|
"chart_name": "New Signups",
|
||||||
|
"chart_type": "Count",
|
||||||
|
"color": "#4463F0",
|
||||||
|
"creation": "2021-09-28 18:57:50.047656",
|
||||||
|
"custom_options": "{\"type\": \"line\", \"axisOptions\": {\"xIsSeries\": 1}, \"lineOptions\": {\"regionFill\": 1}}",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Dashboard Chart",
|
||||||
|
"document_type": "User",
|
||||||
|
"dynamic_filters_json": "[]",
|
||||||
|
"filters_json": "[]",
|
||||||
|
"group_by_type": "Count",
|
||||||
|
"idx": 1,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"last_synced_on": "2022-10-20 10:46:56.849265",
|
||||||
|
"modified": "2022-10-20 11:31:17.184897",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "New Signups",
|
||||||
|
"number_of_groups": 0,
|
||||||
|
"owner": "basawaraj@erpnext.com",
|
||||||
|
"roles": [],
|
||||||
|
"source": "",
|
||||||
|
"time_interval": "Daily",
|
||||||
|
"timeseries": 1,
|
||||||
|
"timespan": "Last Quarter",
|
||||||
|
"type": "Line",
|
||||||
|
"use_report_chart": 0,
|
||||||
|
"value_based_on": "",
|
||||||
|
"y_axis": []
|
||||||
|
}
|
||||||
0
lms/lms/doctype/batch_course/__init__.py
Normal file
0
lms/lms/doctype/batch_course/__init__.py
Normal file
51
lms/lms/doctype/batch_course/batch_course.json
Normal file
51
lms/lms/doctype/batch_course/batch_course.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "autoincrement",
|
||||||
|
"creation": "2022-11-09 16:23:26.454527",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"course",
|
||||||
|
"title",
|
||||||
|
"evaluator"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "course.title",
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Course Title",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "evaluator",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Evaluator",
|
||||||
|
"options": "Course Evaluator"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-08-28 10:03:02.960844",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Batch Course",
|
||||||
|
"naming_rule": "Autoincrement",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
9
lms/lms/doctype/batch_course/batch_course.py
Normal file
9
lms/lms/doctype/batch_course/batch_course.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2022, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class BatchCourse(Document):
|
||||||
|
pass
|
||||||
0
lms/lms/doctype/batch_student/__init__.py
Normal file
0
lms/lms/doctype/batch_student/__init__.py
Normal file
7
lms/lms/doctype/batch_student/batch_student.js
Normal file
7
lms/lms/doctype/batch_student/batch_student.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2022, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Batch Student", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
83
lms/lms/doctype/batch_student/batch_student.json
Normal file
83
lms/lms/doctype/batch_student/batch_student.json
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2022-11-09 16:20:44.602545",
|
||||||
|
"default_view": "List",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"student_details_section",
|
||||||
|
"student",
|
||||||
|
"student_name",
|
||||||
|
"username",
|
||||||
|
"column_break_oduu",
|
||||||
|
"payment",
|
||||||
|
"source",
|
||||||
|
"confirmation_email_sent"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "student",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Student",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "student.full_name",
|
||||||
|
"fieldname": "student_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Student Name",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "student.username",
|
||||||
|
"fieldname": "username",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Username",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "student_details_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Student Details"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_oduu",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "payment",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Payment",
|
||||||
|
"options": "LMS Payment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "confirmation_email_sent",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Confirmation Email Sent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "source",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Source",
|
||||||
|
"options": "LMS Source"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-10-26 16:52:04.266693",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Batch Student",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
23
lms/lms/doctype/batch_student/batch_student.py
Normal file
23
lms/lms/doctype/batch_student/batch_student.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (c) 2022, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class BatchStudent(Document):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def enroll_batch(batch_name):
|
||||||
|
if frappe.db.exists(
|
||||||
|
"Batch Student", {"student": frappe.session.user, "parent": batch_name}
|
||||||
|
):
|
||||||
|
frappe.throw("You are already enrolled in this batch")
|
||||||
|
enrollment = frappe.new_doc("Batch Student")
|
||||||
|
enrollment.student = frappe.session.user
|
||||||
|
enrollment.parent = batch_name
|
||||||
|
enrollment.parentfield = "students"
|
||||||
|
enrollment.parenttype = "LMS Batch"
|
||||||
|
enrollment.save(ignore_permissions=True)
|
||||||
9
lms/lms/doctype/batch_student/test_batch_student.py
Normal file
9
lms/lms/doctype/batch_student/test_batch_student.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2022, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.tests.utils import FrappeTestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestBatchStudent(FrappeTestCase):
|
||||||
|
pass
|
||||||
0
lms/lms/doctype/certification/__init__.py
Normal file
0
lms/lms/doctype/certification/__init__.py
Normal file
7
lms/lms/doctype/certification/certification.js
Normal file
7
lms/lms/doctype/certification/certification.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2021, Frappe and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Certification", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
74
lms/lms/doctype/certification/certification.json
Normal file
74
lms/lms/doctype/certification/certification.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "hash",
|
||||||
|
"creation": "2021-12-07 12:20:37.143096",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"certification_name",
|
||||||
|
"organization",
|
||||||
|
"description",
|
||||||
|
"column_break_4",
|
||||||
|
"expire",
|
||||||
|
"issue_date",
|
||||||
|
"expiration_date"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "certification_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Certification Name",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "organization",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Organization",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "issue_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Issue Date",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: !doc.expire",
|
||||||
|
"fieldname": "expiration_date",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Expiration Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "expire",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "This certificate does no expire"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-12-21 10:05:43.377876",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Certification",
|
||||||
|
"naming_rule": "Random",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
|
}
|
||||||
9
lms/lms/doctype/certification/certification.py
Normal file
9
lms/lms/doctype/certification/certification.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class Certification(Document):
|
||||||
|
pass
|
||||||
9
lms/lms/doctype/certification/test_certification.py
Normal file
9
lms/lms/doctype/certification/test_certification.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCertification(unittest.TestCase):
|
||||||
|
pass
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
|
"autoname": "hash",
|
||||||
"creation": "2021-07-27 16:25:02.903245",
|
"creation": "2021-07-27 16:25:02.903245",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@@ -20,13 +21,15 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-30 10:35:30.014950",
|
"modified": "2022-03-15 09:39:41.937565",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "Chapter Reference",
|
"name": "Chapter Reference",
|
||||||
|
"naming_rule": "Random",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
class ChapterReference(Document):
|
class ChapterReference(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
0
lms/lms/doctype/cohort/__init__.py
Normal file
0
lms/lms/doctype/cohort/__init__.py
Normal file
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Exercise', {
|
frappe.ui.form.on("Cohort", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
133
lms/lms/doctype/cohort/cohort.json
Normal file
133
lms/lms/doctype/cohort/cohort.json
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "format:{course}/{slug}",
|
||||||
|
"creation": "2021-11-19 11:45:31.016097",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"course",
|
||||||
|
"title",
|
||||||
|
"slug",
|
||||||
|
"section_break_2",
|
||||||
|
"instructor",
|
||||||
|
"status",
|
||||||
|
"column_break_4",
|
||||||
|
"begin_date",
|
||||||
|
"end_date",
|
||||||
|
"duration",
|
||||||
|
"section_break_8",
|
||||||
|
"description",
|
||||||
|
"pages"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Markdown Editor",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "instructor",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Instructor",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Upcoming\nLive\nCompleted\nCancelled",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "begin_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Begin Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "end_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "End Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "duration",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Duration"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "slug",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Slug",
|
||||||
|
"reqd": 1,
|
||||||
|
"unique": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_8",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_2",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pages",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Pages",
|
||||||
|
"options": "Cohort Web Page"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"group": "Links",
|
||||||
|
"link_doctype": "Cohort Subgroup",
|
||||||
|
"link_fieldname": "cohort"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified": "2022-10-13 15:46:32.322926",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Cohort",
|
||||||
|
"naming_rule": "Expression",
|
||||||
|
"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",
|
||||||
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
79
lms/lms/doctype/cohort/cohort.py
Normal file
79
lms/lms/doctype/cohort/cohort.py
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class Cohort(Document):
|
||||||
|
def get_url(self):
|
||||||
|
return f"{frappe.utils.get_url()}/courses/{self.course}/cohorts/{self.slug}"
|
||||||
|
|
||||||
|
def get_subgroups(self, include_counts=False, sort_by=None):
|
||||||
|
names = frappe.get_all("Cohort Subgroup", filters={"cohort": self.name}, pluck="name")
|
||||||
|
subgroups = [frappe.get_cached_doc("Cohort Subgroup", name) for name in names]
|
||||||
|
subgroups = sorted(subgroups, key=lambda sg: sg.title)
|
||||||
|
|
||||||
|
if include_counts:
|
||||||
|
mentors = self._get_subgroup_counts("Cohort Mentor")
|
||||||
|
students = self._get_subgroup_counts("LMS Enrollment")
|
||||||
|
join_requests = self._get_subgroup_counts("Cohort Join Request", status="Pending")
|
||||||
|
for s in subgroups:
|
||||||
|
s.num_mentors = mentors.get(s.name, 0)
|
||||||
|
s.num_students = students.get(s.name, 0)
|
||||||
|
s.num_join_requests = join_requests.get(s.name, 0)
|
||||||
|
|
||||||
|
if sort_by:
|
||||||
|
subgroups.sort(key=lambda sg: getattr(sg, sort_by), reverse=True)
|
||||||
|
return subgroups
|
||||||
|
|
||||||
|
def _get_subgroup_counts(self, doctype, **kw):
|
||||||
|
rows = frappe.get_all(
|
||||||
|
doctype,
|
||||||
|
filters={"cohort": self.name, **kw},
|
||||||
|
fields=["subgroup", "count(*) as count"],
|
||||||
|
group_by="subgroup",
|
||||||
|
)
|
||||||
|
return {row["subgroup"]: row["count"] for row in rows}
|
||||||
|
|
||||||
|
def _get_count(self, doctype, **kw):
|
||||||
|
filters = {"cohort": self.name, **kw}
|
||||||
|
return frappe.db.count(doctype, filters=filters)
|
||||||
|
|
||||||
|
def get_page_template(self, slug, scope=None):
|
||||||
|
p = self.get_page(slug, scope=scope)
|
||||||
|
return p and p.get_template_html()
|
||||||
|
|
||||||
|
def get_page(self, slug, scope=None):
|
||||||
|
for p in self.pages:
|
||||||
|
if p.slug == slug and scope in [p.scope, None]:
|
||||||
|
return p
|
||||||
|
|
||||||
|
def get_pages(self, scope=None):
|
||||||
|
return [p for p in self.pages if scope in [p.scope, None]]
|
||||||
|
|
||||||
|
def get_stats(self):
|
||||||
|
return {
|
||||||
|
"subgroups": self._get_count("Cohort Subgroup"),
|
||||||
|
"mentors": self._get_count("Cohort Mentor"),
|
||||||
|
"students": self._get_count("LMS Enrollment"),
|
||||||
|
"join_requests": self._get_count("Cohort Join Request", status="Pending"),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_subgroup(self, slug):
|
||||||
|
q = dict(cohort=self.name, slug=slug)
|
||||||
|
name = frappe.db.get_value("Cohort Subgroup", q, "name")
|
||||||
|
return name and frappe.get_doc("Cohort Subgroup", name)
|
||||||
|
|
||||||
|
def get_mentor(self, email):
|
||||||
|
q = dict(cohort=self.name, email=email)
|
||||||
|
name = frappe.db.get_value("Cohort Mentor", q, "name")
|
||||||
|
return name and frappe.get_doc("Cohort Mentor", name)
|
||||||
|
|
||||||
|
def is_mentor(self, email):
|
||||||
|
q = {"doctype": "Cohort Mentor", "cohort": self.name, "email": email}
|
||||||
|
return frappe.db.exists(q)
|
||||||
|
|
||||||
|
def is_admin(self, email):
|
||||||
|
q = {"doctype": "Cohort Staff", "cohort": self.name, "email": email, "role": "Admin"}
|
||||||
|
return frappe.db.exists(q)
|
||||||
@@ -4,5 +4,6 @@
|
|||||||
# import frappe
|
# import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestLMSCertification(unittest.TestCase):
|
|
||||||
|
class TestCohort(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
0
lms/lms/doctype/cohort_join_request/__init__.py
Normal file
0
lms/lms/doctype/cohort_join_request/__init__.py
Normal file
@@ -1,8 +1,7 @@
|
|||||||
// Copyright (c) 2021, FOSS United and contributors
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('LMS Course Enrollment', {
|
frappe.ui.form.on("Cohort Join Request", {
|
||||||
// refresh: function(frm) {
|
// refresh: function(frm) {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
88
lms/lms/doctype/cohort_join_request/cohort_join_request.json
Normal file
88
lms/lms/doctype/cohort_join_request/cohort_join_request.json
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2021-11-19 16:27:41.716509",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cohort",
|
||||||
|
"email",
|
||||||
|
"column_break_3",
|
||||||
|
"subgroup",
|
||||||
|
"status"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "cohort",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cohort",
|
||||||
|
"options": "Cohort",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "subgroup",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Subgroup",
|
||||||
|
"options": "Cohort Subgroup",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "email",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "E-Mail",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Pending",
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Pending\nAccepted\nRejected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2023-09-29 17:08:18.950560",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Cohort Join Request",
|
||||||
|
"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,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "LMS Student",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": [],
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
52
lms/lms/doctype/cohort_join_request/cohort_join_request.py
Normal file
52
lms/lms/doctype/cohort_join_request/cohort_join_request.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class CohortJoinRequest(Document):
|
||||||
|
def on_update(self):
|
||||||
|
if self.status == "Accepted":
|
||||||
|
self.ensure_student()
|
||||||
|
|
||||||
|
def ensure_student(self):
|
||||||
|
# case 1 - user is already a member
|
||||||
|
q = {
|
||||||
|
"doctype": "LMS Enrollment",
|
||||||
|
"cohort": self.cohort,
|
||||||
|
"subgroup": self.subgroup,
|
||||||
|
"member": self.email,
|
||||||
|
"member_type": "Student",
|
||||||
|
}
|
||||||
|
if frappe.db.exists(q):
|
||||||
|
return
|
||||||
|
|
||||||
|
# case 2 - user has signed up for this course, possibly not this cohort
|
||||||
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
|
|
||||||
|
q = {
|
||||||
|
"doctype": "LMS Enrollment",
|
||||||
|
"course": cohort.course,
|
||||||
|
"member": self.email,
|
||||||
|
"member_type": "Student",
|
||||||
|
}
|
||||||
|
name = frappe.db.exists(q)
|
||||||
|
if name:
|
||||||
|
doc = frappe.get_doc("LMS Enrollment", name)
|
||||||
|
doc.cohort = self.cohort
|
||||||
|
doc.subgroup = self.subgroup
|
||||||
|
doc.save(ignore_permissions=True)
|
||||||
|
else:
|
||||||
|
# case 3 - user has not signed up for this course yet
|
||||||
|
data = {
|
||||||
|
"doctype": "LMS Enrollment",
|
||||||
|
"course": cohort.course,
|
||||||
|
"cohort": self.cohort,
|
||||||
|
"subgroup": self.subgroup,
|
||||||
|
"member": self.email,
|
||||||
|
"member_type": "Student",
|
||||||
|
"role": "Member",
|
||||||
|
}
|
||||||
|
doc = frappe.get_doc(data)
|
||||||
|
doc.insert(ignore_permissions=True)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCohortJoinRequest(unittest.TestCase):
|
||||||
|
pass
|
||||||
0
lms/lms/doctype/cohort_mentor/__init__.py
Normal file
0
lms/lms/doctype/cohort_mentor/__init__.py
Normal file
7
lms/lms/doctype/cohort_mentor/cohort_mentor.js
Normal file
7
lms/lms/doctype/cohort_mentor/cohort_mentor.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Cohort Mentor", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
73
lms/lms/doctype/cohort_mentor/cohort_mentor.json
Normal file
73
lms/lms/doctype/cohort_mentor/cohort_mentor.json
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2021-11-19 15:31:47.129156",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cohort",
|
||||||
|
"email",
|
||||||
|
"subgroup",
|
||||||
|
"course"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "cohort",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Cohort",
|
||||||
|
"options": "Cohort",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "email",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "E-mail",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "subgroup",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Primary Subgroup",
|
||||||
|
"options": "Cohort Subgroup",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "cohort.course",
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-11-29 16:32:33.235281",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Cohort Mentor",
|
||||||
|
"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",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
13
lms/lms/doctype/cohort_mentor/cohort_mentor.py
Normal file
13
lms/lms/doctype/cohort_mentor/cohort_mentor.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class CohortMentor(Document):
|
||||||
|
def get_subgroup(self):
|
||||||
|
return frappe.get_doc("Cohort Subgroup", self.subgroup)
|
||||||
|
|
||||||
|
def get_user(self):
|
||||||
|
return frappe.get_doc("User", self.email)
|
||||||
9
lms/lms/doctype/cohort_mentor/test_cohort_mentor.py
Normal file
9
lms/lms/doctype/cohort_mentor/test_cohort_mentor.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCohortMentor(unittest.TestCase):
|
||||||
|
pass
|
||||||
0
lms/lms/doctype/cohort_staff/__init__.py
Normal file
0
lms/lms/doctype/cohort_staff/__init__.py
Normal file
7
lms/lms/doctype/cohort_staff/cohort_staff.js
Normal file
7
lms/lms/doctype/cohort_staff/cohort_staff.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Cohort Staff", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
@@ -1,54 +1,61 @@
|
|||||||
{
|
{
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"creation": "2021-08-16 15:47:19.494055",
|
"allow_rename": 1,
|
||||||
|
"creation": "2021-11-19 15:35:00.551949",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"student",
|
"cohort",
|
||||||
"issue_date",
|
|
||||||
"column_break_3",
|
|
||||||
"course",
|
"course",
|
||||||
"expiry_date"
|
"column_break_3",
|
||||||
|
"email",
|
||||||
|
"role"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "student",
|
"fieldname": "cohort",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"label": "Cohort",
|
||||||
"label": "Student",
|
"options": "Cohort",
|
||||||
"options": "User"
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "issue_date",
|
"fieldname": "email",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Link",
|
||||||
"label": "Issue Date"
|
"in_list_view": 1,
|
||||||
|
"label": "User",
|
||||||
|
"options": "User",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "role",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Role",
|
||||||
|
"options": "Admin\nManager\nStaff",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "cohort.course",
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "course",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 1,
|
|
||||||
"label": "Course",
|
|
||||||
"options": "LMS Course"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "expiry_date",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"label": "Expiry Date"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-08-16 15:47:19.494055",
|
"modified": "2021-12-16 15:16:04.042372",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "LMS",
|
"module": "LMS",
|
||||||
"name": "LMS Certification",
|
"name": "Cohort Staff",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2021, FOSS United and contributors
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
# import frappe
|
# import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class LMSCourseEnrollment(Document):
|
|
||||||
|
class CohortStaff(Document):
|
||||||
pass
|
pass
|
||||||
9
lms/lms/doctype/cohort_staff/test_cohort_staff.py
Normal file
9
lms/lms/doctype/cohort_staff/test_cohort_staff.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCohortStaff(unittest.TestCase):
|
||||||
|
pass
|
||||||
0
lms/lms/doctype/cohort_subgroup/__init__.py
Normal file
0
lms/lms/doctype/cohort_subgroup/__init__.py
Normal file
7
lms/lms/doctype/cohort_subgroup/cohort_subgroup.js
Normal file
7
lms/lms/doctype/cohort_subgroup/cohort_subgroup.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2021, FOSS United and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on("Cohort Subgroup", {
|
||||||
|
// refresh: function(frm) {
|
||||||
|
// }
|
||||||
|
});
|
||||||
104
lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
Normal file
104
lms/lms/doctype/cohort_subgroup/cohort_subgroup.json
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"autoname": "format:{title} ({cohort})",
|
||||||
|
"creation": "2021-11-19 11:50:27.312434",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"cohort",
|
||||||
|
"slug",
|
||||||
|
"title",
|
||||||
|
"column_break_4",
|
||||||
|
"invite_code",
|
||||||
|
"course",
|
||||||
|
"section_break_7",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "cohort",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_preview": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Cohort",
|
||||||
|
"options": "Cohort",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "title",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_preview": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
|
"label": "Title",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Markdown Editor",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "invite_code",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Invite Code",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "slug",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Slug",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "cohort.course",
|
||||||
|
"fieldname": "course",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Course",
|
||||||
|
"options": "LMS Course",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_7",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"group": "Links",
|
||||||
|
"link_doctype": "Cohort Join Request",
|
||||||
|
"link_fieldname": "subgroup"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified": "2021-12-16 15:12:42.504883",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "LMS",
|
||||||
|
"name": "Cohort Subgroup",
|
||||||
|
"naming_rule": "Expression",
|
||||||
|
"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",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
88
lms/lms/doctype/cohort_subgroup/cohort_subgroup.py
Normal file
88
lms/lms/doctype/cohort_subgroup/cohort_subgroup.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import random_string
|
||||||
|
|
||||||
|
|
||||||
|
class CohortSubgroup(Document):
|
||||||
|
def before_save(self):
|
||||||
|
if not self.invite_code:
|
||||||
|
self.invite_code = random_string(8)
|
||||||
|
|
||||||
|
def get_url(self):
|
||||||
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
|
return (
|
||||||
|
f"{frappe.utils.get_url()}/courses/{self.course}/subgroups/{cohort.slug}/{self.slug}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_invite_link(self):
|
||||||
|
cohort = frappe.get_doc("Cohort", self.cohort)
|
||||||
|
return f"{frappe.utils.get_url()}/courses/{self.course}/join/{cohort.slug}/{self.slug}/{self.invite_code}"
|
||||||
|
|
||||||
|
def has_student(self, email):
|
||||||
|
"""Check if given user is a student of this subgroup."""
|
||||||
|
q = {"doctype": "LMS Enrollment", "subgroup": self.name, "member": email}
|
||||||
|
return frappe.db.exists(q)
|
||||||
|
|
||||||
|
def has_join_request(self, email):
|
||||||
|
"""Check if given user is a student of this subgroup."""
|
||||||
|
q = {"doctype": "Cohort Join Request", "subgroup": self.name, "email": email}
|
||||||
|
return frappe.db.exists(q)
|
||||||
|
|
||||||
|
def get_join_requests(self, status="Pending"):
|
||||||
|
q = {"subgroup": self.name, "status": status}
|
||||||
|
return frappe.get_all(
|
||||||
|
"Cohort Join Request", filters=q, fields=["*"], order_by="creation desc"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_mentors(self):
|
||||||
|
emails = frappe.get_all(
|
||||||
|
"Cohort Mentor", filters={"subgroup": self.name}, fields=["email"], pluck="email"
|
||||||
|
)
|
||||||
|
return self._get_users(emails)
|
||||||
|
|
||||||
|
def get_students(self):
|
||||||
|
emails = frappe.get_all(
|
||||||
|
"LMS Enrollment",
|
||||||
|
filters={"subgroup": self.name},
|
||||||
|
fields=["member"],
|
||||||
|
pluck="member",
|
||||||
|
page_length=1000,
|
||||||
|
)
|
||||||
|
return self._get_users(emails)
|
||||||
|
|
||||||
|
def _get_users(self, emails):
|
||||||
|
users = [frappe.get_cached_doc("User", email) for email in emails]
|
||||||
|
return sorted(users, key=lambda user: user.full_name)
|
||||||
|
|
||||||
|
def is_mentor(self, email):
|
||||||
|
q = {"doctype": "Cohort Mentor", "subgroup": self.name, "email": email}
|
||||||
|
return frappe.db.exists(q)
|
||||||
|
|
||||||
|
def is_manager(self, email):
|
||||||
|
"""Returns True if the given user is a manager of this subgroup.
|
||||||
|
|
||||||
|
Mentors of the subgroup, admins of the Cohort are considered as managers.
|
||||||
|
"""
|
||||||
|
return self.is_mentor(email) or self.get_cohort().is_admin(email)
|
||||||
|
|
||||||
|
def get_cohort(self):
|
||||||
|
return frappe.get_doc("Cohort", self.cohort)
|
||||||
|
|
||||||
|
def add_mentor(self, email):
|
||||||
|
d = {
|
||||||
|
"doctype": "Cohort Mentor",
|
||||||
|
"subgroup": self.name,
|
||||||
|
"cohort": self.cohort,
|
||||||
|
"email": email,
|
||||||
|
}
|
||||||
|
if frappe.db.exists(d):
|
||||||
|
return
|
||||||
|
doc = frappe.get_doc(d)
|
||||||
|
doc.insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
# def after_doctype_insert():
|
||||||
|
# frappe.db.add_unique("Cohort Subgroup", ("cohort", "slug"))
|
||||||
9
lms/lms/doctype/cohort_subgroup/test_cohort_subgroup.py
Normal file
9
lms/lms/doctype/cohort_subgroup/test_cohort_subgroup.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, FOSS United and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCohortSubgroup(unittest.TestCase):
|
||||||
|
pass
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user