From 241c9bb1acbbcdbb1dc18a1d5e03f81aaff692d1 Mon Sep 17 00:00:00 2001 From: Don Mitchell Date: Mon, 19 Nov 2012 16:02:30 -0500 Subject: [PATCH] Checkpoint to rebase --- cms/static/js/models/course_relative.js | 59 + .../js/models/settings/course_detais.js | 40 + .../js/models/settings/course_settings.js | 13 + .../js/views/settings/main_settings_view.js | 29 + cms/static/sass/_settings.scss | 742 +++++++++++ cms/templates/settings.html | 1167 +++++++++++++++++ common/djangoapps/models/__init__.py | 0 common/djangoapps/models/course_relative.py | 25 + common/djangoapps/models/settings/__init__.py | 0 .../models/settings/course_details.py | 36 + 10 files changed, 2111 insertions(+) create mode 100644 cms/static/js/models/course_relative.js create mode 100644 cms/static/js/models/settings/course_detais.js create mode 100644 cms/static/js/models/settings/course_settings.js create mode 100644 cms/static/js/views/settings/main_settings_view.js create mode 100644 cms/static/sass/_settings.scss create mode 100644 cms/templates/settings.html create mode 100644 common/djangoapps/models/__init__.py create mode 100644 common/djangoapps/models/course_relative.py create mode 100644 common/djangoapps/models/settings/__init__.py create mode 100644 common/djangoapps/models/settings/course_details.py diff --git a/cms/static/js/models/course_relative.js b/cms/static/js/models/course_relative.js new file mode 100644 index 0000000000..c9f20d6789 --- /dev/null +++ b/cms/static/js/models/course_relative.js @@ -0,0 +1,59 @@ +CMS.Models.Location = Backbone.Models.extend({ + defaults: { + tag: "", + name: "", + course: "", + category: "", + name: "" + }, + toUrl: function(overrides) { + return + (overrides['tag'] ? overrides['tag'] : this.get('tag')) + "://" + + (overrides['name'] ? overrides['name'] : this.get('name')) + "/" + + (overrides['course'] ? overrides['course'] : this.get('course')) + "/" + + (overrides['category'] ? overrides['category'] : this.get('category')) + "/" + + (overrides['name'] ? overrides['name'] : this.get('name')) + "/"; + }, + _tagPattern = /[^:]+/g, + _fieldPattern = new RegExp('[^/]+','g'), + + parse: function(payload) { + if (payload instanceof Array) { + return { + tag: payload[0], + name: payload[1], + course: payload[2], + category: payload[3], + name: payload[4] + } + } + else if (payload instanceof String) { + var foundTag = this._tagPattern.exec(payload); + if (foundTag) { + this._fieldPattern.lastIndex = this._tagPattern.lastIndex; + return { + tag: foundTag, + name: this._fieldPattern.exec(payload), + course: this._fieldPattern.exec(payload), + category: this._fieldPattern.exec(payload), + name: this._fieldPattern.exec(payload) + } + } + else return null; + } + else { + return payload; + } + } +}); + +CMS.Models.CourseRelative = Backbone.Models.extend({ + defaults: { + course_location : null, // must never be null, but here to doc the field + idx : null // the index making it unique in the containing collection (no implied sort) + } +}); + +CMS.Models.CourseRelativeCollection = Backbone.Collections.extend({ + model : CourseRelative +}); \ No newline at end of file diff --git a/cms/static/js/models/settings/course_detais.js b/cms/static/js/models/settings/course_detais.js new file mode 100644 index 0000000000..aa1fd1463d --- /dev/null +++ b/cms/static/js/models/settings/course_detais.js @@ -0,0 +1,40 @@ +CMS.Models.Settings.CourseDetails = Backbone.Models.extend({ + defaults: { + location : null, # a Location model, required + start_date: null, + end_date: null, + milestones: null, # a CourseRelativeCollection + syllabus: null, + overview: "", + statement: "", + intro_video: null, + requirements: "", + effort: null, # an int or null + textbooks: null, # a CourseRelativeCollection + prereqs: null, # a CourseRelativeCollection + faqs: null # a CourseRelativeCollection + }, + + // When init'g from html script, ensure you pass {parse: true} as an option (2nd arg to reset) + parse: function(attributes) { + if (attributes['location']) { + attributes.location = new CMS.Models.Location(attributes.location); + }; + if (attributes['milestones']) { + attributes.milestones = new CMS.Models.CourseRelativeCollection(attributes.milestones); + }; + if (attributes['textbooks']) { + attributes.textbooks = new CMS.Models.CourseRelativeCollection(attributes.textbooks); + }; + if (attributes['prereqs']) { + attributes.prereqs = new CMS.Models.CourseRelativeCollection(attributes.prereqs); + }; + if (attributes['faqs']) { + attributes.faqs = new CMS.Models.CourseRelativeCollection(attributes.faqs); + }; + }, + + urlRoot: function() { + // TODO impl + } +}); diff --git a/cms/static/js/models/settings/course_settings.js b/cms/static/js/models/settings/course_settings.js new file mode 100644 index 0000000000..10a8f4df69 --- /dev/null +++ b/cms/static/js/models/settings/course_settings.js @@ -0,0 +1,13 @@ +CMS.Models.Settings.CourseSettings = Backbone.Model.extend({ + // a container for the models representing the n possible tabbed states + defaults: { + courseLocation: null, + // NOTE: keep these sync'd w/ the data-section names in settings-page-menu + details: null, + faculty: null, + grading: null, + problems: null, + discussions: null + } + // write getters which get the relevant sub model from the server if not already loaded +}) \ No newline at end of file diff --git a/cms/static/js/views/settings/main_settings_view.js b/cms/static/js/views/settings/main_settings_view.js new file mode 100644 index 0000000000..d910f4dbc0 --- /dev/null +++ b/cms/static/js/views/settings/main_settings_view.js @@ -0,0 +1,29 @@ +CMS.Views.Settings.Main = Backbone.View.extend({ + // Model class is CMS.Models.Settings.CourseSettings + // allow navigation between the tabs + events: { + 'click .settings-page-menu a': "showSettingsTab" + }, + initialize: function() { + // load templates + }, + render: function() { + // create any necessary subviews and put them onto the page + }, + + currentTab: null, + + showSettingsTab: function(e) { + this.currentTab = $(e.target).attr('data-section'); + $('.settings-page-section > section').hide(); + $('.settings-' + this.currentTab).show(); + $('.settings-page-menu .is-shown').removeClass('is-shown'); + $(e.target).addClass('is-shown'); + // fetch model for the tab if not loaded already + if (!this.model.has(this.currentTab)) { + // TODO disable screen until fetch completes? + this.model.retrieve(this.currentTab, function() { this.render(); }); + } + } + +}) \ No newline at end of file diff --git a/cms/static/sass/_settings.scss b/cms/static/sass/_settings.scss new file mode 100644 index 0000000000..89f7841df0 --- /dev/null +++ b/cms/static/sass/_settings.scss @@ -0,0 +1,742 @@ +.settings { + .settings-overview { + @extend .window; + @include clearfix; + display: table; + width: 100%; + + // layout + .sidebar { + display: table-cell; + float: none; + width: 20%; + padding: 30px 0 30px 20px; + @include border-radius(3px 0 0 3px); + background: $lightGrey; + } + + .main-column { + display: table-cell; + float: none; + width: 80%; + padding: 30px 40px 30px 60px; + } + + .settings-page-menu { + a { + display: block; + padding-left: 20px; + line-height: 52px; + + &.is-shown { + background: #fff; + @include border-radius(5px 0 0 5px); + } + } + } + + .settings-page-section { + > .alert { + display: none; + + &.is-shown { + display: block; + } + } + + > section { + display: none; + margin-bottom: 40px; + + &.is-shown { + display: block; + } + + &:last-child { + border-bottom: none; + } + + > .title { + margin-bottom: 30px; + font-size: 28px; + font-weight: 300; + color: $blue; + } + + > section { + margin-bottom: 100px; + @include clearfix; + + header { + @include clearfix; + border-bottom: 1px solid $mediumGrey; + margin-bottom: 20px; + padding-bottom: 10px; + + h3 { + color: $darkGrey; + float: left; + + margin: 0 40px 0 0; + text-transform: uppercase; + } + + .detail { + float: right; + marign-top: 3px; + color: $mediumGrey; + font-size: 13px; + } + } + + &:last-child { + padding-bottom: 0; + border-bottom: none; + } + } + } + } + + // form basics + label, .label { + padding: 0; + border: none; + background: none; + font-size: 15px; + font-weight: 400; + + &.check-label { + display: inline; + margin-left: 10px; + } + + &.ranges { + margin-bottom: 20px; + } + } + + input, textarea { + @include transition(all 1s ease-in-out); + @include box-sizing(border-box); + font-size: 15px; + + &.long { + width: 100%; + } + + &.tall { + height: 200px; + } + + &.short { + width: 25%; + } + + &.date { + + } + + &:focus { + @include linear-gradient(tint($blue, 80%), tint($blue, 90%)); + border-color: $blue; + outline: 0; + } + + &:disabled { + color: $darkGrey; + background: $lightGrey; + } + } + + .input-default { + color: $darkGrey; + background: $lightGrey; + } + + ::-webkit-input-placeholder { + color: $mediumGrey; + font-size: 13px; + } + :-moz-placeholder { + color: $mediumGrey; + font-size: 13px; + } + + .field.ui-status { + + > .input { + display: block !important; + margin-bottom: 15px; + } + + .ui-status-input-checkbox, .ui-status-input-radio { + position: absolute; + top: -9999px; + left: -9999px; + } + + label { + cursor: pointer; + } + + .ui-status-input-checkbox ~ label, .ui-status-input-radio ~ label { + position: relative; + left: -30px; + display: inline-block; + z-index: 100; + margin: 0 0 0 5px; + padding-left: 30px; + color: $offBlack; + opacity: 0.50; + cursor: pointer; + @include transition(opacity 0.25s ease-in-out); + + &:before { + display: inline-block; + margin-right: 10px; + } + + &:after { + display: inline-block; + margin-left: 10px; + } + + ~ .tip { + margin-top: 0; + @include transition(color 0.25s ease-in-out); + } + } + + .ui-status-indic { + position: relative; + top: 2px; + z-index: 10; + display: inline-block; + height: 15px; + width: 15px; + border: 2px; + background: $offBlack; + opacity: 0.50; + @include border-radius(50px); + @include box-sizing(border-box); + @include transition(opacity 0.25s ease-in-out); + } + + .ui-status-input-checkbox:checked ~ label, .ui-status-input-radio:checked ~ label { + opacity: 0.99; + + &:after { + } + + &:before { + } + + ~ .tip { + color: $darkGrey; + } + } + + .ui-status-input-checkbox:checked ~ .ui-status-indic, .ui-status-input-radio:checked ~ .ui-status-indic { + opacity: 0.99; + } + } + + .tip { + color: $mediumGrey; + font-size: 13px; + } + + + // form layouts + .row { + margin-bottom: 30px; + padding-bottom: 30px; + border-bottom: 1px solid $lightGrey; + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + border-bottom: none; + } + + // structural labels, not semantic labels per se + > label, .label { + display: inline-block; + vertical-align: top; + } + + // tips + .tip-inline { + display: inline-block; + margin-left: 10px; + } + + .tip-stacked { + display: block; + margin-top: 10px; + } + + // structural field, not semantic fields per se + .field { + display: inline-block; + width: 100%; + + > input, > textarea, .input { + display: inline-block; + + &:last-child { + margin-bottom: 0; + } + + .group { + input, textarea { + margin-bottom: 5px; + } + + .label, label { + font-size: 13px; + } + } + + // multi-field + &.multi { + display: block; + background: tint($lightGrey, 50%); + padding: 15px; + @include border-radius(4px); + @include box-sizing(border-box); + + .group { + margin-bottom: 10px; + + &:last-child { + margin-bottom: 0; + } + + input, .input, textarea { + + } + } + } + + // multi stacked + &.multi-stacked { + + .group { + input, .input, textarea { + width: 100%; + } + } + } + + // multi-field inline + &.multi-inline { + @include clearfix; + + .group { + float: left; + margin-right: 20px; + + &:nth-child(2) { + margin-right: 0; + } + + .input, input, textarea { + width: 100%; + } + } + + .remove-item { + float: right; + } + } + } + + // input-list + .input-list { + + .input { + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 1px dotted $lightGrey; + + &:last-child { + border: 0; + } + } + } + + // enumerated inputs + &.enum { + } + } + + // layout - aligned label/field pairs + &.row-col2 { + + > label, .label { + width: 200px; + } + + .field { + width: 400px; + } + + &.multi-inline { + @include clearfix; + + .group { + width: 170px; + } + } + } + } + + // editing controls - adding + .new-item, .replace-item { + clear: both; + display: block; + margin-top: 10px; + padding-bottom: 10px; + @include grey-button; + @include box-sizing(border-box); + } + + + // editing controls - removing + .remove-item { + clear: both; + display: block; + opacity: 0.75; + font-size: 13px; + text-align: right; + @include transition(opacity 0.25s ease-in-out); + + + &:hover { + color: $blue; + opacity: 0.99; + } + } + + // editing controls - preview + .input-existing { + display: block !important; + + .current { + width: 100%; + margin: 10px 0; + padding: 15px; + @include box-sizing(border-box); + @include border-radius(5px); + background: tint($blue, 80%); + } + } + + // specific sections + .settings-details { + + } + + .settings-faculty { + + .settings-faculty-members { + + > header { + display: none; + } + + .field .multi { + display: block; + margin-bottom: 40px; + padding: 20px; + background: tint($lightGrey, 50%); + @include border-radius(4px); + @include box-sizing(border-box); + } + + .course-faculty-list-item { + + .row { + + &:nth-child(4) { + padding-bottom: 0; + border-bottom: none; + } + } + } + + #course-faculty-bio-input { + margin-bottom: 0; + } + + .new-course-faculty-item { + } + + .current-faculty-photo { + height: 115px; + width: 115px; + overflow: hidden; + + img { + display: block; + min-height: 100%; + max-width: 100%; + } + } + } + } + + .settings-grading { + + + .course-grading-gradeweight, .course-grading-totalassignments, .course-grading-totalassignmentsdroppable { + + input { + width: 73px; + } + } + } + + .settings-handouts { + + } + + .settings-problems { + + > section { + + &.is-shown { + display: block; + } + } + } + + .settings-discussions { + + } + + // states + label.is-focused { + color: $blue; + @include transition(color 1s ease-in-out); + } + + // extras/abbreviations + // .settings-extras { + + // > header { + // cursor: pointer; + + // &.active { + + // } + // } + + // > div { + // display: none; + // @include transition(display 0.25s ease-in-out); + + // &.is-shown { + // display: block; + // } + // } + // } + + // misc + .divide { + display: none; + } + } + + + + h3 { + margin-bottom: 30px; + font-size: 15px; + font-weight: 700; + color: $blue; + } + + .grade-controls { + @include clearfix; + } + + .new-grade-button { + position: relative; + float: left; + display: block; + width: 29px; + height: 29px; + margin: 4px 10px 0 0; + border-radius: 20px; + border: 1px solid $darkGrey; + @include linear-gradient(top, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)); + background-color: #d1dae3; + @include box-shadow(0 1px 0 rgba(255, 255, 255, .3) inset); + color: #6d788b; + + .plus-icon { + position: absolute; + top: 50%; + left: 50%; + margin-left: -6px; + margin-top: -6px; + } + } + + .grade-slider { + float: right; + width: 95%; + height: 80px; + + .grade-bar { + position: relative; + width: 100%; + height: 50px; + background: $lightGrey; + + .increments { + position: relative; + + li { + position: absolute; + top: 52px; + width: 30px; + margin-left: -15px; + font-size: 9px; + text-align: center; + + &.increment-0 { + left: 0; + } + + &.increment-10 { + left: 10%; + } + + &.increment-20 { + left: 20%; + } + + &.increment-30 { + left: 30%; + } + + &.increment-40 { + left: 40%; + } + + &.increment-50 { + left: 50%; + } + + &.increment-60 { + left: 60%; + } + + &.increment-70 { + left: 70%; + } + + &.increment-80 { + left: 80%; + } + + &.increment-90 { + left: 90%; + } + + &.increment-100 { + left: 100%; + } + } + } + + .grades { + position: relative; + + li { + position: absolute; + top: 0; + height: 50px; + text-align: right; + + &:hover, + &.is-dragging { + .remove-button { + display: block; + } + } + + &.is-dragging { + + + } + + .remove-button { + display: none; + position: absolute; + top: -17px; + right: 1px; + height: 17px; + font-size: 10px; + } + + &:nth-child(1) { + background: #4fe696; + } + + &:nth-child(2) { + background: #ffdf7e; + } + + &:nth-child(3) { + background: #ffb657; + } + + &:nth-child(4) { + background: #fb336c; + } + + &:nth-child(5) { + background: #ef54a1; + } + + .letter-grade { + display: block; + margin: 10px 15px 0 0; + font-size: 16px; + font-weight: 700; + line-height: 14px; + } + + .range { + display: block; + margin-right: 15px; + font-size: 10px; + line-height: 12px; + } + + .drag-bar { + position: absolute; + top: 0; + right: -1px; + height: 50px; + width: 2px; + background-color: #fff; + @include box-shadow(-1px 0 3px rgba(0,0,0,0.1)); + + cursor: ew-resize; + @include transition(none); + + &:hover { + width: 6px; + right: -2px; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/cms/templates/settings.html b/cms/templates/settings.html new file mode 100644 index 0000000000..3f7094fad8 --- /dev/null +++ b/cms/templates/settings.html @@ -0,0 +1,1167 @@ +<%inherit file="base.html" /> +<%block name="bodyclass">settings +<%block name="title">Settings + +<%namespace name='static' file='static_content.html'/> + +<%block name="jsextra"> + + + + + + + + + + + + + + +<%block name="content"> + +
+
+

Settings

+
+ +
+ +
+

Course Details

+ +
+
+

Basic Information

+ The nuts and bolts of your course +
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ + e.g. 101x +
+
+
+
+ +
+ +
+
+

Course Schedule

+ Important steps and segments of your your course +
+ +
+ +
+
+ + First day the class begins +
+
+
+ +
+ +
+
+ + Last day the class begins +
+
+
+ +
+

Milestones:

+ +
+
    +
  • +
    + + +
    + +
    + + +
    + + Delete Milestone +
  • + +
  • +
    + + +
    + +
    + + +
    +
  • +
+ + + New Course Milestone + +
+
+ +
+ +
+
+
+ CS184x_syllabus.pdf +
+ + Delete Syllabus + + PDF formatting preferred +
+ +
+ + Upload Syllabus + + PDF formatting preferred +
+
+
+
+ +
+ +
+
+

Introducing Your Course

+ Information for perspective students +
+ +
+ +
+
+ + Detailed summary of concepts and lessons covered +
+
+
+ +
+ +
+
+ + 1-2 sentences used to introduce your class to perspective students +
+
+
+ +
+ +
+
+
+ +
+ + Delete Video +
+ +
+ + Upload Video + + Video restrictions go here +
+
+
+
+ +
+ +
+
+

Requirements

+ Expectations of the students taking this course +
+ +
+ +
+
+ + Supplies, software, and set-up that students will need +
+
+
+ +
+ +
+
+ + Time students should spend on all course work +
+
+
+ +
+

Textbooks:

+ +
+
    +
  • +
    + + +
    + +
    + + +
    + + Delete Textbook +
  • + +
  • +
    + + +
    + +
    + + +
    + +
  • +
+ + + New Textbook + +
+
+ +
+

Prerequisites:

+ +
+
    +
  • +
    + + +
    + +
    + + +
    + + Delete Prerequisite +
  • + +
  • +
    + + +
    + +
    + + +
    +
  • +
+ + + New Prerequisite + +
+
+
+ +
+ +
+
+

More Information

+ Other helpful information about the course +
+ +
+

FAQs:

+ +
+ + + + New Question & Answer + +
+
+
+
+ +
+

Faculty

+ +
+
+

Faculty Members

+ Individuals instructing and help with this course +
+ +
+
+ + + + New Faculty Member + +
+
+
+ +
+ +
+

Grading

+ +
+
+

Overall Grade Range

+ Course grade ranges and their values +
+ +
+ +
+ +
+
+
    +
  1. 0
  2. +
  3. 10
  4. +
  5. 20
  6. +
  7. 30
  8. +
  9. 40
  10. +
  11. 50
  12. +
  13. 60
  14. +
  15. 70
  16. +
  17. 80
  18. +
  19. 90
  20. +
  21. 100
  22. +
+
    +
  1. + A + 81-100 + remove +
  2. +
  3. + B + 71-80 + + remove +
  4. +
  5. + C + 0-70 + + remove +
  6. +
  7. + F + 0-50 + + remove +
  8. +
+
+
+
+ +
+
+ +
+
+

General Grading

+ Deadlines and Requirements +
+ +
+ + +
+
+ + Boston, MA Local Time (UTC/GMT -5 hours) + Convert to your time zone +
+
+
+ +
+ + +
+
+ + e.g. +5 minutes +
+
+
+
+ +
+
+

Lesson Exercises

+ Grading in-lesson question & problems +
+ +
+ + +
+
+ + e.g. 25% +
+
+
+ +
+ + +
+
+ + total exercises assigned +
+
+
+ +
+ + +
+
+ + total exercises that won't be graded +
+
+
+
+ +
+
+

Labs

+ Grading in-lesson question & problems +
+ +
+ + +
+
+ + e.g. 25% +
+
+
+ +
+ + +
+
+ + total labs assigned +
+
+
+ +
+ + +
+
+ + total labs that won't be graded +
+
+
+
+ +
+
+

Exams

+ Grading in-lesson question & problems +
+ +
+ + +
+
+ + e.g. 50% +
+
+
+ +
+ + +
+
+ + total exams held +
+
+
+ +
+ + +
+
+ + total exams that won't be graded +
+
+
+
+
+ +
+

Problems

+ +
+
+

General Settings

+ Course-wide settings for all problems +
+ +
+

Problem Randomization:

+ +
+
+ +
+ + randomize all problems +
+ +
+ +
+ + do not randomize problems +
+ +
+ +
+ + randomize problems per student +
+
+
+ +
+

Show Answers:

+ +
+
+ +
+ + Answers will be shown after the number of attempts has been met +
+ +
+ +
+ + Answers will never be shown, regardless of attempts +
+
+
+ +
+ + +
+
+ + To set infinite atttempts, use "0" +
+
+
+
+ +
+
+

Lesson Exercises

+ In-lesson question & problem specific rules +
+ +
+

Problem Randomization:

+ +
+
+ +
+ + randomize all problems +
+ +
+ +
+ + do not randomize problems +
+ +
+ +
+ + randomize problems per student +
+
+
+ +
+

Show Answers:

+ +
+
+ +
+ + Answers will be shown after the number of attempts has been met +
+ +
+ +
+ + Answers will never be shown, regardless of attempts +
+
+
+ +
+ + +
+
+ + To set infinite atttempts, use "0" +
+
+
+
+ +
+
+

Labs

+ Exploratory assignment-specific rules +
+ +
+

Problem Randomization:

+ +
+
+ +
+ + randomize all problems +
+ +
+ +
+ + do not randomize problems +
+ +
+ +
+ + randomize problems per student +
+
+
+ +
+

Show Answers:

+ +
+
+ +
+ + Answers will be shown after the number of attempts has been met +
+ +
+ +
+ + Answers will never be shown, regardless of attempts +
+
+
+ +
+ + +
+
+ + To set infinite atttempts, use "0" +
+
+
+
+ +
+
+

Exams

+ Mid-term and final exam-specific rules +
+ +
+

Problem Randomization:

+ +
+
+ +
+ + randomize all problems +
+ +
+ +
+ + do not randomize problems +
+ +
+ +
+ + randomize problems per student +
+
+
+ +
+

Show Answers:

+ +
+
+ +
+ + Answers will be shown after the number of attempts has been met +
+ +
+ +
+ + Answers will never be shown, regardless of attempts +
+
+
+ +
+ + +
+
+ + To set infinite atttempts, use "0" +
+
+
+
+
+ +
+

Discussions

+ +
+
+

General Settings

+ Course-wide settings for online discussion +
+ +
+

Anonymous Discussions:

+ +
+
+ +
+ + Students and faculty will be able to post anonymously +
+ +
+ +
+ + Posting anonymously is not allowed. Any previous anonymous posts will be reverted to non-anonymous +
+
+
+
+ +
+
+

Discussion Categories

+ +
+ + + + New Discussion Category + +
+
+ +
+

Create Discussion Categories per Unit

+ +
+
+ +
+ + This option is automatically set currently +
+
+
+
+
+
+
+
+
+ diff --git a/common/djangoapps/models/__init__.py b/common/djangoapps/models/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/models/course_relative.py b/common/djangoapps/models/course_relative.py new file mode 100644 index 0000000000..4dfb83d183 --- /dev/null +++ b/common/djangoapps/models/course_relative.py @@ -0,0 +1,25 @@ +class CourseRelativeMember: + def __init__(self, location, idx): + self.course_location = location # a Location obj + self.idx = idx # which milestone this represents. Hopefully persisted # so we don't have race conditions + +### ??? If 2+ courses use the same textbook or other asset, should they point to the same db record? +class linked_asset(CourseRelativeMember): + """ + Something uploaded to our asset lib which has a name/label and location. Here it's tracked by course and index, but + we could replace the label/url w/ a pointer to a real asset and keep the join info here. + """ + def __init__(self, location, idx): + CourseRelativeMember.__init__(self, location, idx) + self.label = "" + self.url = None + +class summary_detail_pair(CourseRelativeMember): + """ + A short text with an arbitrary html descriptor used for paired label - details elements. + """ + def __init__(self, location, idx): + CourseRelativeMember.__init__(self, location, idx) + self.summary = "" + self.detail = "" + \ No newline at end of file diff --git a/common/djangoapps/models/settings/__init__.py b/common/djangoapps/models/settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/djangoapps/models/settings/course_details.py b/common/djangoapps/models/settings/course_details.py new file mode 100644 index 0000000000..95ad22efb9 --- /dev/null +++ b/common/djangoapps/models/settings/course_details.py @@ -0,0 +1,36 @@ +from common.djangoapps.models.course_relative import CourseRelativeMember + +### A basic question is whether to break the details into schedule, intro, requirements, and misc sub objects +class CourseDetails: + def __init__(self, location): + self.course_location = location # a Location obj + self.start_date = None + self.end_date = None + self.milestones = [] + self.syllabus = None # a pdf file asset + self.overview = "" # html to render as the overview + self.statement = "" + self.intro_video = None # a video pointer + self.requirements = "" # html + self.effort = None # int hours/week + self.textbooks = [] # linked_asset + self.prereqs = [] # linked_asset + self.faqs = [] # summary_detail_pair + + @classmethod + def fetch(cls, course_location): + """ + Fetch the course details for the given course from persistence and return a CourseDetails model. + """ + course = cls(course_location) + + # TODO implement + + return course + +class CourseMilestone(CourseRelativeMember): + def __init__(self, location, idx): + CourseRelativeMember.__init__(self, location, idx) + self.date = None + self.description = "" +