diff --git a/ansible/roles/web_AI-5/files/000-default.conf b/ansible/roles/web_AI-5/files/000-default.conf index bf85cc0..b9d347e 100644 --- a/ansible/roles/web_AI-5/files/000-default.conf +++ b/ansible/roles/web_AI-5/files/000-default.conf @@ -13,7 +13,7 @@ WSGIPythonPath /vagrant/django/didgeridoo/ ServerAdmin webmaster@localhost - Alias /media/ /vagrant/django/didgeridoo/media/ + Alias /media/ /srv/media/ Alias /static/ /vagrant/django/didgeridoo/static/ @@ -23,7 +23,7 @@ WSGIPythonPath /vagrant/django/didgeridoo/ - + Require all granted diff --git a/django/didgeridoo/currencies/exchange_rates.py b/django/didgeridoo/currencies/exchange_rates.py index 05c279f..7e7cfd9 100644 --- a/django/didgeridoo/currencies/exchange_rates.py +++ b/django/didgeridoo/currencies/exchange_rates.py @@ -11,134 +11,99 @@ Key:Value pairs of new currencys. """ -def get_exchange_rate(): - # zweitweise kann die resource nicht geladen werden. - # https://stackoverflow.com/a/43523497/4061870 - # During weekends there are no updates. - # To develop i need a testresource. - # In that case i comment the Online Resource block and uncomment the - # development Block... - - # ~~~~~~~~~~~~~~~~~~~~~ - # Online Resource block: - # ~~~~~~~~~~~~~~~~~~~~~ - today = datetime.now().strftime("%Y-%m-%d") - SNB_URL = 'https://www.snb.ch/selector/de/mmr/exfeed/rss' +def get_rss(url): urlsocket = '' try: - urlsocket = urllib.request.urlopen(SNB_URL) + urlsocket = urllib.request.urlopen(url) + return(urlsocket) except urllib.error.URLError as e: print('err: urllib.request.urlopen: ', e.reason) + + +def parse_rss(urlsocket): if urlsocket: root = ET.parse(urlsocket) - root = ET.ElementTree(root) - # ~~~~~~~~~~~~~~~~~~~~~ - # development block: - # ~~~~~~~~~~~~~~~~~~~~~ - # today = "2018-01-08" - # try: - # root = ET.ElementTree(file='rss') - # except Exception as e: - # print('exchange_rates.py_urlsocket failed %s ( - # %s) on date: %s for %s' - # % (e, type(e), root)) - # ~~~~~~~~~~~~~~~~~~~~~ + rss_tree = ET.ElementTree(root) + return(rss_tree) - # Namespaces - ns = {'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'none': 'http://purl.org/rss/1.0/', - 'dc': 'http://purl.org/dc/elements/1.1/', - 'dcterms': 'http://purl.org/dc/terms/', - 'cb': 'http://www.cbwiki.net/wiki/index.php/Specification_1.2/' - } - # Pathvariables to XML Namespaces - rate_path = 'cb:statistics/cb:exchangeRate/' - observation_path = 'cb:statistics/cb:exchangeRate/cb:observation/' - exchange_rates = {} - for item in root.findall('none:item', ns): - # for eatch item n the list we grab the release date - # to evaluate if its fresh data or old: - # THE CURRENCY DATE: - datetime_str = item.find('dc:date', ns).text - # convert string to date object: - # https://stackoverflow.com/a/12282040/4061870 - # seams like snb striked the microsecond somewhere - # between Nov. and Dez. 2017 so maybe first check - # time type is with milliseconds: +def get_exchange_rate(rss_tree, ns): + # Pathvariables to XML Namespaces with + rate_path = 'cb:statistics/cb:exchangeRate/' + observation_path = 'cb:statistics/cb:exchangeRate/cb:observation/' + exchange_rates = [] + + for item in rss_tree.findall('none:item', ns): + datetime_str = item.find('dc:date', ns).text + try: + date = datetime.strptime(''.join( + datetime_str.rsplit(':', 1)), + "%Y-%m-%dT%H:%M:%S%z").strftime( + "%Y-%m-%d") + except Exception as e: + print('%s (%s)' % (e, type(e))) try: date = datetime.strptime(''.join( datetime_str.rsplit(':', 1)), - "%Y-%m-%dT%H:%M:%S%z").strftime( + "%Y-%m-%dT%H:%M:%S.%f%z").strftime( "%Y-%m-%d") + continue except Exception as e: print('%s (%s)' % (e, type(e))) - try: - date = datetime.strptime(''.join( - datetime_str.rsplit(':', 1)), - "%Y-%m-%dT%H:%M:%S.%f%z").strftime( - "%Y-%m-%d") - continue - except Exception as e: - print('%s (%s)' % (e, type(e))) - continue - # Print dates for development: - # print("date:", date, "today:", today) - # only the values of today are used so check for date in XML: - if date == today: - # now search for the currency exchange rate: - target_currency = item.find(rate_path + - 'cb:targetCurrency', ns).text - value = float(item.find(observation_path + - 'cb:value', ns).text) - value = float(value) # convert to float - foreign_value = value # copy to new value to have both. - - if item.find(observation_path + 'cb:unit_mult', ns) is None: - # because it's dangerous to check for present, - # i check for none here and have to set the target - # to 1. as im multiplying it later. - unit_mult = float("1.0") - else: - # shift left by 2 digits with "/" - # https://stackoverflow.com/questions/8362792/ - # because some currencys differ widly from CHF - unit_mult = item.find(observation_path + - 'cb:unit_mult', ns).text - # unit_mult defaults to '0' so we check for 8 decimal - # values (2..-6) they represent the fracton value to - # calculate the correct decimalpoint. - if unit_mult == '2': # thinking of Bitcoins - unit_mult = '0.01' - if unit_mult == '1': - unit_mult = '0.10' - if unit_mult == '-1': - unit_mult = '10' - if unit_mult == '-2': # Japan Yen - unit_mult = '100' - if unit_mult == '-3': - unit_mult = '1000' - if unit_mult == '-4': - unit_mult = '10000' - if unit_mult == '-5': - unit_mult = '100000' - if unit_mult == '-6': # indian rupies - unit_mult = '1000000' - unit_mult = float(unit_mult) # convert to float - # calculate the Currency to CHF: - foreign_value = 1 / value - foreign_value *= unit_mult - value = value / unit_mult - # truncate it to decimal values provided by the xml: - foreign_value_round = round(foreign_value, 5) - # Print nice setup of all calculated currencys for development: - # print("date:", date, " 1 ", target_currency, " costs: ", - # CHFvalue, "CHF and 1 ", base_currency, " costs: ", - # FOREIGNvalue_round, target_currency) - exchange_rates.update( - {target_currency: foreign_value_round}) - # Print the Dictionary: - # print(exchange_rates) - else: continue - return(exchange_rates, today) + # now search for the currency exchange rate: + target_currency = item.find(rate_path + + 'cb:targetCurrency', ns).text + value = float(item.find(observation_path + + 'cb:value', ns).text) + value = float(value) # convert to float + foreign_value = value # copy to new value to have both. + + if item.find(observation_path + 'cb:unit_mult', ns) is None: + # because it's dangerous to check for present, + # i check for none here and have to set the target + # to 1. as im multiplying it later. + unit_mult = float("1.0") + else: + # shift left by 2 digits with "/" + # https://stackoverflow.com/questions/8362792/ + # because some currencys differ widly from CHF + unit_mult = item.find(observation_path + + 'cb:unit_mult', ns).text + # unit_mult defaults to '0' so we check for 8 decimal + # values (2..-6) they represent the fracton value to + # calculate the correct decimalpoint. + if unit_mult == '2': # thinking of Bitcoins + unit_mult = '0.01' + if unit_mult == '1': + unit_mult = '0.10' + if unit_mult == '-1': + unit_mult = '10' + if unit_mult == '-2': # Japan Yen + unit_mult = '100' + if unit_mult == '-3': + unit_mult = '1000' + if unit_mult == '-4': + unit_mult = '10000' + if unit_mult == '-5': + unit_mult = '100000' + if unit_mult == '-6': # indian rupies + unit_mult = '1000000' + unit_mult = float(unit_mult) # convert to float + # calculate the Currency to CHF: + foreign_value = 1 / value + foreign_value *= unit_mult + value = value / unit_mult + # truncate it to decimal values provided by the xml: + foreign_value_round = round(foreign_value, 5) + # Print nice setup of all calculated currencys for development: + # print("date:", date, " 1 ", target_currency, " costs: ", + # CHFvalue, "CHF and 1 ", base_currency, " costs: ", + # FOREIGNvalue_round, target_currency) + data = [{'date': date, + 'currency': target_currency, + 'exchangerate': foreign_value_round}] + exchange_rates.append(data) + # Print the Dictionary: + print(exchange_rates) + return(exchange_rates) diff --git a/django/didgeridoo/currencies/migrations/__init__.py b/django/didgeridoo/currencies/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/django/didgeridoo/currencies/models.py b/django/didgeridoo/currencies/models.py index 73a09ba..a5014f3 100644 --- a/django/didgeridoo/currencies/models.py +++ b/django/didgeridoo/currencies/models.py @@ -20,7 +20,8 @@ class ExchangeRate_date(models.Model): class ExchangeRate(models.Model): name = models.ForeignKey(ExchangeRate_name) date = models.ForeignKey(ExchangeRate_date) - exchange_rate_to_chf = models.DecimalField(max_digits=12, decimal_places=5) + exchange_rate_to_chf = models.DecimalField(max_digits=12, + decimal_places=5) def __str__(self): return str(self.name) diff --git a/django/didgeridoo/currencies/templates/currencies/index.html b/django/didgeridoo/currencies/templates/currencies/index.html index d29a52d..2f28ccb 100644 --- a/django/didgeridoo/currencies/templates/currencies/index.html +++ b/django/didgeridoo/currencies/templates/currencies/index.html @@ -1,14 +1,9 @@ - - - - - -
-

Currencies in CHF

-

{{ message }}

-

Frühere Daten:

-
-

US Dollars:

+{% extends "webshop/base.html" %} +{% block section_title %}Currencies in CHF{% endblock %} + {% block content %} +

{{ message }}

+

Frühere Daten:

+

US Dollars:

{% if currency_USD_list %} @@ -28,7 +23,7 @@

{% endif %}
-

EURO:

+

EURO:

{% if currency_EUR_list %}
@@ -48,7 +43,7 @@

{% endif %}
-

Japanese Yenn:

+

Japanese Yenn:

{% if currency_JPY_list %}
@@ -69,7 +64,7 @@

{% endif %}
-

Great Britain Pounds:

+

Great Britain Pounds:

{% if currency_GBP_list %}
@@ -90,5 +85,4 @@

{% endif %} - - +{% endblock %} diff --git a/django/didgeridoo/currencies/templatetags/customfilters.py b/django/didgeridoo/currencies/templatetags/customfilters.py new file mode 100644 index 0000000..a30e2ab --- /dev/null +++ b/django/didgeridoo/currencies/templatetags/customfilters.py @@ -0,0 +1,12 @@ +from django import template + + +register = template.Library() + + +@register.filter() +def boldcoffee(value): + # currency_of_customer = request.session['currency'] + return '%s !!gefiltert!!' % value + + # excample filter: {{ article.price_in_chf|boldcoffee }} diff --git a/django/didgeridoo/currencies/urls.py b/django/didgeridoo/currencies/urls.py index 2804c74..aa24648 100644 --- a/django/didgeridoo/currencies/urls.py +++ b/django/didgeridoo/currencies/urls.py @@ -1,7 +1,9 @@ from django.conf.urls import url -from currencies.views import currencies - +from currencies.views import currencies, currency_update urlpatterns = [ url(r'^currencies/$', currencies), + url(r'^ajax/currency_update/$', + currency_update, + name='currency_update'), ] diff --git a/django/didgeridoo/currencies/views.py b/django/didgeridoo/currencies/views.py index d19f3e8..33cabe1 100644 --- a/django/didgeridoo/currencies/views.py +++ b/django/didgeridoo/currencies/views.py @@ -1,82 +1,133 @@ from django.shortcuts import render -import datetime +from datetime import datetime +from django.views.generic.edit import UpdateView from currencies.models import (ExchangeRate, ExchangeRate_date, ExchangeRate_name) from currencies import exchange_rates +from django.http import JsonResponse + + +def currency_update(request): + # https://simpleisbetterthancomplex.com/tutorial/2016/08/29/how-to-work-with-ajax-request-with-django.html + if request.GET.get('currency_update', None) == 'CHF': + data = {} + else: + currency = request.GET.get('currency_update', None) + data = ExchangeRate.objects.filter( + name__name=currency).values( + 'exchange_rate_to_chf').latest( + 'date__date') + print('currency:', currency, 'data: ', data) + return JsonResponse(data) def currencies(request): - # this function fetches the data from exchange_rates.py - # evaluates if the values are already stored and - # prepares the view all dynamicaly. - # It can grow in terms of more Currencies over time automaticaly. - today = '' - raw_data = [] - try: - raw_data, today = exchange_rates.get_exchange_rate() - except Exception as e: - print('views raw_data: ', raw_data, 'error:', e) # assert False - message_no = "Already querried today: " - message_yes = " Updated successfully: " - if raw_data: - print(raw_data) - for currency, rate in raw_data.items(): - if ExchangeRate.objects.filter( - date__date=today, - name__name=currency): - message_no += currency + ", " - # A: https://stackoverflow.com/a/27802801/4061870 - else: - if ExchangeRate_date.objects.filter(date=today)[:1]: - try: - # lustigerweise gibt .values() den value und die id - # zurück. Ohne .values() gibts nur den "value" - date_dict = ExchangeRate_date.objects.filter( - date=today).values() - except Exception as e: - print('exdate_exists %s (%s) on %s' - % (e, type(e), today)) - else: - try: - exdate = ExchangeRate_date.objects.create( - date=today) - exdate.save() - except Exception as e: - print('exdate_not_exists %s (%s) for %s' - % (e, type(e), today)) - if ExchangeRate_name.objects.filter( - name=currency)[:1]: - try: - name_dict = ExchangeRate_name.objects.filter( - name=currency).values() - except Exception as e: - print('exname_exists %s (%s) on %s' - % (e, type(e), currency)) - else: - try: - exname = ExchangeRate_name.objects.create( - name=currency) - exname.save() - except Exception as e: - print('exname_not_exists %s (%s) on %s' - % (e, type(e), currency)) - try: - exrate = ExchangeRate.objects.create( - # name_id=name_id, - name_id=ExchangeRate_name.objects.get( - name=currency).id, - # date_id=date_id, - date_id=ExchangeRate_date.objects.get( - date=today).id, - exchange_rate_to_chf=rate, - ) - exrate.save() - message_yes += currency + ", " - except Exception as e: - print('exrate_create %s (%s) on %s for %s' - % (e, type(e), currency, today)) + """this function fetches the data from swiss national bank + evaluates if the values are already stored and + prepares a view all dynamicaly. + It can grow in terms of more Currencies over time automaticaly.""" + + # Namespaces + ns = {'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'none': 'http://purl.org/rss/1.0/', + 'dc': 'http://purl.org/dc/elements/1.1/', + 'dcterms': 'http://purl.org/dc/terms/', + 'cb': 'http://www.cbwiki.net/wiki/index.php/Specification_1.2/' + } + SNB_URL = 'https://www.snb.ch/selector/de/mmr/exfeed/rss' + try: + urlsocket = exchange_rates.get_rss(SNB_URL) + except Exception as e: + print('currencies/views get_rss(): ', urlsocket, 'error:', e) + try: + rss_tree = exchange_rates.parse_rss(urlsocket) + except Exception as e: + print('currencies/views parse_rss(): ', rss_tree, 'error:', e) + try: + raw_data = exchange_rates.get_exchange_rate(rss_tree, ns) + except Exception as e: + print('currencies/views get_exchange_rate(): ', raw_data, 'error:', e) + + today = datetime.now().strftime("%Y-%m-%d") + + message_no = "Already querried: " + length_of_message_no = len(message_no) + message_yes = " Updated successfully: " + length_of_message_yes = len(message_yes) + # raw_data can be empty. In this case skip: + if raw_data: + for one_obj_of_list in raw_data: + for exchange_rate_of_one_day in one_obj_of_list: + date = exchange_rate_of_one_day['date'] + currency = exchange_rate_of_one_day['currency'] + exchangerate = exchange_rate_of_one_day['exchangerate'] + # check for already existing exrates per day and add + # to message that its already been saved. + if ExchangeRate.objects.filter( + date__date=date, + name__name=currency): + message_no += currency + ' on ' + date + ", " + else: + if ExchangeRate_date.objects.filter(date=date)[:1]: + # if data and currency is not yet present, save it. + try: + # A: https://stackoverflow.com/a/27802801/4061870 + # lustigerweise gibt .values() den value und die id + # zurück. Ohne .values() gibts nur den "value" + date_dict = ExchangeRate_date.objects.filter( + date=date).values() + except Exception as e: + print('currencies/views/exdate_exists \ + %s (%s) on %s' + % (e, type(e), today)) + else: + try: + exdate = ExchangeRate_date.objects.create( + date=date) + exdate.save() + except Exception as e: + print('currencies/views/exdate_not_exists \ + %s (%s) for %s' + % (e, type(e), date)) + if ExchangeRate_name.objects.filter( + name=currency)[:1]: + # if data and currency is not yet present, save it. + try: + name_dict = ExchangeRate_name.objects.filter( + name=currency).values() + except Exception as e: + print('currencies/views/exname_exists \ + %s (%s) on %s' + % (e, type(e), currency)) + else: + try: + exname = ExchangeRate_name.objects.create( + name=currency) + exname.save() + except Exception as e: + print('currencies/views/exname_not_exists \ + %s (%s) on %s' + % (e, type(e), currency)) + try: + # save item to where id's match. + exrate = ExchangeRate.objects.create( + # name_id=name_id, + name_id=ExchangeRate_name.objects.get( + name=currency).id, + # date_id=date_id, + date_id=ExchangeRate_date.objects.get( + date=date).id, + exchange_rate_to_chf=exchangerate, + ) + exrate.save() + message_yes += currency + ' on ' + date + ", " + + except Exception as e: + print('currencies/views/exrate_create \ + %s (%s) on %s for %s' + % (e, type(e), currency, date)) # prepare messages: # python can not swap a char insinde a sting so i have @@ -87,12 +138,13 @@ def currencies(request): message_yes = message_yes[::-1] # invert the string message_yes = message_yes.replace(",", "!", 1) # replace f. , with ! message_yes = message_yes[::-1] # invert the string back - - if len(message_no) > 24 and len(message_yes) > 23: + # here we evaluate what kind of message is valid: + if len(message_no) > length_of_message_no\ + and len(message_yes) > length_of_message_yes: message = message_no + message_yes elif len(message_no) > 24: message = message_no - elif len(message_yes) > 23: + elif len(message_yes) > 18: message = message_yes elif datetime.datetime.today().isoweekday() == 6: message = """Die Abfrage wurde ohne ergebniss beendet. @@ -106,61 +158,18 @@ def currencies(request): """ else: message = """Die Abfrage wurde ohne ergebniss beendet. - Kann es sein dass die SNB aufgrund eines Feiertages - geschlossen ist? """ + # know we can query our data for presentaton: currency_list = ExchangeRate.objects.all() - currency_USD_list = ExchangeRate.objects.filter(name__name='USD') - currency_EUR_list = ExchangeRate.objects.filter(name__name='EUR') - currency_JPY_list = ExchangeRate.objects.filter(name__name='JPY') - currency_GBP_list = ExchangeRate.objects.filter(name__name='GBP') - # ------------------------------------------------------------------- - # ------------------------------------------------------------------- - # I leave this part in the document as history. - # Problem is that i get the expected List with dictionaries like: - # view_currencies_list[ - # {'date': '2017-12-29, 'USD':'1.00', 'EUR':'1.00', 'GBP':'1.00', 'JPY':'1.00'}, - # {'date': '2017-12-30, 'USD':'1.00', 'EUR':'1.00', 'GBP':'1.00', 'JPY':'1.00'}, - # ] - # but the dict of 'date:' does not seam to deliver the same values as - # the dict's of key name:'USD' im not able to fix this in moment. - # nor am i able to generate a HTML table with date | USD | EUR | ... - # ------------------------------------------------------------------- - # ------------------------------------------------------------------- - # prepare data to be displayed in a html table: - # https://stackoverflow.com/questions/8749158/removing-duplicates-from-dictionary#8749473 - # A: https://stackoverflow.com/questions/37205793/django-values-list-vs-values#37205928 - # B: https://stackoverflow.com/questions/6521892/how-to-access-a-dictionary-key-value-present-inside-a-list - # # search for currencies in a date and apend them to the list - # view_currency_list = [] - # view_currencies_list = ExchangeRate_name.objects.all() - # view_dates_list = ExchangeRate_date.objects.all() - # count_date = 0 - # count_currencies = 0 - # for view_date in view_dates_list: - # count_date += 1 - # view_currency_dict = {view_date} - # # view_currency_dict.update({}) - # for view_currency in view_currencies_list: - # count_currencies += 1 - # try: - # x = ExchangeRate.objects.filter(date__date=str( - # view_date), - # name__name=str( - # view_currency - # )).values() # A - # view_exchange_rate_to_chf = x[0]['exchange_rate_to_chf'] - # except Exception as e: - # print('prepare_view %s (%s) for %s on %s is %s' - # % (e, type(e), view_currency, view_date, - # view_exchange_rate_to_chf)) - # view_exchange_rate_to_chf = " " - # - # view_currency_dict.update({view_currency: - # view_exchange_rate_to_chf}) # B - # - # view_currency_list.append(view_currency_dict) - # assert False + currency_USD_list = ExchangeRate.objects.filter( + name__name='USD').order_by('date__date') + currency_EUR_list = ExchangeRate.objects.filter( + name__name='EUR').order_by('date__date') + currency_JPY_list = ExchangeRate.objects.filter( + name__name='JPY').order_by('date__date') + currency_GBP_list = ExchangeRate.objects.filter( + name__name='GBP').order_by('date__date') + # and publish it on template: return render(request, 'currencies/index.html', {'currency_list': currency_list, diff --git a/django/didgeridoo/didgeridoo/settings.py b/django/didgeridoo/didgeridoo/settings.py index ec774ba..ec5f881 100644 --- a/django/didgeridoo/didgeridoo/settings.py +++ b/django/didgeridoo/didgeridoo/settings.py @@ -44,7 +44,6 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'currencies', - 'bootstrap3', ] MIDDLEWARE = [ @@ -132,9 +131,34 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ +# seams like it changed with 1.13+ (14.1.18|IH) +# https://stackoverflow.com/a/14800489/4061870 +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" STATIC_URL = '/static/' -STATIC_ROOT = '/vagrant/django/didgeridoo/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + '/vagrant/django/didgeridoo/static', +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + # 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) MEDIA_URL = '/media/' MEDIA_ROOT = '/srv/media/' diff --git a/django/didgeridoo/static/js/app.js b/django/didgeridoo/static/js/app.js new file mode 100644 index 0000000..fefddab --- /dev/null +++ b/django/didgeridoo/static/js/app.js @@ -0,0 +1,30 @@ + $("#id_currency_update").change(function () { + var currency_update = $(this).val(); + $("#id_currency_update").val(currency_update); + $.ajax({ + url: '/ajax/currency_update/', + data: { + 'currency_update': currency_update + }, + dataType: 'json', + success: function (data) { + var foo = jQuery.parseJSON(data); + alert("es pop auf! --dies kommt von: static/js/app.js--." + foo.currency_update); + + } + }); + }); + + //document.getElementById('id_currency_update').getElementsByTagName('currency_update') + //$("#id_currency_update").val('USD').selected = 'selected'; + + //https://stackoverflow.com/a/30489067/4061870 + // var obj = document.getElementById("id_currency_update"); + // for(i=0; i - + {% block nav %}{% include "webshop/nav.html" %}{% endblock %}
-
+
{% block sidebar %} {% if category_list %}
    @@ -67,13 +49,15 @@ {% endif %} {% endblock %}
-
+
+

{% block section_title %}{% endblock %}

-
-
- {% block content %}{% endblock %} +
+
+ {% block content %}{% endblock %} +
@@ -82,12 +66,37 @@
{% block footer %} - This is a case study project of Ivan Hörler and Andreas Zweili.
- It is a school project/excercise and has no commercial intent. + {% endblock %}
+ + + diff --git a/django/didgeridoo/webshop/templates/webshop/index.html b/django/didgeridoo/webshop/templates/webshop/index.html index 7eff913..4d13d5b 100644 --- a/django/didgeridoo/webshop/templates/webshop/index.html +++ b/django/didgeridoo/webshop/templates/webshop/index.html @@ -1,5 +1,7 @@ {% extends "webshop/base.html" %} +{% load customfilters %} {% block section_title %}Articles{% endblock %} + {% block content %} {% if articles_list %}
diff --git a/django/didgeridoo/webshop/templates/webshop/nav.html b/django/didgeridoo/webshop/templates/webshop/nav.html new file mode 100644 index 0000000..169d043 --- /dev/null +++ b/django/didgeridoo/webshop/templates/webshop/nav.html @@ -0,0 +1,42 @@ +{% block nav %} + +{% endblock %} diff --git a/django/didgeridoo/webshop/urls.py b/django/didgeridoo/webshop/urls.py index dc79c1d..5a4685e 100644 --- a/django/didgeridoo/webshop/urls.py +++ b/django/didgeridoo/webshop/urls.py @@ -11,6 +11,10 @@ urlpatterns = [ views.articles_in_category, name='category'), url('^', include('django.contrib.auth.urls')), - url(r'^profile/$', views.profile, name='profile'), - url(r'^registration/$', views.registration, name='registration'), + url(r'^profile/$', + views.profile, + name='profile'), + url(r'^registration/$', + views.registration, + name='registration'), ] diff --git a/django/didgeridoo/webshop/views.py b/django/didgeridoo/webshop/views.py index 1a4a4db..040f522 100644 --- a/django/didgeridoo/webshop/views.py +++ b/django/didgeridoo/webshop/views.py @@ -11,6 +11,7 @@ from webshop.models import (Article, Picture) from webshop.forms import RegistrationForm + # Create your views here. diff --git a/docs/andreas.bib b/docs/andreas.bib index b0a3cdc..34315f1 100644 --- a/docs/andreas.bib +++ b/docs/andreas.bib @@ -1,4 +1,4 @@ -@misc{django_extensions, +@misc{djangoextensions, month = {{01}}, note = {{\url{https://github.com/django-extensions/django-extensions}}}, Urldate = {{2018-01-05}}, @@ -16,3 +16,93 @@ year = {2016}, } +@misc{removeadd, + month = {{01}}, + note = {{\url{https://stackoverflow.com/a/21454467/7723859}}}, + Urldate = {{2018-01-15}}, + author = {Avinash Garg}, + title = {{How to remove Add button in Django admin, for specific Model?}}, + year = {2014}, +} + +@misc{removedelete, + month = {{08}}, + note = {{\url{https://stackoverflow.com/a/7031093/7723859}}}, + Urldate = {{2018-01-15}}, + author = {Jonathan R.}, + title = {{In Django Admin how do I disable the Delete link}}, + year = {2011}, +} + +@misc{readonly, + month = {{09}}, + note = {{\url{https://stackoverflow.com/a/46124159/7723859}}}, + Urldate = {{2018-01-15}}, + author = {gdlmx}, + title = {{Display a model field as readonly in Django admin}}, + year = {2017}, +} + +@misc{timezone, + month = {{07}}, + note = {{\url{https://stackoverflow.com/a/38239673}}}, + Urldate = {{2018-01-15}}, + author = {Antoine Pinsard}, + title = {{Django: timezone.now vs timezone.now()}}, + year = {2016}, +} + +@misc{usermodel, + month = {{07}}, + note = {{\url{https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html}}}, + Urldate = {{2018-01-15}}, + author = {Vitor Freitas}, + title = {{How to Extend Django User Model}}, + year = {2016}, +} + +@misc{djangogirls, + month = {{10}}, + note = {{\url{https://tutorial.djangogirls.org/en/}}}, + Urldate = {{2018-01-15}}, + author = {DjangoGirls}, + title = {{Django Girls Tutorial}}, + year = {2017}, +} + +@misc{images, + month = {{08}}, + note = {{\url{https://stackoverflow.com/a/1235542}}}, + Urldate = {{2018-01-15}}, + author = {steve}, + title = {{How do I include image files in Django templates?}}, + year = {2009}, +} + +@misc{djangodoc, + month = {{01}}, + note = {{\url{https://docs.djangoproject.com/en/1.11/}}}, + Urldate = {{2018-01-15}}, + author = {Django Foundation}, + title = {{Django documentation}}, + year = {2018}, +} + +@misc{upload, + month = {{12}}, + note = {{\url{https://stackoverflow.com/a/8542030}}}, + Urldate = {{2018-01-15}}, + author = {Akseli Pal{\'{e}}n}, + title = {{Need a minimal Django file upload example [closed]}}, + year = {2011}, +} + +@misc{tree, + month = {{09}}, + note = {{\url{https://stackoverflow.com/a/39729832}}}, + Urldate = {{2018-01-15}}, + author = {ht\_}, + title = {{Tree view in django template}}, + year = {2016}, +} + diff --git a/docs/doku.org b/docs/doku.org index e4ced80..7150df6 100644 --- a/docs/doku.org +++ b/docs/doku.org @@ -5,6 +5,7 @@ #+LATEX_CLASS_OPTIONS: [a4paper,11pt] #+LaTeX_HEADER: \input{style} #+OPTIONS: H:5 todo:t +#+LANGUAGE: de #+STARTUP: align @@ -390,7 +391,7 @@ Abbildung: ([[fig:umweltgrafik]]) grafisch dargestellt. *** NEXT Risikobewertung #+CAPTION: Risikobewertung Wahrscheinlichkeit -#+ATTR_LATEX: :align l|l +#+ATTR_LATEX: :align l|l :placement [H] #+NAME: tab:wahrscheinlichkeit | *Bewertung* | *Beschreibung: Warscheinlichkeit (W)* | |-------------+---------------------------------------| @@ -399,7 +400,7 @@ Abbildung: ([[fig:umweltgrafik]]) grafisch dargestellt. | 3 = hoch | Hohe warscheinlichkeit > 50% | #+CAPTION: Risikobewertung Auswirkung -#+ATTR_LATEX: :align l|l +#+ATTR_LATEX: :align l|l :placement [H] #+NAME: tab:auswirkung | *Bewertung* | *Beschreibung: Auswirkung (A)* | |-------------+-------------------------------------------------| @@ -408,7 +409,7 @@ Abbildung: ([[fig:umweltgrafik]]) grafisch dargestellt. | 3 = hoch | Projekt erfüllt nicht alle Anforderungen | #+CAPTION: Grafische Darstellung der Risikoanalyse -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+NAME: fig:risk [[file:diagrammes/risk_analysis.eps]] @@ -475,7 +476,7 @@ Als Interessent möchte ich... - die Preise in einer anderen Währung anzeigen können um die Preise in einer mir bekannten Währung vergleichen zu können. -*** TODO Use Cases +*** Use Cases Ein Use Case sammelt alle möglichen Szenarien, die eintreten können, wenn ein Akteur versucht, mit Hilfe des betrachteten Systems ein @@ -506,158 +507,617 @@ Webshops beschränkt. #+LATEX:\end{landscape} #+LATEX:\newpage -**** NEXT Use Case Detailbeschreibung +**** Use Case Detailbeschreibung -#+CAPTION: Use Case -#+ATTR_LATEX: :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| +Use Cases werden in der Regel mit Hilfe einer sogenannten Use Case +Schablone im Detail beschrieben damit klar ist wie der Ablauf jeweils +genau aussieht. Die von uns verwendete Schablone wurde von Alistair +Cockburn definiert. + +Da ein Web-Shop eine sehr umfangreiche Applikation ist gibt es sehr +viele Use Cases welche beschrieben und umgesetzt werden müssen. Aus +zeitlichen Gründen haben wir nur einen kleinen Teil der Use Cases im +Detail ausgearbeitet. Insbesondere diese welche wir selber +ausprogrammiert haben. Die gesamte Liste an Use Cases sieht wie folgt +aus, die Use Cases mit den Nummern wurden dabei im Detail ausgearbeitet: + +#+LATEX: {\footnotesize +| - [[*Artikel durchst%C3%B6bern][1.0 Artikel durchstöbern]] | - Kategorie erfassen (Admin Funktion) | +| - [[Registration][2.0 Registration]] | - Kategorie ändern (Admin Funktion) | +| - [[User Login][2.1 User Login]] | - Kategorie löschen (Admin Funktion) | +| - User Profil ansehen | - Bild hochladen (Admin Funktion) | +| - [[Artikel in Warenkorb legen][3.0 Artikel in Warenkorb legen]] | - Bild ändern (Admin Funktion) | +| - [[W%C3%A4hrung %C3%A4ndern][3.1 Währung ändern]] | - Bild löschen (Admin Funktion) | +| - Währung aktualisieren (Admin Funktion) | - Bestellung erfassen (Admin Funktion) | +| - [[Checkout][3.2 Checkout]] | - [[Bestellung %C3%A4ndern/korrigieren][7.0 Bestellung ändern/korrigieren (Admin Funktion)]] | +| - [[User Passwort %C3%A4ndern][4.0 User Passwort ändern (Admin Funktion)]] | - Bestellung löschen (Admin Funktion) | +| - [[Artikel erfassen][5.0 Artikel erfassen (Admin Funktion)]] | - [[max_pictures Option anpassen][6.0 max_pictures Option anpassen (Admin Funktion)]] | +| - Artikel ändern (Admin Funktion) | - max_pictures Option deaktivieren (Admin Funktion) | +| - Artikel löschen (Admin Funktion) | - User erfassen (Admin Funktion) | +| - Materialbestellung erfassen (Admin Funktion) | - User/Personen Daten ändern (Admin Funktion) | +| - Materialbestellung ändern/korrigieren (Admin Funktion) | - User löschen (Admin Funktion) | +| - Materialbestellung löschen (Admin Funktion) | - User Berechtigungen anpassen (Admin Funktion) | +| - Stadt hinzufügen (Admin Funktion) | | +| - Stadt ändern (Admin Funktion) | | +| - Stadt löschen (Admin Funktion) | | +#+LATEX:} + +***** Artikel durchstöbern + +#+LATEX:{\footnotesize +#+CAPTION: Use 1.0 Artikel durchstöbern +#+ATTR_LATEX::environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:browse_article +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 1.0 Artikel durchstöbern | +|---------------------+--------------------------------| +| *Description* | Durchklicken der verschiedenen Kategorieren und ansehen der Artikel Details und Bilder. | +|---------------------+--------------------------------| +| *Actors* | Kunden, Interessenten | +|---------------------+--------------------------------| +| *Status* | Freigegeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | User möchte Artikel einsehen | +|---------------------+--------------------------------| +| *Preconditions* | Website aufgerufen | +|---------------------+--------------------------------| +| *Postconditions* | - | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Website aufrufen | +| | 2. Kategorienen durchsehen | +| | 3. Artikel anklicken | +|---------------------+--------------------------------| +| *Alternative Flow* | - | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Registration + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 2.0 Registration +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:registration +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 2.0 Registration | +|---------------------+--------------------------------| +| *Description* | Ein User registriert sich einen Account. | +|---------------------+--------------------------------| +| *Actors* | Interessent | +|---------------------+--------------------------------| +| *Status* | Freigebgen | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | User möchte einen Account erstellen. | +|---------------------+--------------------------------| +| *Preconditions* | Email Adresse vorhanden | +|---------------------+--------------------------------| +| *Postconditions* | Account wurde erfolgreich erstellt. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. User klickt auf den Link "Go to registration.". | +| | 2. User füllt das Registrations Formular aus. | +| | 3. User schliesst die Registrierung mit Klick auf "Register" ab. | +| | 4. Die Website leitet ihn in den Login Bereich um. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. User klickt auf den Link "Go to registration.". | +| | 2. User füllt das Registrations Formular mir falschen Daten aus. | +| | 3. Die Website gibt die entsprechenden Fehler aus. | +| | 4. Der User korrigiert die Angaben. | +| | 5. User schliesst die Registrierung mit Klick auf "Register" ab. | +| | 6. Die Website leitet ihn in den Login Bereich um. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** User Login + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 2.1 User Login +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] #+NAME: tab:login -|---------------------+-----------------------------| -| *Identifier + Name* | | -|---------------------+-----------------------------| -| *Description* | | -|---------------------+-----------------------------| -| *Actors* | | -|---------------------+-----------------------------| -| *Status* | Freigegeben | -|---------------------+-----------------------------| -| *Includes* | - | -|---------------------+-----------------------------| -| *Trigger* | | -|---------------------+-----------------------------| -| *Preconditions* | | -|---------------------+-----------------------------| -| *Postconditions* | | -|---------------------+-----------------------------| -| *Normal Flow* | | -|---------------------+-----------------------------| -| *Alternative Flow* | - | -|---------------------+-----------------------------| -| *Notes* | - | -|---------------------+-----------------------------| -| *UC History* | 1.0 Darft erstellt durch AZ | -|---------------------+-----------------------------| -| *Author* | A. Zweili & I. | -|---------------------+-----------------------------| -| *Date* | | -|---------------------+-----------------------------| +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 2.1 User Login | +|---------------------+--------------------------------| +| *Description* | Ein Kunde logt sich auf der Website ein. | +|---------------------+--------------------------------| +| *Actors* | Kunde | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein Kunde möchte sich einloggen. | +|---------------------+--------------------------------| +| *Preconditions* | UC 2.0 erfolgreich abgeschlossen. | +|---------------------+--------------------------------| +| *Postconditions* | User hat sich erfolgreich eingeloggt. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. User klickt in der Navigation auf "Login". | +| | 2. User gibt Zugangsdaten ein. | +| | 3. User beendet Login mit Klick auf "Login". | +| | 4. Die Website leitet ihn auf die Index Seite um und zeigt neu eine "Profil" und "Logout" Schaltfläche. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. User klickt in der Navigation auf "Login". | +| | 2. User gibt falsche Zugangsdaten ein. | +| | 3. User beendet Login mit Klick auf "Login". | +| | 4. Die Website gibt entsprechende Fehlermeldungen aus. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Artikel in Warenkorb legen + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 3.0 Artikel in Warenkorb legen +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:cart +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 3.0 Artikel in Warenkorb legen | +|---------------------+--------------------------------| +| *Description* | Ein Kunde legt einen Artikel in den Warenkorb. | +|---------------------+--------------------------------| +| *Actors* | Kunde | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein Kunde möchte einen Artikel kaufen. | +|---------------------+--------------------------------| +| *Preconditions* | UC2.1 erfolgreich abgeschlossen. | +|---------------------+--------------------------------| +| *Postconditions* | Artikel wurde im Warenkorb gespeichert. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. User klickt einen Artikel an. | +| | 2. User klickt auf "Add to cart". | +| | 3. Die Website speichert den Artikel im Warenkorb. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. User klickt einen Artikel mit Stock "0.0" an. | +| | 2. User klickt auf "Add to cart". | +| | 3. Die Website meldet "We are sorry but this item is out of stock.". | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Währung ändern + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 3.1 Währung ändern +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:currency +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 3.1 Währung ändern | +|---------------------+--------------------------------| +| *Description* | Ein User ändert die Währung für die Preise. | +|---------------------+--------------------------------| +| *Actors* | Kunde, Interessent | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein User möchte sich die Preise in einer anderen Währung anzeigen lassen. | +|---------------------+--------------------------------| +| *Preconditions* | - | +|---------------------+--------------------------------| +| *Postconditions* | Die Preise werden in der gewünschten Währung angezeigt. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der User wählt im Drop-Down die gewünschte Währung aus. | +| | 2. Die Website aktualisiert und zeigt die neu berechneten Preise an. | +|---------------------+--------------------------------| +| *Alternative Flow* | - | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Checkout + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 3.2 Checkout +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:checkout +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 3.2 Checkout | +|---------------------+--------------------------------| +| *Description* | User gibt seinen Warenkorb als Bestellung auf. | +|---------------------+--------------------------------| +| *Actors* | Kunde | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein Kunde möchte seine Artikel im Warenkorb bestellen. | +|---------------------+--------------------------------| +| *Preconditions* | UC2.1 und UC3.0 erfolgreich abgeschlossen. | +|---------------------+--------------------------------| +| *Postconditions* | Die Bestellung wurde von der Website gespeichert. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der User klickt in der Navigation auf "Cart". | +| | 2. Die Website leitet ihn zum Warenkorb um. | +| | 3. Der User klickt dort auf "Checkout". | +| | 4. Die Website gibt ihm eine komplette Übersicht der Bestellung sowie der Empfängeradresse. | +| | 5. User klickt auf "Send order". | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. Der User klickt in der Navigation auf "Cart". | +| | 2. Die Website leitet ihn zum Warenkorb um. | +| | 3. Der User klickt dort auf "Checkout". | +| | 4. Die Website gibt ihm eine komplette Übersicht der Bestellung sowie der Empfängeradresse. | +| | 5. Der User bricht die Bestellung mit Klick auf "Cancel" ab. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** User Passwort ändern + +#+LATEX:{\footnotesize +#+CAPTION: 4.0 User Passwort ändern +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:password +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 4.0 User Passwort ändern | +|---------------------+--------------------------------| +| *Description* | Ein Administrator ändert ein User Kennwort. | +|---------------------+--------------------------------| +| *Actors* | Verwaltung | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein Administrator möchte ein Passwort zurücksetzen weil es vergessen wurde. | +|---------------------+--------------------------------| +| *Preconditions* | Account mit Administrationsrechten vorhanden. | +|---------------------+--------------------------------| +| *Postconditions* | Auf dem User Account wurde ein neues Passwort gesetzt. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt auf "Users". | +| | 3. Admin wählt den passenden Account aus. | +| | 4. Klickt unterhalb des Passwort Hashes auf "this form". | +| | 5. Gibt zweimal das neue Passwort ein und klickt "Change password". | +| | 6. Die Website leitet den Admin zurück zu den User Details. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klicht auf "Users". | +| | 3. Admin wählt den passenden Account aus. | +| | 4. Klickt unterhalb des Passwort Hashes auf "this form". | +| | 5. Gibt zweimal ein invalides Passwort ein und klickt "Change password". | +| | 6. Die Website gibt eine entsprechende Fehlermeldung aus. | +| | 7. Der Admin korrigiert die Passwörter und klickt auf "Change password". | +| | 8. Die Website leitet den Admin zurück zu den User Details. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Artikel erfassen + +#+LATEX:{\footnotesize +#+CAPTION: 5.0 Artikel erfassen +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:create_article +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 5.0 Artikel erfassen | +|---------------------+--------------------------------| +| *Description* | Ein Administrator erfasst einen neuen Artikel mit Bildern. | +|---------------------+--------------------------------| +| *Actors* | Verwaltung | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Um das Sortiment zu erweitern möchte der Administrator einen neuen Artikel erfassen. | +|---------------------+--------------------------------| +| *Preconditions* | Account mit Administrationsrechten vorhanden. | +|---------------------+--------------------------------| +| *Postconditions* | Der Artikel wir im Webshop angezeigt. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt neben "Articles" auf "+ Add". | +| | 3. Admin füllt das Formular aus und lädt ein Bild hoch. | +| | 4. Klickt unten rechts auf "Save". | +| | 5. Die Website speichert den Artikel in der Datenbank. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt neben "Articles" auf "+ Add". | +| | 3. Admin füllt das Formular aus und lädt zuviele Bilder hoch. | +| | 4. Klickt unten rechts auf "Save". | +| | 5. Die Website gibt eine entsprechende Fehlermeldung aus. | +| | 6. Der Admin entfernt die überzähligen Bilder. | +| | 7. Die Website speichert den Artikel in der Datenbank. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** max_pictures Option anpassen + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 6.0 max_pictures Option anpassen +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:max_pictures +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 6.0 max_pictures Option anpassen | +|---------------------+--------------------------------| +| *Description* | Ein Administrator ändert die max_pictures Option. | +|---------------------+--------------------------------| +| *Actors* | Verwaltung | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Ein Administrator möchte die maximale Anzahl Bilder pro Artikel anpassen. | +|---------------------+--------------------------------| +| *Preconditions* | Account mit Administrationsrechten vorhanden. | +|---------------------+--------------------------------| +| *Postconditions* | Der neue Wert wurde von der Website gespeichert. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt auf "Options" und anschliessend auf "max_pictures". | +| | 3. Admin ändert den Wert "Value" zu einer Ganzzahl seiner Wahl. | +| | 4. Klickt unten rechts auf "Save". | +| | 5. Die Website speichert den Wert in der Datenbank. | +|---------------------+--------------------------------| +| *Alternative Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt auf "Options" und anschliessend auf "max_pictures". | +| | 3. Admin ändert den Wert "Value" zu einer Gleitzahl seiner Wahl. | +| | 4. Klickt unten rechts auf "Save". | +| | 5. Die Website gibt eine entsprechende Fehlermeldung aus. | +| | 6. Der Admin korrigiert den Wert und klickt "Save". | +| | 7. Die Website speichert den Wert in der Datenbank. | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} + +***** Bestellung ändern/korrigieren + +#+LATEX:{\footnotesize +#+CAPTION: Use Case 7.0 Bestellung ändern/korrigieren +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{.25\textwidth}|p{.7\textwidth}| :placement [H] +#+NAME: tab:change_order +|---------------------+--------------------------------| +| | <30> | +| *Identifier + Name* | 7.0 Bestellung ändern/korrigieren | +|---------------------+--------------------------------| +| *Description* | Ein Administrator korrigiert eine Bestellung. | +|---------------------+--------------------------------| +| *Actors* | Verwaltung | +|---------------------+--------------------------------| +| *Status* | Freigeben | +|---------------------+--------------------------------| +| *Includes* | - | +|---------------------+--------------------------------| +| *Trigger* | Administrator ändert auf Wunsch eines Kunden eine Bestellung. | +|---------------------+--------------------------------| +| *Preconditions* | Account mit Administrationsrechten vorhanden. | +|---------------------+--------------------------------| +| *Postconditions* | Die Bestellung hat eine angepasste Artikel Menge. | +|---------------------+--------------------------------| +| *Normal Flow* | 1. Der Administrator loggt sich unter https://didgeridoo.ml/admin ein. | +| | 2. Admin klickt auf "Orders" und anschliessend auf die passende Order ID. | +| | 3. Admin ändert den Wert "Amount" des ersten Artikels zu 0. | +| | 4. Klickt unten rechts auf "Save". | +| | 5. Die Website speichert die Bestellung in der Datenbank. | +|---------------------+--------------------------------| +| *Alternative Flow* | - | +|---------------------+--------------------------------| +| *Notes* | - | +|---------------------+--------------------------------| +| *UC History* | 1.0 Darft erstellt durch AZ | +|---------------------+--------------------------------| +| *Author* | A. Zweili & I. Hörler | +|---------------------+--------------------------------| +| *Date* | 16.01.2018 | +|---------------------+--------------------------------| +#+LATEX:} *** NEXT Mockup #+CAPTION: Ein frühes Mockup des Shop #+ATTR_LATEX: :width \textwidth #+NAME: mockup -[[file:pictures/mockup-full-snipet.png][file:pictures/mockup-full-snipet.png]] +[[./pictures/mockup-full-snipet.png]] + +*** TODO Models + +\footcite{djangoextensions} -*** TODO Klassendiagramme der Models **** NEXT Category -#+ATTR_LATEX: :width 9cm +\footcite{tree} + +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Kategorien #+NAME: fig:category -[[file:pictures/class_category.png]] +[[./pictures/class_category.png]] + **** NEXT Option -#+ATTR_LATEX: :width 9cm +\footcite{readonly} +\footcite{removeadd} +\footcite{removedelete} + +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Optionen #+NAME: fig:option -[[file:pictures/class_option.png][file:pictures/class_option.png]] - -**** NEXT Setting - -#+ATTR_LATEX: :width 9cm -#+CAPTION: Klassenmodel für Einstellungen -#+NAME: fig:umweltgrafik -[[file:pictures/class_setting.png][file:pictures/class_setting.png]] +[[./pictures/class_option.png]] **** NEXT ArticleStatus -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Artikelstatus #+NAME: fig:articlestatus -[[file:pictures/class_articlestatus.png][file:pictures/class_articlestatus.png]] +[[./pictures/class_articlestatus.png]] **** TODO ExchangeRate -#+ATTR_LATEX: :width 9cm +\footcite{timezone} + +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Wechselkurse #+NAME: fig:exchangerate -[[file:pictures/class_exchangerate.png][file:pictures/class_exchangerate.png]] +[[./pictures/class_exchangerate.png]] **** NEXT Article -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Artikel #+NAME: fig:article -[[file:pictures/class_article.png][file:pictures/class_article.png]] +[[./pictures/class_article.png]] **** NEXT OrderStatus -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Bestellstatus #+NAME: fig:orderstatus -[[file:pictures/class_orderstatus.png][file:pictures/class_orderstatus.png]] +[[./pictures/class_orderstatus.png]] **** NEXT OrderOfGoods -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Warenbestellungen #+NAME: fig:orderofgoods -[[file:pictures/class_orderofgoods.png][file:pictures/class_orderofgoods.png]] +[[./pictures/class_orderofgoods.png]] **** NEXT Picture -#+ATTR_LATEX: :width 9cm +\footcite{upload} +\footcite{images} + +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Bilder #+NAME: fig:picture -[[file:pictures/class_picture.png][file:pictures/class_picture.png]] +[[./pictures/class_picture.png]] **** NEXT Order -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Bestellungen #+NAME: fig:order -[[file:pictures/class_order.png][file:pictures/class_order.png]] +[[./pictures/class_order.png]] **** NEXT ShoppingCart -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Warenkörbe #+NAME: fig:shoppingcart -[[file:pictures/class_shoppingcart.png][file:pictures/class_shoppingcart.png]] +[[./pictures/class_shoppingcart.png]] **** NEXT City -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Städte #+NAME: fig:city -[[file:pictures/class_city.png][file:pictures/class_city.png]] +[[./pictures/class_city.png]] **** NEXT Salutation -#+ATTR_LATEX: :width 9cm +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Anreden #+NAME: fig:salutation -[[file:pictures/class_salutation.png][file:pictures/class_salutation.png]] +[[./pictures/class_salutation.png]] **** NEXT Person -#+ATTR_LATEX: :width 9cm +\footcite{usermodel} + +#+ATTR_LATEX: :width 9cm :placement [H] #+CAPTION: Klassenmodel für Personen #+NAME: fig:person -[[file:pictures/class_person.png][file:pictures/class_person.png]] +[[./pictures/class_person.png]] ** Benutzerinterface *** Mockup skizzieren *** Frontend Umsetzung *** Backend Umsetzung -** Testfälle +** Testing + +*** Fixtures #+LATEX:\newpage #+LATEX:\begin{landscape} +*** Testfälle + #+CAPTION: Testfälle -#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{1.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}| +#+ATTR_LATEX: :environment longtable :align |>{\columncolor[HTML]{EFEFEF}}p{1.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}|p{2.5cm}| :placement [H] #+NAME: tab:testcases |----------------------+----------------------+----------------------+----------------------+----------------------+----------------------+----------------------+----------------------| | <20> | <20> | <20> | <20> | <20> | <20> | <20> | <20> |