Skip to content

Commit c214643

Browse files
committed
Merge branch 'sandershi'
2 parents f1c1922 + 9957ff8 commit c214643

32 files changed

+737
-304
lines changed

client/imports/ui/components/mdeditor/mdeditor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
ngAfterViewInit(){
3030
var self = this;
3131
// Instantiate SimpleMDE
32-
this.mde = new SimpleMDE({ element: this.elementRef.nativeElement.value });
32+
this.mde = new SimpleMDE({ element: this.textarea.nativeElement });
3333
// Read initial data from task markdown
3434
this.mde.value(self.mdData);
3535
// Catch changes

client/imports/ui/pages/course/course.html

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<a md-button [routerLink]="['/course/' + courseId + '/labs']">Labs</a>
1010
<a md-button [routerLink]="['/course/' + courseId + '/grades']" *ngIf="!isInstruct()">Grades</a>
1111
<a md-button [routerLink]="['/course/' + courseId]" *ngIf="isInstruct()">Students</a>
12+
<a md-button [routerLink]="['/course/' + courseId + '/lab-create']" *ngIf="isInstruct()">Upload</a>
1213
</div>
1314
</div>
1415
</md-toolbar>
@@ -17,7 +18,8 @@
1718
<a md-button [routerLink]="['/course/' + courseId]">{{ courseNumber }} Home</a>
1819
<a md-button [routerLink]="['/course/' + courseId + '/labs']">Labs</a>
1920
<a md-button [routerLink]="['/course/' + courseId + '/grades']" *ngIf="!isInstruct()">Grades</a>
20-
<a md-button [routerLink]="['/course/' + courseId]" *ngIf="isInstruct()">Students</a>
21+
<a md-button [routerLink]="['/course/' + courseId]" *ngIf="isInstruct()">Students</a>
22+
<a md-button [routerLink]="['/course/' + courseId + '/lab-create']" *ngIf="isInstruct()">Upload</a>
2123
</div>
2224
<md-icon fontIcon="tuxicon-right"></md-icon>
2325
</md-toolbar>

client/imports/ui/pages/course/course.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141

4242
this.subscribe('user-courses', () => {
4343
this.autorun(() => {
44-
this.courseNumber = Collections.courses.findOne({ _id: this.courseId }).course_number;
44+
if(typeof Collections.courses.findOne({ _id: this.courseId }) !== "undefined") {
45+
this.courseNumber = Collections.courses.findOne({ _id: this.courseId }).course_number;
46+
}
4547
});
4648
}, true);
4749
}

client/imports/ui/pages/course/lablist.html

+14-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,24 @@
1313
<th class="md-text-cell">Lab</th>
1414
<th>Tasks Completed</th>
1515
<th>Due Date</th>
16+
<th *ngIf="isInstruct()"></th>
1617
</tr>
1718
</thead>
1819
<tbody>
19-
<tr *ngFor="let lab of labs" [routerLink]="['/course/' + courseId + '/labs/' + lab.id]">
20-
<td class="md-text-cell">{{ lab.name }}</td>
20+
<tr *ngFor="let lab of getLabs()">
21+
<td class="md-text-cell" [routerLink]="['/course/' + courseId + '/labs/' + lab._id]">{{ lab.lab_name }}</td>
2122
<td>{{ lab.completed }}</td>
22-
<td>{{ lab.date }}</td>
23+
<td>soon</td>
24+
<td *ngIf="isInstruct()">
25+
<button md-button (click)="export.show(); exportLab(lab._id);">Export</button>
26+
<md-dialog #export>
27+
<md-dialog-title>
28+
Exported Markdown
29+
</md-dialog-title>
30+
<div>{{ exportData }}</div>
31+
<md-dialog-actions ok="Got It"></md-dialog-actions>
32+
</md-dialog>
33+
</td>
2334
</tr>
2435
</tbody>
2536
</md-data-table>

client/imports/ui/pages/course/lablist.ts

+68-48
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@
88
// Angular Material Imports
99
import { MATERIAL_DIRECTIVES, MATERIAL_PROVIDERS } from 'ng2-material';
1010
import { MdProgressBar } from '@angular2-material/progress-bar';
11+
import { OVERLAY_PROVIDERS } from '@angular2-material/core/overlay/overlay';
1112

1213
// Angular Meteor Imports
1314
import { MeteorComponent } from 'angular2-meteor';
1415
import { InjectUser } from 'angular2-meteor-accounts-ui';
1516

17+
// Roles
18+
import { Roles } from '../../../../../collections/users.ts';
19+
1620
// Declare Collections
1721
declare var Collections: any;
22+
declare var _: any;
1823

1924
// Inject current user into class
2025
@InjectUser("user")
@@ -28,81 +33,96 @@
2833
ROUTER_DIRECTIVES,
2934
MATERIAL_DIRECTIVES
3035
],
31-
providers: [ MATERIAL_PROVIDERS ]
36+
providers: [ MATERIAL_PROVIDERS, OVERLAY_PROVIDERS ]
3237
})
3338

3439
export class LabList extends MeteorComponent {
3540
user: Meteor.User;
3641
courseId: string;
3742
userId: string = Meteor.userId();
38-
labs: Array<Object> = [];
3943
courseRecord: any;
40-
cur_user: boolean;
44+
exportData: string = "";
45+
46+
// Test
47+
allLabs: Array<Object>;
48+
partialLabs: Array<Object>;
4149

4250
// Progress Bar Value
4351
public determinateValue: number = 0;
4452

4553
constructor(private route: ActivatedRoute, private router: Router) {
4654
super();
47-
}
4855

49-
getCourseRecords() {
50-
// Get from course_records
56+
// Get labs in course_records
5157
this.subscribe('course-records', () => {
5258
this.autorun(() => {
53-
if(this.cur_user) {
54-
// Student
55-
this.courseRecord = Collections.course_records.findOne({ course_id: this.courseId, user_id: Meteor.userId() });
56-
}
57-
else {
58-
var localCourseRecord = Collections.course_records.findOne({ course_id: this.courseId, user_id: this.userId });
59-
if(localCourseRecord === null || typeof localCourseRecord === "undefined") {
60-
// Admin
61-
this.courseRecord = Meteor.call('getUserCourseRecord', this.courseId, this.userId);
62-
}
63-
else {
64-
// Instructor
65-
this.courseRecord = localCourseRecord;
66-
}
67-
}
68-
this.setLabs();
69-
});
70-
}, true);
59+
var record = Collections.course_records.findOne({ course_id: this.courseId });
60+
this.partialLabs = record.labs;
61+
}, true);
62+
});
63+
64+
// Get all labs of this course
65+
this.subscribe('labs', () => {
66+
this.autorun(() => {
67+
this.allLabs = Collections.labs.find({ course_id: this.courseId }).fetch();
68+
}, true);
69+
});
7170
}
7271

73-
setLabs() {
74-
if(typeof this.courseRecord !== "undefined" && this.courseRecord !== null) {
75-
let labs = this.courseRecord.labs;
76-
let totalCompleted = 0;
77-
let totalNumTasks = 0;
78-
for (let i = 0; i < labs.length; i++) {
79-
let lab = labs[i];
80-
let tasksCompleted = 0;
81-
let tasks = lab.tasks;
82-
for (let j = 0; j < tasks.length; j++) {
83-
let task = tasks[j];
84-
if (task.status === 'COMPLETED') {
85-
tasksCompleted++;
72+
getLabs() {
73+
var finalLabs = []; // Return this, an array of formatted labs
74+
if(typeof this.partialLabs !== "undefined" && typeof this.allLabs !== "undefined") {
75+
// All labs from course database
76+
finalLabs = this.allLabs;
77+
// Get Lab Ids and compare with partial labs from course_records
78+
var finalLabIds = _.map(finalLabs, function(lb) { return lb._id; });
79+
for(let i = 0; i < finalLabIds.length; i++) {
80+
let currentLabId = finalLabIds[i];
81+
let numTasks = finalLabs[i].tasks.length;
82+
// Set default completed in case it is not in course_records
83+
finalLabs[i].completed = "0/" + numTasks;
84+
for(let j = 0; j < this.partialLabs.length; j++) {
85+
if((<any>(this.partialLabs[j]))._id.str === currentLabId.str) {
86+
finalLabs[i].completed = this.compTasks(this.partialLabs[j]) + "/" + numTasks;
8687
}
8788
}
88-
this.labs.push({
89-
'id': lab._id,
90-
'name': 'Lab ' + (i + 1).toString(),
91-
'completed': tasksCompleted.toString() + '/' + tasks.length.toString(),
92-
'date': 'soon'
93-
});
94-
totalCompleted += tasksCompleted;
95-
totalNumTasks += tasks.length;
9689
}
97-
this.determinateValue = (totalCompleted * 100.0) / totalNumTasks;
9890
}
91+
return finalLabs;
92+
}
93+
94+
compTasks(lab) {
95+
let comp = 0;
96+
for(let i = 0; i < lab.tasks.length; i++) {
97+
if(lab.tasks[i].status === "COMPLETED") {
98+
comp++;
99+
}
100+
}
101+
return comp;
99102
}
100103

101104
ngOnInit(){
102105
this.userId = this.router.routerState.parent(this.route).snapshot.params['userid'];
103106
this.courseId = this.router.routerState.parent(this.route).snapshot.params['courseid'];
104-
this.cur_user = (typeof this.userId === "undefined" || this.userId === null);
105-
this.getCourseRecords();
106107
}
107108

109+
isInstruct() {
110+
if(typeof this.courseId !== "undefined") {
111+
return Roles.isInstructorFor(this.courseId);
112+
}
113+
else {
114+
return false;
115+
}
116+
}
117+
exportLab(lab_id) {
118+
var self = this;
119+
Meteor.call('exportLab', lab_id, function(err, res) {
120+
if(err) {
121+
self.exportData = "Error getting data";
122+
}
123+
else {
124+
self.exportData = res;
125+
}
126+
});
127+
}
108128
}

client/imports/ui/pages/dashboard/dashboard.html

+48-10
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,51 @@
6262
</div>
6363

6464
<!-- Non-Logged In Dashboard -->
65-
<div class="tuxlab-dashboard" *ngIf="!user">
66-
<md-card>
67-
<md-card-title>
68-
<h1>Welcome to TuxLab!</h1>
69-
</md-card-title>
70-
<md-card-content>
71-
72-
</md-card-content>
73-
</md-card>
74-
</div>
65+
<div class="tuxlab-dashboard-nonuser" *ngIf="!user">
66+
67+
<!-- Section with Login and Background Picture -->
68+
<div class="login-section">
69+
<div class="login-wrapper">
70+
<div class="login-card">
71+
<h1>Welcome to TuxLab!</h1>
72+
<md-card>
73+
<md-card-content>
74+
<tuxlab-login></tuxlab-login>
75+
</md-card-content>
76+
</md-card>
77+
</div>
78+
</div>
79+
</div>
80+
81+
<!-- About Section -->
82+
<div class="about-section">
83+
<div class="set-max-width">
84+
<h1>About TuxLab</h1>
85+
<p>
86+
TuxLab is an opensource platform for creating interactive Linux courses.
87+
Click on the link below to read more about TuxLab!
88+
Come back if you like what you see, go away if you do not.
89+
Scroll down to browse some of the available courses on the site!
90+
</p>
91+
<div class="button-wrapper">
92+
<a md-raised-button href="http://www.tuxlab.org">Go To TuxLab.org</a>
93+
</div>
94+
</div>
95+
</div>
96+
97+
<!-- Courses Section -->
98+
<div class="courses-section">
99+
<div class="set-max-width">
100+
<h1>Explore Courses</h1>
101+
<p>
102+
These are some courses that you will be able to enroll
103+
in once you have registered.
104+
</p>
105+
<div>
106+
<tuxlab-exploreview></tuxlab-exploreview>
107+
</div>
108+
109+
</div>
110+
</div>
111+
112+
</div>

client/imports/ui/pages/dashboard/dashboard.ts

+19-11
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,38 @@
1515
import { courses } from '../../../../../collections/courses.ts';
1616
import { course_records } from '../../../../../collections/course_records.ts';
1717

18+
// Explore
19+
import { ExploreView } from '../../components/explore/explore.ts';
20+
21+
// Login
22+
import Login from '../account/login.ts';
23+
1824
// Define Dashboard Component
1925
@Component({
20-
selector: 'tuxlab-dashboard',
21-
templateUrl: '/client/imports/ui/pages/dashboard/dashboard.html',
22-
directives: [
23-
MATERIAL_DIRECTIVES,
24-
MD_SIDENAV_DIRECTIVES,
25-
ROUTER_DIRECTIVES
26-
]
26+
selector: 'tuxlab-dashboard',
27+
templateUrl: '/client/imports/ui/pages/dashboard/dashboard.html',
28+
directives: [
29+
MATERIAL_DIRECTIVES,
30+
MD_SIDENAV_DIRECTIVES,
31+
ROUTER_DIRECTIVES,
32+
Login,
33+
ExploreView
34+
]
2735
})
2836

2937
// Export Dashboard Class
3038
@InjectUser('user')
3139
export default class Dashboard extends MeteorComponent {
32-
user : Meteor.User;
40+
user : Meteor.User;
3341
private courses = [];
3442
private grades = [];
3543

3644
constructor() {
3745
super();
3846

39-
this.subscribe('user-courses', () => {
40-
this.courses = courses.find({}, { limit: 5 }).fetch();
41-
}, true);
47+
this.subscribe('user-courses', () => {
48+
this.courses = courses.find({}, { limit: 5 }).fetch();
49+
}, true);
4250
this.subscribe('course-records', () => {
4351
let records = course_records.find().fetch();
4452
for(let i = 0; i < records.length; i++) {

client/imports/ui/pages/lab/labcreate.ts

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export default class LabCreate extends MeteorComponent {
4646
this.lab.file = "";
4747
this.uploaded = false;
4848
this.output = "Compiling... Errors will display below.";
49+
50+
document.getElementById('course-content').style.maxWidth = "100%";
4951
}
5052

5153
/* UPLOAD HANDLER */

client/imports/ui/pages/lab/labview.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ export default class LabView extends MeteorComponent {
7979
slf.labProgress = "0 / "+slf.tasks.length;
8080
slf.auth = {
8181
username: Meteor.user().profile.nickname,
82-
password: res.sshInfo.pass,
83-
domain: "10.100.1.11"
82+
password: res.system.password,
83+
domain: res.system.node_ip
8484
};
8585
slf.taskUpdates = res.taskUpdates;
8686

client/imports/ui/routes/course.routes.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { GradeList } from '../pages/course/gradelist.ts';
1212
import { LabList } from '../pages/course/lablist.ts';
1313
import { CourseDashboard } from '../pages/course/course_dashboard.ts';
1414
import { GradeView } from '../pages/course/gradeview.ts';
15+
import LabCreate from '../pages/lab/labcreate.ts'
1516
import LabView from '../pages/lab/labview.ts';
1617

1718
export const courseRoutes: RouterConfig = [
@@ -23,7 +24,8 @@ export const courseRoutes: RouterConfig = [
2324
{ path: 'grades', component: GradeList },
2425
{ path: 'labs', component: LabList },
2526
{ path: 'labs/:labid', canActivate: [ GuardAuth, CourseGuardRecord ], component: LabView },
26-
{ path: 'grades/:gradeid', canActivate: [ GuardAuth ], component: GradeView }
27+
{ path: 'grades/:gradeid', canActivate: [ GuardAuth ], component: GradeView },
28+
{ path: 'lab-create', component: LabCreate }
2729
// { path: 'users', as: 'UserList', component: UserList },
2830
// { path: 'user/:userid', as: 'UserView', component: UserView },
2931
// { path: 'labs', as: 'LabList', component: LabList },

client/imports/ui/routes/routes.ts

-4
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ import { provideRouter, RouterConfig } from '@angular/router';
1414
// Error
1515
import ErrorPage from '../pages/error/error.ts'
1616

17-
// Lab
18-
import LabCreate from '../pages/lab/labcreate.ts'
19-
2017
// Course
2118
import { courseRoutes } from './course.routes.ts';
2219
import { CourseGuardRecord } from './course.guard.record.ts';
@@ -40,7 +37,6 @@ const routes : RouterConfig = [
4037
{ path: 'login', component: Login },
4138
{ path: 'terms', component: Terms },
4239
{ path: 'privacy', component: Privacy },
43-
{ path: 'lab-create', component: LabCreate },
4440
{ path: 'explore', component: Explore },
4541
{ path: 'courses', component: CourseList },
4642
{ path: 'error/:code', component: ErrorPage },

0 commit comments

Comments
 (0)