-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
121 changed files
with
3,239 additions
and
183 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions
7
app/src/main/java/org/stepic/droid/adaptive/listeners/AdaptiveReactionListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package org.stepic.droid.adaptive.listeners | ||
|
||
import org.stepic.droid.adaptive.model.Reaction | ||
|
||
interface AdaptiveReactionListener { | ||
fun createReaction(lessonId: Long, reaction: Reaction) | ||
} |
6 changes: 6 additions & 0 deletions
6
app/src/main/java/org/stepic/droid/adaptive/listeners/AnswerListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.stepic.droid.adaptive.listeners | ||
|
||
interface AnswerListener { | ||
fun onCorrectAnswer(submissionId: Long) | ||
fun onWrongAnswer() | ||
} |
150 changes: 150 additions & 0 deletions
150
app/src/main/java/org/stepic/droid/adaptive/math/LinearRegression.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package org.stepic.droid.adaptive.math; | ||
|
||
/****************************************************************************** | ||
* | ||
* Compute least squares solution to y = beta * x + alpha. | ||
* Simple linear regression. | ||
* | ||
******************************************************************************/ | ||
|
||
|
||
import java.util.Locale; | ||
|
||
/** | ||
* The {@code LinearRegression} class performs a simple linear regression | ||
* on an set of <em>n</em> data points (<em>y<sub>i</sub></em>, <em>x<sub>i</sub></em>). | ||
* That is, it fits a straight line <em>y</em> = α + β <em>x</em>, | ||
* (where <em>y</em> is the response variable, <em>x</em> is the predictor variable, | ||
* α is the <em>y-intercept</em>, and β is the <em>slope</em>) | ||
* that minimizes the sum of squared residuals of the linear regression model. | ||
* It also computes associated statistics, including the coefficient of | ||
* determination <em>R</em><sup>2</sup> and the standard deviation of the | ||
* estimates for the slope and <em>y</em>-intercept. | ||
* | ||
* @author Robert Sedgewick | ||
* @author Kevin Wayne | ||
*/ | ||
public class LinearRegression { | ||
private final double intercept, slope; | ||
private final double r2; | ||
private final double svar0, svar1; | ||
|
||
/** | ||
* Performs a linear regression on the data points {@code (y[i], x[i])}. | ||
* | ||
* @param x the values of the predictor variable | ||
* @param y the corresponding values of the response variable | ||
* @throws IllegalArgumentException if the lengths of the two arrays are not equal | ||
*/ | ||
public LinearRegression(double[] x, double[] y) { | ||
if (x.length != y.length) { | ||
throw new IllegalArgumentException("array lengths are not equal"); | ||
} | ||
int n = x.length; | ||
|
||
// first pass | ||
double sumx = 0.0, sumy = 0.0, sumx2 = 0.0; | ||
for (int i = 0; i < n; i++) { | ||
sumx += x[i]; | ||
sumx2 += x[i] * x[i]; | ||
sumy += y[i]; | ||
} | ||
double xbar = sumx / n; | ||
double ybar = sumy / n; | ||
|
||
// second pass: compute summary statistics | ||
double xxbar = 0.0, yybar = 0.0, xybar = 0.0; | ||
for (int i = 0; i < n; i++) { | ||
xxbar += (x[i] - xbar) * (x[i] - xbar); | ||
yybar += (y[i] - ybar) * (y[i] - ybar); | ||
xybar += (x[i] - xbar) * (y[i] - ybar); | ||
} | ||
slope = xybar / xxbar; | ||
intercept = ybar - slope * xbar; | ||
|
||
// more statistical analysis | ||
double rss = 0.0; // residual sum of squares | ||
double ssr = 0.0; // regression sum of squares | ||
for (int i = 0; i < n; i++) { | ||
double fit = slope * x[i] + intercept; | ||
rss += (fit - y[i]) * (fit - y[i]); | ||
ssr += (fit - ybar) * (fit - ybar); | ||
} | ||
|
||
int degreesOfFreedom = n - 2; | ||
r2 = ssr / yybar; | ||
double svar = rss / degreesOfFreedom; | ||
svar1 = svar / xxbar; | ||
svar0 = svar / n + xbar * xbar * svar1; | ||
} | ||
|
||
/** | ||
* Returns the <em>y</em>-intercept α of the best of the best-fit line <em>y</em> = α + β <em>x</em>. | ||
* | ||
* @return the <em>y</em>-intercept α of the best-fit line <em>y = α + β x</em> | ||
*/ | ||
public double intercept() { | ||
return intercept; | ||
} | ||
|
||
/** | ||
* Returns the slope β of the best of the best-fit line <em>y</em> = α + β <em>x</em>. | ||
* | ||
* @return the slope β of the best-fit line <em>y</em> = α + β <em>x</em> | ||
*/ | ||
public double slope() { | ||
return slope; | ||
} | ||
|
||
/** | ||
* Returns the coefficient of determination <em>R</em><sup>2</sup>. | ||
* | ||
* @return the coefficient of determination <em>R</em><sup>2</sup>, | ||
* which is a real number between 0 and 1 | ||
*/ | ||
public double R2() { | ||
return r2; | ||
} | ||
|
||
/** | ||
* Returns the standard error of the estimate for the intercept. | ||
* | ||
* @return the standard error of the estimate for the intercept | ||
*/ | ||
public double interceptStdErr() { | ||
return Math.sqrt(svar0); | ||
} | ||
|
||
/** | ||
* Returns the standard error of the estimate for the slope. | ||
* | ||
* @return the standard error of the estimate for the slope | ||
*/ | ||
public double slopeStdErr() { | ||
return Math.sqrt(svar1); | ||
} | ||
|
||
/** | ||
* Returns the expected response {@code y} given the value of the predictor | ||
* variable {@code x}. | ||
* | ||
* @param x the value of the predictor variable | ||
* @return the expected response {@code y} given the value of the predictor | ||
* variable {@code x} | ||
*/ | ||
public double predict(double x) { | ||
return slope * x + intercept; | ||
} | ||
|
||
/** | ||
* Returns a string representation of the simple linear regression model. | ||
* | ||
* @return a string representation of the simple linear regression model, | ||
* including the best-fit line and the coefficient of determination | ||
* <em>R</em><sup>2</sup> | ||
*/ | ||
public String toString() { | ||
return String.format(Locale.ENGLISH, "%.2f n + %.2f (R^2 = %.3f)", slope(), intercept(), R2()); | ||
} | ||
|
||
} |
139 changes: 139 additions & 0 deletions
139
app/src/main/java/org/stepic/droid/adaptive/model/Card.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package org.stepic.droid.adaptive.model | ||
|
||
import io.reactivex.Observable | ||
import io.reactivex.Scheduler | ||
import io.reactivex.Single | ||
import io.reactivex.SingleObserver | ||
import io.reactivex.disposables.CompositeDisposable | ||
import io.reactivex.disposables.Disposable | ||
import org.stepic.droid.base.App | ||
import org.stepic.droid.di.qualifiers.BackgroundScheduler | ||
import org.stepic.droid.di.qualifiers.MainScheduler | ||
import org.stepic.droid.model.Attempt | ||
import org.stepic.droid.model.Lesson | ||
import org.stepic.droid.model.Step | ||
import org.stepic.droid.web.Api | ||
import javax.inject.Inject | ||
|
||
class Card( | ||
val courseId: Long, | ||
val lessonId: Long, | ||
|
||
var lesson: Lesson? = null, | ||
var step: Step? = null, | ||
var attempt: Attempt? = null | ||
) : Single<Card>() { | ||
@Inject | ||
lateinit var api: Api | ||
|
||
@Inject | ||
@field:MainScheduler | ||
lateinit var mainScheduler: Scheduler | ||
|
||
@Inject | ||
@field:BackgroundScheduler | ||
lateinit var backgroundScheduler: Scheduler | ||
|
||
init { | ||
App.componentManager() | ||
.adaptiveCourseComponent(courseId) | ||
.inject(this) | ||
} | ||
|
||
private var observer: SingleObserver<in Card>? = null | ||
|
||
private var error: Throwable? = null | ||
|
||
private var lessonDisposable: Disposable? = null | ||
private var stepSubscription: Disposable? = null | ||
private var attemptDisposable: Disposable? = null | ||
|
||
private val compositeDisposable = CompositeDisposable() | ||
|
||
var correct = false | ||
private set | ||
|
||
fun initCard() { | ||
error = null | ||
|
||
if (stepSubscription == null || stepSubscription?.isDisposed == true && step == null) { | ||
stepSubscription = api.getStepsByLessonId(lessonId) | ||
.subscribeOn(backgroundScheduler) | ||
.observeOn(mainScheduler) | ||
.subscribe({ setStep(it.steps?.firstOrNull()) }, { onError(it) }) | ||
} else { | ||
setStep(step) | ||
} | ||
|
||
if (lessonDisposable == null || lessonDisposable?.isDisposed == true && lesson == null) { | ||
lessonDisposable = api.getLessons(lessonId) | ||
.subscribeOn(backgroundScheduler) | ||
.observeOn(mainScheduler) | ||
.subscribe({ setLesson(it.lessons?.firstOrNull()) }, { onError(it) }) | ||
} | ||
} | ||
|
||
private fun setStep(newStep: Step?) = newStep?.let { | ||
this.step = newStep | ||
if (attemptDisposable == null || attemptDisposable?.isDisposed == true && attempt == null) { | ||
attemptDisposable = Observable.concat( | ||
api.getExistingAttemptsReactive(newStep.id).toObservable(), | ||
api.createNewAttemptReactive(newStep.id).toObservable() | ||
) | ||
.filter { it.attempts.isNotEmpty() } | ||
.take(1) | ||
.map { it.attempts.firstOrNull() } | ||
.subscribeOn(backgroundScheduler) | ||
.observeOn(mainScheduler) | ||
.subscribe({ setAttempt(it) }, { onError(it) }) | ||
} | ||
|
||
notifyDataChanged() | ||
} | ||
|
||
private fun setLesson(lesson: Lesson?) = lesson?.let { | ||
this.lesson = it | ||
notifyDataChanged() | ||
} | ||
|
||
private fun setAttempt(attempt: Attempt?) = attempt?.let { | ||
this.attempt = it | ||
notifyDataChanged() | ||
} | ||
|
||
private fun onError(error: Throwable?) { | ||
this.error = error | ||
notifyDataChanged() | ||
} | ||
|
||
private fun notifyDataChanged() = observer?.let { | ||
error?.let(it::onError) | ||
|
||
if (lesson != null && step != null && attempt != null) { | ||
it.onSuccess(this) | ||
} | ||
} | ||
|
||
/** | ||
* Free resources | ||
*/ | ||
fun recycle() { | ||
App.componentManager() | ||
.releaseAdaptiveCourseComponent(courseId) | ||
lessonDisposable?.dispose() | ||
stepSubscription?.dispose() | ||
attemptDisposable?.dispose() | ||
compositeDisposable.dispose() | ||
observer = null | ||
} | ||
|
||
override fun subscribeActual(observer: SingleObserver<in Card>) { | ||
this.observer = observer | ||
initCard() | ||
notifyDataChanged() | ||
} | ||
|
||
fun onCorrect() { | ||
correct = true | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
app/src/main/java/org/stepic/droid/adaptive/model/Reaction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.stepic.droid.adaptive.model | ||
|
||
enum class Reaction(val value: Int) { | ||
SOLVED(2), INTERESTING(1), MAYBE_LATER(0), NEVER_AGAIN(-1) | ||
} |
6 changes: 6 additions & 0 deletions
6
app/src/main/java/org/stepic/droid/adaptive/model/Recommendation.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.stepic.droid.adaptive.model | ||
|
||
class Recommendation( | ||
val id: Long, | ||
val lesson: Long, | ||
val reasons: List<String>?) |
6 changes: 6 additions & 0 deletions
6
app/src/main/java/org/stepic/droid/adaptive/model/RecommendationReaction.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.stepic.droid.adaptive.model | ||
|
||
|
||
class RecommendationReaction(val lesson: Long, reaction: Reaction, var user: Long = 0) { | ||
private val reaction = reaction.value | ||
} |
Oops, something went wrong.