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. _______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp