Skip to content

Commit

Permalink
Merge pull request #434 from rl-institut/release/v0.16.0
Browse files Browse the repository at this point in the history
Release/v0.16.0
  • Loading branch information
henhuy authored Jul 15, 2020
2 parents b84e9ad + da019f1 commit c3c148b
Show file tree
Hide file tree
Showing 48 changed files with 602 additions and 325 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project tries to adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.16.0] - 2020-07-15
### Added
- answered questions of finished category can be re-seen

### Changed
- merged landscape view into portrait view
- app checks for mobile/desktop view and shows only related parts
- major redesign of answer page (question form is shown with correct/wrong answers)

### Fixed
- share links

## [0.15.0] - 2020-07-13
### Added
- slogan to finished and share link
Expand Down
2 changes: 1 addition & 1 deletion e_metrobus/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.15.0"
__version__ = "0.16.0"
__version_info__ = tuple(
[
int(num) if num.isdigit() else num
Expand Down
2 changes: 1 addition & 1 deletion e_metrobus/navigation/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self, figure, **config):
div_id_end = plotly_div.find('"', div_id_start + 9)
self.div_id = plotly_div[div_id_start + 9 : div_id_end]

self.div = f'<div id="{self.div_id}" class="plotly-graph-div" style="height:55vh; width:100vw;"></div>'
self.div = f'<div id="{self.div_id}" class="plotly-graph-div"></div>'


def get_sizes(max_value):
Expand Down
2 changes: 1 addition & 1 deletion e_metrobus/navigation/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class Vehicle:
]

DATA_SOURCES = [
'Umweltbundesamt, "Vergleich der durchschnittlicher Emissionen einzelner Verkehrsmittel im Personenverkehr in Deutschland - Bezugsjahr 2018", 01/2020',
'Umweltbundesamt, "Vergleich der durchschnittlichen Emissionen einzelner Verkehrsmittel im Personenverkehr in Deutschland - Bezugsjahr 2018", 01/2020',
'Umweltbundesamt, "Entwicklung der spezifischen Kohlendioxid-Emissionen des deutschen Strommix in den Jahren 1990 - 2019", 13/2020',
]

Expand Down
16 changes: 5 additions & 11 deletions e_metrobus/navigation/questions.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@
icon = "images/icons/i_ebus_black_line.svg"
small_icon = "images/icons/i_ebus_black_fill.svg"
[[questions]]
[[[route]]]
label = "This is a placeholder for route selection"
question = ""
answers = "", ""
correct = 0
short_answer = ""
[[[loading_time]]]
label = "Ladezeiten"
question = "Wie lange müssen die Elektro-Gelenkbusse der 200 an den Endhaltestellen laden?"
question = "Wie lange müssen die Elektro-Gelenkbusse der Linie 200 an den Endhaltestellen laden?"
answers = "Eine halbe Stunde", "Eine Stunde", "Wenige Minuten", "Wenige Sekunden"
correct = 2
short_answer = "Die Ladezeit beträgt in der Regel wenige Minuten."
Expand All @@ -24,13 +18,13 @@
short_answer = "Der Bus wird an den Endhaltestellen Hertzallee und Michelangelostraße sowie im Betriebshof mit einem Pantographen aufgeladen."
[[[batteries]]]
label = "Batterien"
question = "Wo befinden sich die Batterien der Elektro-Gelenkbusse der Linie 200?"
question = "Wo befinden sich die Batterien in den Elektro-Gelenkbussen der Linie 200?"
answers = "Im Boden", "Am Heck", "Auf dem Dach", "An den Seiten"
correct = 2
short_answer = "Die Batterien der Elektro-Gelenkbusse, die auf der Linie 200 eingesetzt werden, sich auf dem Dach angebracht."
[[[costs]]]
label = "Kosten"
question = "Wie viel mehr kostet ein Elektrobus als ein konventioneller Dieselbus?"
question = "Wie viel mehr kostet ein Elektrobus im Vergleich zu einem konventionellen Dieselbus?"
answers = "Doppelt", "Dreifach", "Zehnfach", "Genauso"
correct = 0
short_answer = "Elektrobusse kosten aktuell noch etwa doppelt so viel wie konventionelle Dieselbusse der gleichen Größenklasse."
Expand All @@ -42,8 +36,8 @@
[[questions]]
[[[energy]]]
label = "Energie"
question = "Wieviel Energie ist für die jährliche Stromversorgung der Elektrobusse auf der Linie 200 notwendig? So viel wie ausreichen würde zur Versorgung von:"
answers = "584 Haushalten", "56 Haushalten", "285 Haushalten", "1010 Haushalten"
question = "Wie viel Energie ist für die jährliche Stromversorgung der Elektrobusse auf der Linie 200 notwendig? So viel wie der Jahresstromverbrauch von:"
answers = "584 Dreipersonenhaushalten", "56 Dreipersonenhaushalten", "285 Dreipersonenhaushalten", "1010 Dreipersonenhaushalten"
correct = 0
short_answer = "Der jährliche Stromverbrauch der gesamten Buslinie 200 mit allen im Betrieb befindlichen Elektrobussen liegt bei etwa 2.044.000 kWh, was dem Energiebedarf von ca. 584 Dreipersonenhaushalten entspricht."
[[[weather]]]
Expand Down
43 changes: 36 additions & 7 deletions e_metrobus/navigation/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,22 +121,29 @@ def get_category_shares(category, session):
correct = 0
if "questions" not in session or category not in session["questions"]:
return Shares(0, 0)
for question in QUESTIONS[category].questions:
for question_name, question in QUESTIONS[category].questions.items():
total += 1
if question in session["questions"][category]:
if question_name in session["questions"][category]:
done += 1
correct += session["questions"][category][question]
correct += check_answer(
question,
session["questions"][category][question_name],
)
return Shares(done / total, correct / total)


def get_category_answers(category, session):
"""Returns list of correct/wrong/unanswered questions for category"""
if "questions" not in session or category not in session["questions"]:
return [Answer.Unanswered for question in QUESTIONS[category].questions]
return [
Answer.get(session["questions"][category].get(question))
for question in QUESTIONS[category].questions
]
answers = []
for question_name, question in QUESTIONS[category].questions.items():
answer = session["questions"][category].get(question_name)
if answer is None:
answers.append(Answer.Unanswered)
else:
answers.append(Answer.get(check_answer(question, answer)))
return answers


def get_all_answers(session, sort=True):
Expand All @@ -160,6 +167,21 @@ def get_next_question(category, session):
return None


def get_next_answer(category, current_answer=None):
if category not in QUESTIONS:
raise KeyError("Invalid category")
if current_answer is None:
return list(QUESTIONS[category].questions.keys())[0]
questions_iterator = iter(QUESTIONS[category].questions)
question = None
try:
while question != current_answer:
question = next(questions_iterator)
return next(questions_iterator)
except StopIteration:
return None


def get_question_from_name(question_name):
for category in QUESTIONS.values():
if question_name in category.questions:
Expand All @@ -172,3 +194,10 @@ def all_questions_answered(session):
if get_category_shares(category, session).done != 1.0:
return False
return True


def check_answer(question, answer):
if isinstance(question.correct, list):
return answer == question.correct
else:
return answer == question.correct
50 changes: 25 additions & 25 deletions e_metrobus/navigation/tests/test_questions.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,40 @@
from django.test import TestCase
from django.test import TestCase, tag

from e_metrobus.navigation import questions


@tag("questions")
class QuestionTestCase(TestCase):
def setUp(self):
self.session = {
"stations": (3, 4),
"questions": {
"e_metrobus": {"loading_time": True, "line_200": False},
"personal": {"advantages": True},
"politics": {"invalid": True},
"ich": {"advantages": True},
"politik": {"invalid": True},
},
}

def test_score_category_empty(self):
self.assertEqual(
questions.get_score_for_category("environment", self.session), 0
)
self.assertEqual(questions.get_score_for_category("umwelt", self.session), 0)

def test_score_category_complete(self):
self.assertEqual(
questions.get_score_for_category("e_metrobus", self.session),
questions.SCORE_CORRECT
+ questions.SCORE_WRONG
questions.get_score_for_category("e_metrobus", self.session), 1 / 4
)

def test_score_category_not_complete(self):
self.assertEqual(
questions.get_score_for_category("personal", self.session),
questions.SCORE_CORRECT + questions.SCORE_CATEGORY_COMPLETE
questions.get_score_for_category("ich", self.session),
questions.SCORE_CORRECT + questions.SCORE_CATEGORY_COMPLETE,
)

def test_score_category_error(self):
with self.assertRaises(KeyError):
questions.get_score_for_category("not_there", self.session)

def test_score_invalid_question(self):
self.assertEqual(questions.get_score_for_category("politics", self.session), 0)
self.assertEqual(questions.get_score_for_category("politik", self.session), 0)

def test_total_score(self):
self.assertEqual(
Expand All @@ -49,28 +46,31 @@ def test_total_score(self):

def test_percentage(self):
self.assertEqual(
questions.get_category_done_share("e_metrobus", self.session), 2/3
)
self.assertEqual(
questions.get_category_done_share("personal", self.session), 1
)
self.assertEqual(
questions.get_category_done_share("politics", self.session), 0
questions.get_category_shares("e_metrobus", self.session).done, 1 / 4
)
self.assertEqual(questions.get_category_shares("ich", self.session), 1)
self.assertEqual(questions.get_category_shares("politik", self.session), 0)

def test_next_question(self):
self.assertEqual(
questions.get_next_question("e_metrobus", self.session),
questions.QUESTIONS["e_metrobus"].questions["loading"],
)
self.assertIsNone(
questions.get_next_question("personal", self.session)
self.assertIsNone(questions.get_next_question("ich", self.session))
self.assertEqual(
questions.get_next_question("politik", self.session),
questions.QUESTIONS["politik"].questions["ebus_time"],
)
self.assertEqual(
questions.get_next_question("politics", self.session),
questions.QUESTIONS["politics"].questions["ebus_time"],
questions.get_next_question("umwelt", self.session),
questions.QUESTIONS["umwelt"].questions["co2_reduction"],
)

def test_next_answer(self):
self.assertEqual(questions.get_next_answer("e_metrobus"), "loading_time")
self.assertEqual(
questions.get_next_answer("e_metrobus", "loading_time"), "loading"
)
self.assertEqual(
questions.get_next_question("environment", self.session),
questions.QUESTIONS["environment"].questions["co2_reduction"],
questions.get_next_answer("e_metrobus", "costs"), None
)
1 change: 1 addition & 0 deletions e_metrobus/navigation/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
path("quiz/", view=views.DashboardView.as_view(), name="dashboard"),
path("quiz/<str:category>/", view=views.QuestionView.as_view(), name="question"),
path("antwort/", view=views.AnswerView.as_view(), name="answer"),
path("antwort/<str:category>/", view=views.AnswerView.as_view(), name="answer"),
path("tour/", view=views.TourView.as_view(), name="tour"),
path(
"abgeschlossen/<str:category>/",
Expand Down
4 changes: 2 additions & 2 deletions e_metrobus/navigation/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def share_url(request):
def share_text(request):
if "non_bus_user" in request.session:
text = _(
"Ich bin gerade in einem E-Bus auf der Linie 200 gefahren. Schau mal hier:"
"Ich bin gerade in einem E-Bus auf der Linie 200 gefahren. Schau mal hier"
)
else:
current_stations = [
Expand All @@ -46,7 +46,7 @@ def share_text(request):
route_data = stations.STATIONS.get_route_data(*current_stations)
co2 = route_data["bus"].co2 - route_data["e-bus"].co2
text = _(
"Ich bin gerade in einem E-Bus auf der Linie 200 gefahren und habe der Welt dabei %(co2)s g CO2-Emissionen erspart. Schau mal hier:"
"Ich bin gerade in einem E-Bus auf der Linie 200 gefahren und habe der Welt dabei %(co2)s g CO2-Emissionen erspart. Schau mal hier"
) % {"co2": round(co2, 2)}
return text

Expand Down
48 changes: 30 additions & 18 deletions e_metrobus/navigation/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,6 @@ def get(self, request, *args, **kwargs):
# Set first question as "answered":
if "questions" not in request.session:
request.session["questions"] = {}
if "e_metrobus" not in request.session["questions"]:
request.session["questions"]["e_metrobus"] = {}
request.session["questions"]["e_metrobus"]["route"] = True
request.session["last_answered_question"] = "route"
request.session.save()
return super(DisplayRouteView, self).get(request, *args, **kwargs)

Expand Down Expand Up @@ -246,8 +242,20 @@ def get_context_data(self, **kwargs):
return context

def get(self, request, *args, **kwargs):
if kwargs["category"] in request.session["questions"] and request.session[
"questions"
][kwargs["category"]].get("finished", False):
next_answer = questions.get_next_answer(kwargs["category"])
if next_answer is None:
return redirect("navigation:dashboard")
else:
request.session["last_answered_question"] = next_answer
return redirect("navigation:answer", category=kwargs["category"])

next_question = questions.get_next_question(kwargs["category"], request.session)
if next_question is None:
request.session["questions"][kwargs["category"]]["finished"] = True
request.session.save()
return redirect("navigation:category_finished", category=kwargs["category"])

context = self.get_context_data(**kwargs, question=next_question)
Expand All @@ -256,11 +264,9 @@ def get(self, request, *args, **kwargs):
def post(self, request, **kwargs):
question = questions.get_question_from_name(request.POST["question"])
if isinstance(question.correct, list):
answer = request.POST.getlist("answer") == [
question.answers[i] for i in map(int, question.correct)
]
answer = request.POST.getlist("answer")
else:
answer = request.POST["answer"] == question.answers[int(question.correct)]
answer = request.POST["answer"]

if "questions" not in request.session:
request.session["questions"] = {}
Expand Down Expand Up @@ -291,15 +297,25 @@ def get_context_data(self, question, **kwargs):
self.title_icon = questions.QUESTIONS[question.category].small_icon
context = super(AnswerView, self).get_context_data(**kwargs)
context["question"] = question
context["answer"] = self.request.session["questions"][question.category][
question.name
]
answer = self.request.session["questions"][question.category][question.name]
context["given_answer"] = list(map(int, answer))
context["correct_answer"] = list(map(int, question.correct))
context["flashes"] = questions.get_category_answers(
question.category, self.request.session
)
if "category" in kwargs and self.request.session["questions"][
kwargs["category"]
].get("finished", False):
self.request.session["last_answered_question"] = questions.get_next_answer(
kwargs["category"], self.request.session["last_answered_question"]
)
context["category_finished"] = True
return context

def get(self, request, **kwargs):
question_name = request.session.get("last_answered_question")
if question_name is None:
raise ValueError("No question answered yet!")
return redirect("navigation:dashboard")
question = questions.get_question_from_name(question_name)
context = self.get_context_data(question=question, **kwargs)
return self.render_to_response(context)
Expand Down Expand Up @@ -329,10 +345,6 @@ def get_context_data(self, **kwargs):
context["footer"] = widgets.FooterWidget(links=self.footer_links)
answers = questions.get_all_answers(self.request.session)
context["answers"] = answers
correct = len(
[answer for answer in answers if answer == questions.Answer.Correct]
)
total = len(answers)
percent = questions.get_total_score(self.request.session)
context["score"] = percent
context["slogan"] = utils.get_slogan(percent)
Expand All @@ -341,8 +353,8 @@ def get_context_data(self, **kwargs):
return context

def get(self, request, *args, **kwargs):
if not questions.all_questions_answered(request.session):
raise Http404("Not all questions answered. Please go back to quiz.")
# if not questions.all_questions_answered(request.session):
# raise Http404("Not all questions answered. Please go back to quiz.")
if "hashed_score" not in request.session:
score = models.Score.save_score(request.session)
request.session["hashed_score"] = score.hash
Expand Down
Loading

0 comments on commit c3c148b

Please sign in to comment.