← Back to team overview

widelands-dev team mailing list archive

Re: [Merge] lp:~trimardio/widelands-website/scheduling_module into lp:widelands-website

 

Another batch of comments.

I feel that I am slowing this code review down because I have so little time for Widelands these days - so it is always days before I comment again. I am sorry for that. I think it is better I just remove myself from this code review completely before being a demotivating factor. I will hence not complain any more :). Thanks for your work on this feature!

Kaputtnik, could you merge this once you are happy?


Diff comments:

> === added file 'media/css/kalendae.css'
> --- media/css/kalendae.css	1970-01-01 00:00:00 +0000
> +++ media/css/kalendae.css	2017-09-29 17:35:23 +0000
> @@ -0,0 +1,268 @@
> +/** Base container **/

Can we get a consistent formatting in this file? one property per line, { always as last character? It gets hard to read like this.

> +.kalendae {
> +	display: inline-block;zoom:1;*display:inline;

;*display seems like a syntax error to me?

> +	background:#eee;
> +	padding:10px;
> +	margin:5px;
> +	border-radius:5px;
> +	font-size:11px;
> +	font-family:'Helvetica Neue', 'Helvetica';
> +	cursor:default;
> +	position:relative;
> +	-moz-box-sizing: border-box;
> +	box-sizing: border-box;
> +}
> +
> +.kalendae * {
> +	-moz-box-sizing: border-box;
> +	box-sizing: border-box;
> +}
> +
> +/** Popup Container for Kalendae.Input **/
> +.kalendae.k-floating {
> +	position:absolute;
> +	top:0;
> +	left:0;
> +	z-index:100000;
> +	margin:0;
> +	box-shadow:0 1px 3px rgba(0,0,0,0.75);
> +}
> +
> +/** Kalendae.Input's popup close button **/
> +.kalendae .k-btn-close {
> +	position:absolute;
> +	top:-8px;
> +	right:-8px;
> +	width:20px;
> +	height:20px;
> +	background:white;
> +	border:2px solid #ccc;
> +	color:#999;
> +	line-height:17px;
> +	text-align:center;
> +	font-size:13px;
> +	border-radius:10px;
> +	box-shadow:0 1px 3px rgba(0,0,0,0.75);
> +	cursor:pointer;
> +	text-decoration:none;
> +}
> +.kalendae .k-btn-close:after {content:"\2716";}
> +.kalendae .k-btn-close:hover {
> +	color:#7EA0E2;
> +	background:white;
> +	border-color:#7EA0E2;
> +}
> +
> +/** Month Container **/
> +.kalendae .k-calendar {display: inline-block;zoom:1;*display:inline;width:155px;vertical-align:top;}
> +
> +/** Month Separator **/
> +.kalendae .k-separator {
> +	display: inline-block;zoom:1;*display:inline;
> +	width:2px;
> +	vertical-align:top;
> +	background:#ddd;
> +	height:155px;
> +	margin:0px 10px;
> +}
> +
> +/** Month Title Row **/
> +.kalendae .k-title {text-align:center;white-space:nowrap;position:relative;height:18px;}
> +.kalendae .k-caption {font-size:12px;line-height:18px;}
> +
> +
> +/** Month and Year Buttons **/
> +.kalendae .k-btn-previous-month,
> +.kalendae .k-btn-next-month,
> +.kalendae .k-btn-previous-year,
> +.kalendae .k-btn-next-year {
> +	width:16px;
> +	height:23px;
> +	cursor:pointer;
> +	position:absolute;
> +	top:-3px;
> +	color:#777;
> +	font-size:32px;
> +	line-height: 18px;
> +	font-weight: bold;
> +	font-family: arial;
> +	text-decoration:none;
> +}
> +
> +.kalendae .k-btn-previous-year {left:0;}
> +.kalendae .k-btn-previous-month {left:16px;}
> +.kalendae .k-btn-next-month {right:16px;}
> +.kalendae .k-btn-next-year {right:0;}
> +
> +.kalendae .k-btn-previous-month:after {content:"\2039";}
> +.kalendae .k-btn-next-month:after {content:"\203A";}
> +
> +.kalendae .k-btn-previous-year:after {content:"\00AB";}
> +.kalendae .k-btn-next-year:after {content:"\00BB";}
> +
> +.kalendae .k-btn-previous-month:hover,
> +.kalendae .k-btn-next-month:hover {color:#7EA0E2;}
> +
> +.kalendae .k-btn-previous-year:hover,
> +.kalendae .k-btn-next-year:hover {color:#6FDF81;}
> +
> +/** Remove extra buttons when calendar shows multiple months **/
> +.kalendae .k-first-month .k-btn-next-month,
> +.kalendae .k-middle-month .k-btn-next-month,
> +.kalendae .k-middle-month .k-btn-previous-month,
> +.kalendae .k-last-month .k-btn-previous-month,
> +.kalendae .k-first-month .k-btn-next-year,
> +.kalendae .k-middle-month .k-btn-next-year,
> +.kalendae .k-middle-month .k-btn-previous-year,
> +.kalendae .k-last-month .k-btn-previous-year {display:none;}
> +
> +/** Disable year nav option **/
> +.kalendae .k-title.k-disable-year-nav .k-btn-next-year,
> +.kalendae .k-title.k-disable-year-nav .k-btn-previous-year { display: none; }
> +.kalendae .k-title.k-disable-year-nav .k-btn-next-month { right: 0; }
> +.kalendae .k-title.k-disable-year-nav .k-btn-previous-month { left: 0; }
> +
> +/** Force specific width for month container contents **/
> +.kalendae .k-title,
> +.kalendae .k-header,
> +.kalendae .k-days {
> +	width:154px;
> +	display:block;
> +	overflow:hidden;
> +}
> +
> +
> +/** Hide unusable buttons **/
> +.kalendae.k-disable-next-month-btn .k-btn-next-month,
> +.kalendae.k-disable-previous-month-btn .k-btn-previous-month,
> +.kalendae.k-disable-next-year-btn .k-btn-next-year,
> +.kalendae.k-disable-previous-year-btn .k-btn-previous-year {
> +	display:none;
> +}
> +
> +
> +/** Week columns and day cells **/
> +.kalendae .k-header span,
> +.kalendae .k-days span {
> +	float:left;
> +	margin:1px 1px 2px 1px;
> +}
> +
> +.kalendae .k-header span {
> +	text-align:center;
> +	font-weight:bold;
> +	width:20px;
> +	padding:1px 0;
> +	color:#666;
> +}
> +
> +.kalendae .k-header.k-active span {
> +	cursor: pointer;
> +	border-radius:3px;
> +}
> +
> +.kalendae .k-days span {
> +	text-align:right;
> +	width:20px;
> +	height:1.5em;
> +	line-height:1em;
> +	padding:2px 3px 2px 2px;
> +	border:1px solid transparent;
> +	border-radius:3px;
> +	color:#999;
> +}
> +
> +/** Today **/
> +.kalendae .k-today {
> +	text-decoration:underline;
> +}
> +
> +/** Days inside of the month view **/
> +.kalendae .k-days span.k-in-month.k-active {
> +	border-color:#ddd;
> +	background-color:#fff;
> +	color:#333;
> +}
> +/** Days outside of the month view (before the first day of the month, after the last day of the month) **/
> +.kalendae .k-days span.k-out-of-month {color:#ddd;}
> +
> +/** Selectable  **/
> +.kalendae .k-days span.k-active {
> +	cursor:pointer;
> +}
> +
> +/** Selected day, when outside the selectable area **/
> +.kalendae .k-days span.k-selected {
> +	border-color:#1072A5;
> +	color:#1072A5;
> +}
> +
> +/** Selected day, when inside the selectable area **/
> +.kalendae .k-days span.k-selected.k-active,
> +.kalendae .k-header.k-active span.k-selected {
> +	background:#7EA0E2;
> +	color:white;
> +}
> +
> +/** Days between the start and end points on a range, outside of the selectable area **/
> +.kalendae .k-days span.k-range {
> +	background:none;
> +	border-color:#6DD4FE;
> +}
> +
> +/** Days between the start and end points on a range, inside of the selectable area **/
> +.kalendae .k-days span.k-range.k-in-month {
> +	background:#C4D4F1;
> +	border-color:#19AEFE;
> +	color:#333;
> +}
> +
> +/** Selectable day, hovered **/
> +.kalendae .k-days span.k-active:hover,
> +.kalendae .k-days span.k-active.k-day-hover-active {
> +	border-color:#666;
> +}
> +
> +/** Selectable week, hovered **/
> +.kalendae .k-week:hover span.k-active {
> +	border-color:#666;
> +}
> +
> +.clearfix:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
> +
> +/*-------------------------------------IE8 ONLY CODE BELOW THIS LINE--------------------------------------------*/
> +
> +.kalendae.ie8.k-floating {
> +	border:1px solid #ccc;
> +}
> +
> +.kalendae.ie8 .k-btn-close {
> +	width:20px;
> +	height:20px;
> +	border:none;
> +	background:url('close.png') no-repeat top left;
> +}
> +.kalendae.ie8 .k-btn-close:after {display:none;}
> +
> +.kalendae.ie8 .k-btn-previous-month,
> +.kalendae.ie8 .k-btn-next-month,
> +.kalendae.ie8 .k-btn-previous-year,
> +.kalendae.ie8 .k-btn-next-year {width:16px;height:16px;cursor:pointer;background:#777 url('arrows.png') no-repeat center left;position:absolute;top:0;}
> +
> +.kalendae.ie8 .k-btn-next-month,
> +.kalendae.ie8 .k-btn-next-year {background-position:center right;}
> +
> +.kalendae.ie8 .k-btn-previous-month:hover,
> +.kalendae.ie8 .k-btn-next-month:hover {background-color:#7EA0E2;}
> +
> +.kalendae.ie8 .k-btn-previous-year,
> +.kalendae.ie8 .k-btn-next-year {background-color:#333;}
> +
> +.kalendae.ie8 .k-btn-previous-year:hover,
> +.kalendae.ie8 .k-btn-next-year:hover {background-color:#6FDF81;}
> +
> +.kalendae.ie8 .k-btn-previous-month:after,
> +.kalendae.ie8 .k-btn-next-month:after,
> +.kalendae.ie8 .k-btn-previous-year:after,
> +.kalendae.ie8 .k-btn-next-year:after {display:none;}
> +
> 
> === added file 'media/js/scheduling.js'
> --- media/js/scheduling.js	1970-01-01 00:00:00 +0000
> +++ media/js/scheduling.js	2017-09-29 17:35:23 +0000
> @@ -0,0 +1,313 @@
> +// global variables
> +var lastSelectedDates = []
> +
> +// Create calandar and return it as an object. Useful for callbacks.
> +function createCalandar() {
> +    return new Kalendae('calandar', {
> +        mode: 'multiple',
> +        direction: 'today-future',
> +        subscribe: {
> +            'change': function (date) {
> +                selectedDate = this.getSelected().split(",");
> +                //Cleanup of whitespace
> +                for (var i in selectedDate) {
> +                    selectedDate[i] = selectedDate[i].replace(' ' , '');
> +                }
> +                dateToUpdate = array_diff(lastSelectedDates, selectedDate);
> +                updateAvailableDate(dateToUpdate[0])
> +                lastSelectedDates = selectedDate;
> +            }
> +        }
> +    });
> +}
> +
> +//Add warning in case of disparency between system and profil timezone
> +function addTimeZoneWarningIfNeeded() {
> +    var userTimeZone = document.getElementById('django-data').getAttribute('user-time-zone')
> +    var systemTimeZone = - new Date().getTimezoneOffset()/60
> +    if ( systemTimeZone != userTimeZone) {
> +        document.getElementById('timezone-error').removeAttribute("hidden");
> +        profilTime = document.getElementsByClassName('profil-time');
> +        for (var element in profilTime) {
> +            profilTime[element].innerHTML = cleanAndAddSign(userTimeZone);
> +        }
> +        document.getElementById('system-time').innerHTML = cleanAndAddSign(systemTimeZone);
> +    }
> +}
> +
> +function addPreviousDateFromUser(calendar) {
> +    //Populate the current date with already filled date by the user
> +    old_availabilities_string_JSON = document.getElementById('django-data').getAttribute('day-to-fill')
> +    old_availabilities_list = stringJSONtoJSList(old_availabilities_string_JSON)
> +    if (old_availabilities_list == "") {
> +        return;
> +    }
> +    dateToAdd = []
> +    for (var date in old_availabilities_list) {
> +        // Extract day of the date. See format in view.py.
> +        dateString = old_availabilities_list[date].substring(1,11);
> +        if (!existInList(dateToAdd, dateString)){
> +            dateToAdd.push(dateString)
> +        }
> +    }
> +    dateStringList = dateToAdd.join(',')
> +    calendar.setSelected(dateStringList);
> +    for (var date in old_availabilities_list) {
> +        // Extract hour of the date. See format in view.py.
> +        hourString = old_availabilities_list[date].substring(12,14);
> +        // Extract day of the date. See format in view.py.
> +        dateString = old_availabilities_list[date].substring(1,11);
> +        hourString = removeZeroIfUnderTen(hourString);
> +        displayHourForDate(dateString, hourString);
> +    }
> +}
> +
> +function addOtherUsersAvailabilities() {
> +    //Populate the result area showing other users and their disponibilities
> +    if (document.getElementById('django-data').getAttribute('users-to-fill')) {
> +        var otherPlayerAvailabilitiesJSON = document.getElementById('django-data').getAttribute('users-to-fill');
> +        otherPlayerAvailabilities = JSON.parse(otherPlayerAvailabilitiesJSON)
> +    }
> +    noOtherUser = true;
> +    for (var user in otherPlayerAvailabilities) {
> +        noOtherUser = false;
> +        dateList = otherPlayerAvailabilities[user]
> +        for (var date in dateList) {
> +            createUserDivOrUpdateIt(user, dateList[date])
> +        }
> +    }
> +    if (noOtherUser) {
> +        document.getElementById('no-user-to-display').removeAttribute("hidden");
> +    }
> +}
> +
> +function sendDataAsForm(calendar) {
> +    //Get informations from selected hours
> +    var selectedDates = calendar.getSelected().split(",");
> +    var selectedDatesList = [];
> +    for (var d in selectedDates) {
> +        //remove whitespace
> +        selectedDates[d] = selectedDates[d].replace(" ", "");
> +        var dateID = 'day-' + selectedDates[d];
> +        dateObj = document.getElementById(dateID);
> +        if (dateObj) {
> +            hoursList = dateObj.getElementsByClassName('hours');
> +            selectedHours = dateObj.getElementsByClassName('selected');
> +            if (selectedHours[0]){
> +                for (var h in hoursList) {
> +                    if (hasClass(hoursList[h] , 'selected')){
> +                        var hourAsDate = new Date(selectedDates[d] + "T" + addZeroIfUnderTen(h) + ":00:00")
> +                        // we get the hour in utc time and remove unneeded informations
> +                        stringDate = hourAsDate.toISOString().slice(0,13)
> +                        selectedDatesList.push(stringDate);
> +                    }
> +                }
> +            } 
> +        }
> +    }
> +    //Send informations to server
> +    post('.', selectedDatesList)
> +}
> +
> +//Add or remove available dates in the ui.
> +function updateAvailableDate(date) {
> +    newDateID = "day-" + date
> +    dateAlreadyExist = !!document.getElementById(newDateID);
> +    document.getElementById('second-step').removeAttribute('hidden');
> +    if (dateAlreadyExist) {
> +        document.getElementById(newDateID).remove();
> +    } else {
> +        var original = document.getElementById('day-template');
> +        // We clone the date and fix different attributes
> +        var newDate = original.cloneNode(true);
> +        newDate.id = "day-" + date;
> +        newDate.removeAttribute("hidden");
> +        var textDate = new Date(date);
> +        textDate = textDate.toDateString();
> +        newDate.getElementsByClassName('day-title')[0].innerHTML = '<h3>' + textDate + '</h3>';
> +        //We add the listeners to each hours One for the click event, the other for the hover when the mouse is pressed
> +        hoursObj = newDate.getElementsByClassName('hours');
> +        for (var i = 0; i < hoursObj.length; i++) {
> +            hoursObj[i].addEventListener('click', updateHour, false);
> +            hoursObj[i].addEventListener("mouseover", function(e){
> +                if(e.buttons == 1 || e.buttons == 3){
> +                    updateHour(e);
> +                }
> +            })
> +        }
> +        //we look for the order the new date should be in
> +        daysList = document.getElementById('days-wrapper').getElementsByClassName('days');
> +        //We finally add the new date
> +        document.getElementById('days-wrapper').appendChild(newDate);
> +    }
> +
> +}
> +
> +function updateHour (event) {
> +    var div = (event.fromElement ? event.fromElement : event.currentTarget);
> +    isAlreadySelected = hasClass(div, 'selected');
> +    if (isAlreadySelected) {
> +        div.className = 'hours';
> +    } else {
> +        div.className += ' selected';
> +    }
> +}
> +
> +function displayHourForDate(date, hour, user) {
> +    var dateDivID = 'day-' + date
> +    if (user) {
> +        dateDivID = user + '-day-' + date
> +    }
> +    dateDiv = document.getElementById(dateDivID);
> +    hourDiv = dateDiv.getElementsByClassName('hours');
> +    for (var hourInDiv in hourDiv) {
> +        if (hourInDiv == hour) {
> +            hourDiv[hourInDiv].className += ' selected';
> +        }
> +    }
> +}
> +
> +function createUserDivOrUpdateIt(user, availTime) {
> +    if (!document.getElementById("user-" + user)){
> +        var original = document.getElementById('other-user-template');
> +        // We clone the date and fix different attributes
> +        var otherUser = original.cloneNode(true);
> +        otherUser.id = "user-" + user;
> +        otherUser.removeAttribute("hidden");
> +        var jsEventHTML = "/messages/compose/" + user

yup seems a bit better. that way they can be styled through django, like the rest of the code.

> +        var imageHTML = '<img src="/wlmedia/forum/img/send_pm.png" alt="" class="middle"><span class="middle">Send PM</span>'
> +        var userTitle = otherUser.children[0]
> +        var button =  document.createElement("button");
> +        button.innerHTML = imageHTML;
> +        button.onclick = function(){
> +            window.location.href = '/messages/compose/' + user;
> +        }
> +        var usernameP = document.createElement('p')
> +        usernameP.innerHTML = user;
> +        userTitle.appendChild(usernameP)
> +        userTitle.appendChild(button)
> +    } else {
> +        otherUser = document.getElementById("user-" + user);
> +    }
> +    var dtavailTime = new Date(availTime + ":00:00");
> +    //Remove timezone offset which js automatically add...
> +    js_offset = dtavailTime.getTimezoneOffset()/60
> +    dtavailTime = dtavailTime.addHours(js_offset);
> +    textDate = dtavailTime.toDateString();
> +    var dateFormated = dtavailTime.getFullYear() + "-" + dtavailTime.getMonth() + '-' + dtavailTime.getDay()
> +    var availTimeFormated = dtavailTime.getHours()
> +    var originalDay = document.getElementById('other-day-template')
> +    if (!document.getElementById(user + "-day-" + dateFormated)) {
> +        var day = originalDay.cloneNode(true);
> +        day.id = user + "-day-" + dateFormated;
> +        day.removeAttribute("hidden");
> +        day.getElementsByClassName('day-title')[0].innerHTML = '<h3>' + textDate + '</h3>'; 
> +        otherUser.appendChild(day);
> +    }
> +    document.getElementById('other-users-wrapper').appendChild(otherUser);
> +    displayHourForDate(dateFormated, availTimeFormated, user);
> +}
> +
> +/*****************************/
> +/********* utilities *********/
> +/*****************************/
> +// read as "Stackoverflow coded this"

Include the link to the SO post?

> +
> +//Return diff between two list
> +function array_diff(a1, a2) {
> +    var a = [], diff=[];
> +    for (var i = 0; i < a1.length; i++) {
> +        a[a1[i]] = true;
> +    }
> +    for (var i=0; i < a2.length; i++) {
> +        if (a[a2[i]]) {
> +            delete a[a2[i]];
> +        } else {
> +            a[a2[i]] = true;
> +        }
> +    }
> +    for (var k in a) {
> +        diff.push(k)
> +    }
> +    return diff
> +}
> +
> +function hasClass(element, cls) {
> +    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
> +}
> +
> +//We need a custom function to submit a form because our data isn't formated as a form.
> +function post(path, params, method) {
> +    method = method || "post"; // Set method to post by default if not specified.
> +    // The rest of this code assumes you are not using a library.
> +    // It can be made less wordy if you use one.
> +    var form = document.createElement("form");
> +    form.setAttribute("method", method);
> +    form.setAttribute("action", path);
> +    for(var key in params) {
> +            var hiddenField = document.createElement("input");
> +            hiddenField.setAttribute("type", "hidden");
> +            hiddenField.setAttribute("name", key);
> +            hiddenField.setAttribute("value", params[key]);
> +
> +            form.appendChild(hiddenField);
> +    }
> +    //CSRF token
> +    var csrf_div = document.createElement("div");
> +    csrf_div.innerHTML = document.getElementById('django-data').getAttribute('csrf-token')
> +    form.appendChild(csrf_div);
> +    document.body.appendChild(form);
> +    form.submit();
> +}
> +
> +function cleanJSONfromWhiteSpace(json) {
> +    var name, newName;
> +    for (var name in json) {
> +        // Get the name without spaces
> +        newName = name.replace(/ /g, "");
> +        // If that's different...
> +        if (newName != name) {
> +            // Create the new property
> +            json[newName] = json[name];
> +            // Delete the old one
> +            delete json[name];
> +        }
> +    }
> +    return json;
> +}
> +
> +function stringJSONtoJSList(json) {
> +    //removes brackets
> +    json = json.substring(1, json.length-1);
> +    var jsonList= json.split(",")
> +    for (var i in jsonList){
> +        jsonList[i] = jsonList[i].replace(/\s/g, '');
> +    }
> +    return jsonList
> +
> +}
> +
> +function addZeroIfUnderTen(number) {
> +    return ('0' + number).slice(-2)
> +}
> +
> +function removeZeroIfUnderTen(number) {
> +    return parseInt(number, 10);
> +}
> +
> +Date.prototype.addHours = function(h) {    
> +   this.setTime(this.getTime() + (h*60*60*1000)); 
> +   return this;   
> +}
> +
> +function existInList(list, value) {
> +    return list.indexOf(value) > -1
> +}
> +
> +function cleanAndAddSign(number) {
> +    if (number < 0) {
> +        return ' ' + parseInt(number)
> +    } else {
> +        return '+ ' + parseInt(number)
> +    }
> +}
> \ No newline at end of file
> 
> === added directory 'templates/wlscheduling'
> === added file 'templates/wlscheduling/base.html'
> --- templates/wlscheduling/base.html	1970-01-01 00:00:00 +0000
> +++ templates/wlscheduling/base.html	2017-09-29 17:35:23 +0000
> @@ -0,0 +1,9 @@
> +{% extends "base.html" %}

wonderful!

> +{% block extra_head %}
> +
> +<link  rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/kalendae.css" >
> +<link  rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/scheduling.css" >
> +
> +<script src="{{ MEDIA_URL }}js/kalendae.min.js" type="text/javascript"></script>
> +<script src="{{ MEDIA_URL }}js/scheduling.js" type="text/javascript"></script>
> +{% endblock %}
> \ No newline at end of file
> 
> === added file 'templates/wlscheduling/find.html'
> --- templates/wlscheduling/find.html	1970-01-01 00:00:00 +0000
> +++ templates/wlscheduling/find.html	2017-09-29 17:35:23 +0000
> @@ -0,0 +1,47 @@
> +
> +
> +{% extends "wlscheduling/base.html" %}
> +{% comment %}
> +   vim:ft=htmldjango
> +{% endcomment %}
> +
> +{% block content %}
> +<script type="text/javascript">
> +document.addEventListener('DOMContentLoaded', function(){ 
> +    addTimeZoneWarningIfNeeded();
> +    addOtherUsersAvailabilities();
> +}, false);
> +</script>
> +
> +<div class="blogEntry">
> +    <h1>Times other players are available</h1>
> +    <h3 id="timezone-msg">You are currently noted as UTC <span class="profil-time"></span></h3>

This dive including the warning seems duplicated. Pull out into e separate {% block %}?

> +    <div id="timezone-error" class="errormessage" hidden="hidden">WARNING! The time indicated by your system (UTC <span id="system-time"></span>) is different from the one stored in your profile (UTC <span id="profil-time"></span>). By default we will use the one stored in your profile.<br /> <a href="{% url 'profile_edit' %}">Edit Profile</a></div>

s/system/browser?

> +    <div id="other-users-wrapper"></div>
> +    <div id="no-user-to-display" hidden="hidden">Sorry currently there isn't anybody who has scheduled a gaming session. This shouldn't happen. Who doesn't want to play to widelands????!! :O :O</div>

This shouldn't happen sounds like the user found a bug. Drop the last two sentences?

> +
> +</div>
> +
> +<!-- TODO entirely in JS? -->

the more you can do on the backend - i.e. python + django - the better imho. There are different school of thoughts here, but I am definitively a backend person. I would even make a JSON handler for everything you currently construct in Javascript - i.e. let the browser query from django the HTML for the dynamic content. But I am also fine with the mix we have.

> +<div id="other-day-template" hidden="hidden">
> +    <div class="day-title"></div>
> +    <div id="hours-wrapper" class="hours-wrapper" >
> +        {% for i in "xxxxxxxxxxxxxxxxxxxxxxxx" %}
> +            <div class="hours" title="{{ forloop.counter0 }} to {{ forloop.counter0|add:1 }} hour"></div>
> +        {% endfor %}
> +    </div>
> +</div>
> +
> +
> +<!-- TODO remove this template and do it entirely in js (it doesn't need any django value) -->
> +<div id="other-user-template" class="other-user-div" hidden="hidden">
> +    <div class="title"></div>
> +</div>
> +
> +
> +
> +<!-- Div to send django data to js file -->
> +<div id="django-data" user-time-zone="{{user.wlprofile.time_zone}}" users-to-fill="{{other_users_availabilities}}"></div>
> +
> +
> +{% endblock %}
> \ No newline at end of file
> 
> === added file 'wlscheduling/views.py'
> --- wlscheduling/views.py	1970-01-01 00:00:00 +0000
> +++ wlscheduling/views.py	2017-09-29 17:35:23 +0000
> @@ -0,0 +1,118 @@
> +#!/usr/bin/env python -tt
> +# encoding: utf-8
> +#
> +
> +from django.shortcuts import render
> +from models import Availabilities
> +from django.contrib.auth.decorators import login_required
> +import json
> +from datetime import datetime, timedelta
> +
> +
> +#########
> +# Views #
> +#########
> +def scheduling_main (request):
> +    return render(request, 'wlscheduling/main.html')
> +
> +@login_required
> +def scheduling_find (request):
> +    current_user = request.user
> +    other_users_availabilities = {}
> +    for a in Availabilities.objects.exclude(user=current_user).order_by('avail_time'):
> +        user_utc_dt_avail_time = a.avail_time
> +        if datetime.now() < user_utc_dt_avail_time:
> +            other_user = a.user
> +            current_user_timezone = current_user.wlprofile.time_zone
> +            user_dt_avail_time = user_utc_dt_avail_time + timedelta(hours= current_user_timezone)
> +            user_string_avail_time = datetime.strftime(user_dt_avail_time, '%Y-%m-%dT%H')
> +                
> +            if not other_user.username in other_users_availabilities:
> +                other_users_availabilities[other_user.username] = []
> +            other_users_availabilities[other_user.username].append(user_string_avail_time)
> +        else:

deletion can make this execute arbitrarily slow. Deletion of old objects should be done in ./manage.py cleanup. Add a TODO for this?

> +            a.delete()
> +    return render(request, 'wlscheduling/find.html', {'other_users_availabilities': json.dumps(other_users_availabilities)})
> +
> +@login_required
> +def scheduling(request):
> +    current_user = request.user
> +    current_user_availabilities = []
> +    user_timezone = current_user.wlprofile.time_zone
> +    
> +    # remove obsoletes dates from db
> +    for a in Availabilities.objects.filter(user=current_user):

same comment about slowness.

> +        if datetime.utcnow() > a.avail_time:
> +            a.delete()
> +        
> +    # Update of user's availabilities when post mode
> +    if request.method == 'POST':
> +        request_avail_times = []
> +        for r in request.POST:
> +            if r != "csrfmiddlewaretoken":
> +                request_avail_times.append(request.POST[r])
> +
> +
> +        current_user_availabilities = []
> +        for avail_time in request_avail_times:
> +            dt_avail_time = datetime.strptime(avail_time, '%Y-%m-%dT%H')
> +            utc_dt_avail_time =  dt_avail_time + timedelta(hours= - user_timezone)
> +
> +            # We append the string to the list because apparently datetime objects cannot be stored in a list?
> +            utc_string_avail_time = datetime.strftime(utc_dt_avail_time, '%Y-%m-%dT%H')
> +            current_user_availabilities.append(utc_string_avail_time)
> +        
> +        
> +        for request_avail_time in request_avail_times:
> +            dt_avail_time = datetime.strptime(request_avail_time, '%Y-%m-%dT%H')
> +            # Actual change of timezone, we got back to UTC
> +            utc_dt_avail_time =  dt_avail_time + timedelta(hours= - user_timezone)
> +            avail_time_already_exist = False
> +            for a in Availabilities.objects.filter(user=current_user, avail_time=utc_dt_avail_time):
> +                avail_time_already_exist = True
> +            
> +            if not avail_time_already_exist:
> +                a = Availabilities.objects.create(
> +                    user=current_user,
> +                    avail_time=utc_dt_avail_time
> +                )
> +                a.save()
> +        
> +        # We remove any previously stored date that is not present in the request anymore
> +        for a in Availabilities.objects.filter(user=current_user):
> +            utc_dt_avail_time = a.avail_time
> +            to_remove = True
> +            for utc_string_avail_time in current_user_availabilities:
> +                request_utc_dt_avail_time = datetime.strptime(utc_string_avail_time, '%Y-%m-%dT%H')
> +                if utc_dt_avail_time == request_utc_dt_avail_time:
> +                    to_remove = False
> +            if to_remove:
> +                a.delete()
> +
> +        
> +
> +    current_user_availabilities = []
> +    for a in Availabilities.objects.filter(user=current_user).order_by('-avail_time'):
> +        utc_dt_avail_time = a.avail_time
> +        # We display the time with current user timezone
> +        dt_avail_time = utc_dt_avail_time + timedelta(hours=user_timezone)
> +        string_avail_time = datetime.strftime(dt_avail_time, '%Y-%m-%dT%H')
> +        current_user_availabilities.append(string_avail_time)
> +
> +    other_users_availabilities = {}
> +    for current_user_a in Availabilities.objects.filter(user=current_user).order_by('-avail_time'):
> +        current_user_utc_dt_avail_time = current_user_a.avail_time
> +        for a in Availabilities.objects.filter(avail_time=current_user_utc_dt_avail_time).exclude(user=current_user).order_by('-avail_time'):
> +            user_utc_dt_avail_time = a.avail_time
> +            other_user = a.user
> +            current_user_timezone = current_user.wlprofile.time_zone
> +            user_dt_avail_time = user_utc_dt_avail_time + timedelta(hours= current_user_timezone)
> +            user_string_avail_time = datetime.strftime(user_dt_avail_time, '%Y-%m-%dT%H')
> +             
> +            if not other_user.username in other_users_availabilities:
> +                other_users_availabilities[other_user.username] = []
> +            other_users_availabilities[other_user.username].append(user_string_avail_time)
> +
> +
> +    return render(request, 'wlscheduling/scheduling.html', {'current_user_availabilities': json.dumps(current_user_availabilities),
> +'other_users_availabilities': json.dumps(other_users_availabilities)})
> \ No newline at end of file


-- 
https://code.launchpad.net/~trimardio/widelands-website/scheduling_module/+merge/331477
Your team Widelands Developers is subscribed to branch lp:widelands-website.


References