← Back to team overview

widelands-dev team mailing list archive

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

 

Trimardio has proposed merging lp:~trimardio/widelands-website/module_scheduling into lp:widelands-website.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~trimardio/widelands-website/module_scheduling/+merge/335570

Added the scheduling module.

Widelands games are long. And the player base is relatively small. Thus on IRC it is hard to find someone who has the time to play.

To solve that problem the goal of the scheduling module is to allow players to set the times at which they are available for a game and display it for other.

There are two pages for this module:
- the "find" page allows to see the availabilities of all the players.
- the "scheduling" page allow to set the times when the player will have time to play. It then allow him to see who will be available at the same times.

This is the third attempt I make at merging this cleanly. I'm not a bzr person.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~trimardio/widelands-website/module_scheduling into lp:widelands-website.
=== added file 'media/css/jquery-ui.multidatespicker.css'
--- media/css/jquery-ui.multidatespicker.css	1970-01-01 00:00:00 +0000
+++ media/css/jquery-ui.multidatespicker.css	2017-12-23 09:18:54 +0000
@@ -0,0 +1,12 @@
+/* jQuery UI Datepicker moving pixels fix */
+table.ui-datepicker-calendar {border-collapse: separate;}
+.ui-datepicker-calendar td {border: 1px solid transparent;}
+
+/* jQuery UI Datepicker hide datepicker helper */
+#ui-datepicker-div {display:none;}
+
+/* jQuery UI Datepicker emphasis on selected dates */
+.ui-datepicker .ui-datepicker-calendar .ui-state-highlight a {
+	background: #743620 none;
+	color: white;
+}
\ No newline at end of file

=== added file 'media/css/scheduling.css'
--- media/css/scheduling.css	1970-01-01 00:00:00 +0000
+++ media/css/scheduling.css	2017-12-23 09:18:54 +0000
@@ -0,0 +1,180 @@
+/* Main */
+.main-choices {
+    display: flex;
+    justify-content: space-around;
+    margin-top: 40px;
+}
+
+.main-choices button {
+    padding: 40px;
+    font-size: 1em;
+}
+
+/* actual scheduling module */
+#calandar {
+    display: flex;
+    justify-content: center;
+}
+
+/*  Hours of each day display for the user  */
+.day {
+    margin-bottom: 15px;
+    background-image: url("../img/black50.png");
+    padding: 10px;
+}
+
+.day-title h3{
+    color:white;
+    font-weight: bold;
+    margin-top: 0;
+}
+
+.hours-wrapper {
+    display: flex;
+}
+
+.hours-title-wrapper {
+    display: flex;
+    width: 100%;
+}
+
+.hours-title-wrapper p{
+    width: 42px;
+    user-select: none;
+}
+
+.hours {
+    border: 1px solid #909090;
+    height:40px;
+    width: 40px;
+    cursor: pointer;
+}
+
+.hours.selected {
+    background-color: #118811;
+}
+
+.hours:hover {
+    background-color: lightgreen;
+}
+
+.hidden-hour {
+    height: 40px;
+    width: 40px;
+}
+
+
+/*  Display for the other users  */
+#other-users-wrapper {
+    display: flex;
+    flex-wrap: wrap;
+    padding: 1%;
+}
+
+.other-user-div{
+    width: 29%;
+    border: 1px solid black;
+    background-image: url("../img/black50.png");
+    padding: 11px;
+    margin-top: 5px;
+    margin-right: 5px;
+}
+
+.other-user-div > div {
+    margin-top: 14px;
+}
+
+.other-user-div .hours{
+    height: 10px;
+    width: 20px;
+}
+
+.other-user-div .title {
+    height: 36px;
+    display: flex;
+    justify-content: space-between;
+    margin-top: 0;
+}
+
+.other-user-div .title p {
+    font-size: 20px;
+    margin-top: 5px;
+    text-transform: capitalize;
+    white-space: nowrap;
+    overflow: hidden !important;
+    text-overflow: ellipsis;
+    color: #feea72;;
+    font-weight: bold;
+}
+
+.other-user-div .title button {
+    min-width: 114px;
+}
+
+.other-user-div .title button:hover {
+    background-color:#118811;;
+}
+
+.other-user-div .title img {
+    margin-right: 4px;
+}
+
+
+/* btn */
+#validate-btn {
+    margin-top: 10px;
+}
+
+
+/**************************/
+/* CSS for the datepicker */
+/**************************/
+
+.ui-datepicker {
+    background-image: url("../img/wood.png");
+    border: 1px solid black;
+    border-radius: 4px;
+    box-shadow: 4px 4px 4px 0px rgba(0, 0, 0, 0.7);
+    padding: 0.8em;
+    display: none;
+}
+
+.ui-datepicker-header {
+    text-align: center;
+    border: 1px inset darkgray;
+    background-image: url("../img/black50.png");
+    margin-bottom: 5px;
+}
+
+.ui-datepicker-calendar {
+    border: 1px inset darkgray;
+    background-image: url("../img/black20.png");
+}
+
+.ui-datepicker-prev {
+    padding: 0 3em 0 0;
+    cursor: pointer;
+}
+ 
+.ui-datepicker-next {
+    padding: 0 0 0 3em;
+    cursor: pointer;
+}
+
+.ui-widget-content {
+    display: flex !important;
+    justify-content: space-around;
+    width: 540px !important;
+}
+
+.ui-datepicker-header {
+    height: 42px;
+    position: relative;
+}
+
+.ui-datepicker-title {
+    position: absolute;
+    transform: translateX(-50%);
+    left: 50%;
+    bottom: 0;
+}
\ No newline at end of file

=== added file 'media/js/jquery-ui.multidatespicker.js'
--- media/js/jquery-ui.multidatespicker.js	1970-01-01 00:00:00 +0000
+++ media/js/jquery-ui.multidatespicker.js	2017-12-23 09:18:54 +0000
@@ -0,0 +1,498 @@
+/*
+ * MultiDatesPicker v1.6.4
+ * http://multidatespickr.sourceforge.net/
+ * 
+ * Copyright 2014, Luca Lauretta
+ * Dual licensed under the MIT or GPL version 2 licenses.
+ */
+(function( $ ){
+	$.extend($.ui, { multiDatesPicker: { version: "1.6.4" } });
+	
+	$.fn.multiDatesPicker = function(method) {
+		var mdp_arguments = arguments;
+		var ret = this;
+		var today_date = new Date();
+		var day_zero = new Date(0);
+		var mdp_events = {};
+		
+		function removeDate(date, type) {
+			if(!type) type = 'picked';
+			date = dateConvert.call(this, date);
+			for(var i = 0; i < this.multiDatesPicker.dates[type].length; i++)
+				if(!methods.compareDates(this.multiDatesPicker.dates[type][i], date))
+					return this.multiDatesPicker.dates[type].splice(i, 1).pop();
+		}
+		function removeIndex(index, type) {
+			if(!type) type = 'picked';
+			return this.multiDatesPicker.dates[type].splice(index, 1).pop();
+		}
+		function addDate(date, type, no_sort) {
+			if(!type) type = 'picked';
+			date = dateConvert.call(this, date);
+			
+			// @todo: use jQuery UI datepicker method instead
+			date.setHours(0);
+			date.setMinutes(0);
+			date.setSeconds(0);
+			date.setMilliseconds(0);
+			
+			if (methods.gotDate.call(this, date, type) === false) {
+				this.multiDatesPicker.dates[type].push(date);
+				if(!no_sort) this.multiDatesPicker.dates[type].sort(methods.compareDates);
+			} 
+		}
+		function sortDates(type) {
+			if(!type) type = 'picked';
+			this.multiDatesPicker.dates[type].sort(methods.compareDates);
+		}
+		function dateConvert(date, desired_type, date_format) {
+			if(!desired_type) desired_type = 'object';/*
+			if(!date_format && (typeof date == 'string')) {
+				date_format = $(this).datepicker('option', 'dateFormat');
+				if(!date_format) date_format = $.datepicker._defaults.dateFormat;
+			}
+			*/
+			return methods.dateConvert.call(this, date, desired_type, date_format);
+		}
+		
+		var methods = {
+			init : function( options ) {
+				var $this = $(this);
+				this.multiDatesPicker.changed = false;
+				
+				var mdp_events = {
+					beforeShow: function(input, inst) {
+						this.multiDatesPicker.changed = false;
+						if(this.multiDatesPicker.originalBeforeShow) 
+							this.multiDatesPicker.originalBeforeShow.call(this, input, inst);
+					},
+					onSelect : function(dateText, inst) {
+						var $this = $(this);
+						this.multiDatesPicker.changed = true;
+						
+						if (dateText) {
+							$this.multiDatesPicker('toggleDate', dateText);
+							this.multiDatesPicker.changed = true;
+							// @todo: this will be optimized when I'll move methods to the singleton.
+						}
+						
+						if (this.multiDatesPicker.mode == 'normal' && this.multiDatesPicker.pickableRange) {
+							if(this.multiDatesPicker.dates.picked.length > 0) {
+								var min_date = this.multiDatesPicker.dates.picked[0],
+									max_date = new Date(min_date.getTime());
+								
+								methods.sumDays(max_date, this.multiDatesPicker.pickableRange-1);
+									
+								// counts the number of disabled dates in the range
+								if(this.multiDatesPicker.adjustRangeToDisabled) {
+									var c_disabled, 
+										disabled = this.multiDatesPicker.dates.disabled.slice(0);
+									do {
+										c_disabled = 0;
+										for(var i = 0; i < disabled.length; i++) {
+											if(disabled[i].getTime() <= max_date.getTime()) {
+												if((min_date.getTime() <= disabled[i].getTime()) && (disabled[i].getTime() <= max_date.getTime()) ) {
+													c_disabled++;
+												}
+												disabled.splice(i, 1);
+												i--;
+											}
+										}
+										max_date.setDate(max_date.getDate() + c_disabled);
+									} while(c_disabled != 0);
+								}
+								
+								if(this.multiDatesPicker.maxDate && (max_date > this.multiDatesPicker.maxDate))
+									max_date = this.multiDatesPicker.maxDate;
+								
+								$this
+									.datepicker("option", "minDate", min_date)
+									.datepicker("option", "maxDate", max_date);
+							} else {
+								$this
+									.datepicker("option", "minDate", this.multiDatesPicker.minDate)
+									.datepicker("option", "maxDate", this.multiDatesPicker.maxDate);
+							}
+						}
+						
+						if(this.multiDatesPicker.originalOnSelect && dateText)
+							this.multiDatesPicker.originalOnSelect.call(this, dateText, inst);
+						
+					},
+					beforeShowDay : function(date) {
+						var $this = $(this),
+							gotThisDate = $this.multiDatesPicker('gotDate', date) !== false,
+							isDisabledCalendar = $this.datepicker('option', 'disabled'),
+							isDisabledDate = $this.multiDatesPicker('gotDate', date, 'disabled') !== false,
+							areAllSelected = this.multiDatesPicker.maxPicks <= this.multiDatesPicker.dates.picked.length;
+						
+						var bsdReturn = [true, '', null];
+						if(this.multiDatesPicker.originalBeforeShowDay)
+							bsdReturn = this.multiDatesPicker.originalBeforeShowDay.call(this, date);
+						
+						bsdReturn[1] = gotThisDate ? 'ui-state-highlight '+bsdReturn[1] : bsdReturn[1];
+						bsdReturn[0] = bsdReturn[0] && !(isDisabledCalendar || isDisabledDate || (areAllSelected && !bsdReturn[1]));
+						return bsdReturn;
+					}
+				};
+				
+				// value have to be extracted before datepicker is initiated
+				if($this.val()) var inputDates = $this.val()
+				
+				if(options) {
+					// value have to be extracted before datepicker is initiated
+					//if(options.altField) var inputDates = $(options.altField).val();
+					if(options.separator) this.multiDatesPicker.separator = options.separator;
+					if(!this.multiDatesPicker.separator) this.multiDatesPicker.separator = ', ';
+					
+					this.multiDatesPicker.originalBeforeShow = options.beforeShow;
+					this.multiDatesPicker.originalOnSelect = options.onSelect;
+					this.multiDatesPicker.originalBeforeShowDay = options.beforeShowDay;
+					this.multiDatesPicker.originalOnClose = options.onClose;
+					
+					// datepicker init
+					$this.datepicker(options);
+					
+					this.multiDatesPicker.minDate = $.datepicker._determineDate(this, options.minDate, null);
+					this.multiDatesPicker.maxDate = $.datepicker._determineDate(this, options.maxDate, null);
+					if(options.addDates) methods.addDates.call(this, options.addDates);
+					 
+					if(options.addDisabledDates)
+						methods.addDates.call(this, options.addDisabledDates, 'disabled');
+					
+					methods.setMode.call(this, options);
+				} else {
+					$this.datepicker();
+				}
+				$this.datepicker('option', mdp_events);
+				
+				// adds any dates found in the input or alt field
+				if(inputDates) $this.multiDatesPicker('value', inputDates);
+				
+				// generates the new string of added dates
+				var inputs_values = $this.multiDatesPicker('value');
+				
+				// fills the input field back with all the dates in the calendar
+				$this.val(inputs_values);
+				
+				// Fixes the altField filled with defaultDate by default
+				var altFieldOption = $this.datepicker('option', 'altField');
+				if (altFieldOption) $(altFieldOption).val(inputs_values);
+				
+				// Updates the calendar view
+				$this.datepicker('refresh');
+			},
+			compareDates : function(date1, date2) {
+				date1 = dateConvert.call(this, date1);
+				date2 = dateConvert.call(this, date2);
+				// return > 0 means date1 is later than date2 
+				// return == 0 means date1 is the same day as date2 
+				// return < 0 means date1 is earlier than date2 
+				var diff = date1.getFullYear() - date2.getFullYear();
+				if(!diff) {
+					diff = date1.getMonth() - date2.getMonth();
+					if(!diff) 
+						diff = date1.getDate() - date2.getDate();
+				}
+				return diff;
+			},
+			sumDays : function( date, n_days ) {
+				var origDateType = typeof date;
+				obj_date = dateConvert.call(this, date);
+				obj_date.setDate(obj_date.getDate() + n_days);
+				return dateConvert.call(this, obj_date, origDateType);
+			},
+			dateConvert : function( date, desired_format, dateFormat ) {
+				var from_format = typeof date;
+				var $this = $(this);
+				
+				if(from_format == desired_format) {
+					if(from_format == 'object') {
+						try {
+							date.getTime();
+						} catch (e) {
+							$.error('Received date is in a non supported format!');
+							return false;
+						}
+					}
+					return date;
+				}
+				
+				if(typeof date == 'undefined') date = new Date(0);
+				
+				if(desired_format != 'string' && desired_format != 'object' && desired_format != 'number')
+					$.error('Date format "'+ desired_format +'" not supported!');
+				
+				if(!dateFormat) {
+					// thanks to bibendus83 -> http://sourceforge.net/tracker/index.php?func=detail&aid=3213174&group_id=358205&atid=1495382
+					var dp_dateFormat = $this.datepicker('option', 'dateFormat');
+					if (dp_dateFormat) {
+						dateFormat = dp_dateFormat;
+					} else {
+						dateFormat = $.datepicker._defaults.dateFormat;
+					}
+				}
+				
+				// converts to object as a neutral format
+				switch(from_format) {
+					case 'object': break;
+					case 'string': date = $.datepicker.parseDate(dateFormat, date); break;
+					case 'number': date = new Date(date); break;
+					default: $.error('Conversion from "'+ desired_format +'" format not allowed on jQuery.multiDatesPicker');
+				}
+				// then converts to the desired format
+				switch(desired_format) {
+					case 'object': return date;
+					case 'string': return $.datepicker.formatDate(dateFormat, date);
+					case 'number': return date.getTime();
+					default: $.error('Conversion to "'+ desired_format +'" format not allowed on jQuery.multiDatesPicker');
+				}
+				return false;
+			},
+			gotDate : function( date, type ) {
+				if(!type) type = 'picked';
+				for(var i = 0; i < this.multiDatesPicker.dates[type].length; i++) {
+					if(methods.compareDates.call(this, this.multiDatesPicker.dates[type][i], date) === 0) {
+						return i;
+					}
+				}
+				return false;
+			},
+			value : function( value ) {
+				if(value && typeof value == 'string') {
+					methods.addDates.call(this, value.split(this.multiDatesPicker.separator));
+				} else {
+					var dates = methods.getDates.call(this, 'string');
+					return dates.length
+						? dates.join(this.multiDatesPicker.separator)
+						: "";
+				}
+			},
+			getDates : function( format, type ) {
+				if(!format) format = 'string';
+				if(!type) type = 'picked';
+				switch (format) {
+					case 'object':
+						return this.multiDatesPicker.dates[type];
+					case 'string':
+					case 'number':
+						var o_dates = new Array();
+						for(var i in this.multiDatesPicker.dates[type])
+							o_dates.push(
+								dateConvert.call(
+									this, 
+									this.multiDatesPicker.dates[type][i], 
+									format
+								)
+							);
+						return o_dates;
+					
+					default: $.error('Format "'+format+'" not supported!');
+				}
+			},
+			addDates : function( dates, type ) {
+				if(dates.length > 0) {
+					if(!type) type = 'picked';
+					switch(typeof dates) {
+						case 'object':
+						case 'array':
+							if(dates.length) {
+								for(var i = 0; i < dates.length; i++)
+									addDate.call(this, dates[i], type, true);
+								sortDates.call(this, type);
+								break;
+							} // else does the same as 'string'
+						case 'string':
+						case 'number':
+							addDate.call(this, dates, type);
+							break;
+						default: 
+							$.error('Date format "'+ typeof dates +'" not allowed on jQuery.multiDatesPicker');
+					}
+					//$(this).datepicker('refresh');
+				} else {
+					$.error('Empty array of dates received.');
+				}
+			},
+			removeDates : function( dates, type ) {
+				if(!type) type = 'picked';
+				var removed = [];
+				if (Object.prototype.toString.call(dates) === '[object Array]') {
+					for(var i in dates.sort(function(a,b){return b-a})) {
+						removed.push(removeDate.call(this, dates[i], type));
+					}
+				} else {
+					removed.push(removeDate.call(this, dates, type));
+				}
+				return removed;
+			},
+			removeIndexes : function( indexes, type ) {
+				if(!type) type = 'picked';
+				var removed = [];
+				if (Object.prototype.toString.call(indexes) === '[object Array]') {
+					for(var i in indexes.sort(function(a,b){return b-a})) {
+						removed.push(removeIndex.call(this, indexes[i], type));
+					}
+				} else {
+					removed.push(removeIndex.call(this, indexes, type));
+				}
+				return removed;
+			},
+			resetDates : function ( type ) {
+				if(!type) type = 'picked';
+				this.multiDatesPicker.dates[type] = [];
+			},
+			toggleDate : function( date, type ) {
+				if(!type) type = 'picked';
+				
+				switch(this.multiDatesPicker.mode) {
+					case 'daysRange':
+						this.multiDatesPicker.dates[type] = []; // deletes all picked/disabled dates
+						var end = this.multiDatesPicker.autoselectRange[1];
+						var begin = this.multiDatesPicker.autoselectRange[0];
+						if(end < begin) { // switch
+							end = this.multiDatesPicker.autoselectRange[0];
+							begin = this.multiDatesPicker.autoselectRange[1];
+						}
+						for(var i = begin; i < end; i++) 
+							methods.addDates.call(this, methods.sumDays.call(this,date, i), type);
+						break;
+					default:
+						if(methods.gotDate.call(this, date) === false) // adds dates
+							methods.addDates.call(this, date, type);
+						else // removes dates
+							methods.removeDates.call(this, date, type);
+						break;
+				}
+			}, 
+			setMode : function( options ) {
+				var $this = $(this);
+				if(options.mode) this.multiDatesPicker.mode = options.mode;
+				
+				switch(this.multiDatesPicker.mode) {
+					case 'normal':
+						for(option in options)
+							switch(option) {
+								case 'maxPicks':
+								case 'minPicks':
+								case 'pickableRange':
+								case 'adjustRangeToDisabled':
+									this.multiDatesPicker[option] = options[option];
+									break;
+								//default: $.error('Option ' + option + ' ignored for mode "'.options.mode.'".');
+							}
+					break;
+					case 'daysRange':
+					case 'weeksRange':
+						var mandatory = 1;
+						for(option in options)
+							switch(option) {
+								case 'autoselectRange':
+									mandatory--;
+								case 'pickableRange':
+								case 'adjustRangeToDisabled':
+									this.multiDatesPicker[option] = options[option];
+									break;
+								//default: $.error('Option ' + option + ' does not exist for setMode on jQuery.multiDatesPicker');
+							}
+						if(mandatory > 0) $.error('Some mandatory options not specified!');
+					break;
+				}
+				
+				/*
+				if(options.pickableRange) {
+					$this.datepicker("option", "maxDate", options.pickableRange);
+					$this.datepicker("option", "minDate", this.multiDatesPicker.minDate);
+				}
+				*/
+				
+				if(mdp_events.onSelect)
+					mdp_events.onSelect();
+			},
+			destroy: function(){
+				this.multiDatesPicker = null;
+				$(this).datepicker('destroy');
+			}
+		};
+		
+		this.each(function() {
+			var $this = $(this);
+			if (!this.multiDatesPicker) {
+				this.multiDatesPicker = {
+					dates: {
+						picked: [],
+						disabled: []
+					},
+					mode: 'normal',
+					adjustRangeToDisabled: true
+				};
+			}
+			
+			if(methods[method]) {
+				var exec_result = methods[method].apply(this, Array.prototype.slice.call(mdp_arguments, 1));
+				switch(method) {
+					case 'removeDates':
+					case 'removeIndexes':
+					case 'resetDates':
+					case 'toggleDate':
+					case 'addDates':
+						var altField = $this.datepicker('option', 'altField');
+						// @todo: should use altFormat for altField
+						var dates_string = methods.value.call(this);
+						if (altField !== undefined && altField != "") {
+							$(altField).val(dates_string);
+						}
+						$this.val(dates_string);
+						
+						$.datepicker._refreshDatepicker(this);
+				}
+				switch(method) {
+					case 'removeDates':
+					case 'getDates':
+					case 'gotDate':
+					case 'sumDays':
+					case 'compareDates':
+					case 'dateConvert':
+					case 'value':
+						ret = exec_result;
+				}
+				return exec_result;
+			} else if( typeof method === 'object' || ! method ) {
+				return methods.init.apply(this, mdp_arguments);
+			} else {
+				$.error('Method ' +  method + ' does not exist on jQuery.multiDatesPicker');
+			}
+			return false;
+		}); 
+		
+		return ret;
+	};
+
+	var PROP_NAME = 'multiDatesPicker';
+	var dpuuid = new Date().getTime();
+	var instActive;
+
+	$.multiDatesPicker = {version: false};
+	//$.multiDatesPicker = new MultiDatesPicker(); // singleton instance
+	$.multiDatesPicker.initialized = false;
+	$.multiDatesPicker.uuid = new Date().getTime();
+	$.multiDatesPicker.version = $.ui.multiDatesPicker.version;
+	
+	// allows MDP not to hide everytime a date is picked
+	$.multiDatesPicker._hideDatepicker = $.datepicker._hideDatepicker;
+	$.datepicker._hideDatepicker = function(){
+		var target = this._curInst.input[0];
+		var mdp = target.multiDatesPicker;
+		if(!mdp || (this._curInst.inline === false && !mdp.changed)) {
+			return $.multiDatesPicker._hideDatepicker.apply(this, arguments);
+		} else {
+			mdp.changed = false;
+			$.datepicker._refreshDatepicker(target);
+			return;
+		}
+	};
+
+	// Workaround for #4055
+	// Add another global to avoid noConflict issues with inline event handlers
+	window['DP_jQuery_' + dpuuid] = $;
+})( jQuery );
\ No newline at end of file

=== added file 'media/js/scheduling.js'
--- media/js/scheduling.js	1970-01-01 00:00:00 +0000
+++ media/js/scheduling.js	2017-12-23 09:18:54 +0000
@@ -0,0 +1,287 @@
+// global variables
+var lastSelectedDates = []
+
+// Create calandar and return it as an object. Useful for callbacks.
+function createCalandar() {
+    $('#scheduling-datepicker').multiDatesPicker({
+        dateFormat: "yy-mm-dd",
+        showAnim: "slideDown",
+        numberOfMonths: 3,
+        minDate: 0,
+        onSelect: function(date) {
+            selectedDate = date;
+            updateAvailableDate(date)
+        },
+    });
+}
+
+
+//Add warning in case of disparency between browser and profil timezone
+function addTimeZoneWarningIfNeeded() {
+    var userTimeZone = document.getElementById('django-data').getAttribute('user-time-zone')
+    var browserTimeZone = - new Date().getTimezoneOffset()/60
+    if ( browserTimeZone != userTimeZone) {
+        document.getElementById('timezone-error').removeAttribute("hidden");
+        profilTime = document.getElementsByClassName('profil-time');
+        for (var element in profilTime) {
+            profilTime[element].innerHTML = cleanAndAddSign(userTimeZone);
+        }
+        document.getElementById('browser-time').innerHTML = cleanAndAddSign(browserTimeZone);
+    }
+}
+
+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)
+            updateAvailableDate(dateString)
+        }
+    }
+    $('#scheduling-datepicker').multiDatesPicker('addDates', dateToAdd);
+    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 = $( "#scheduling-datepicker" ).multiDatesPicker( "getDates" );
+    var selectedDatesList = [];
+    for (var d in selectedDates) {
+        //remove whitespace
+        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 = selectedDates[d] + "T" + addZeroIfUnderTen(h);
+                        selectedDatesList.push(hourAsDate);
+                    }
+                }
+            } 
+        }
+    }
+    //Send informations to server
+    console.log(selectedDatesList);
+    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");
+        newDate.style.display = "inline-block";
+        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
+        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"
+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

=== modified file 'settings.py'
--- settings.py	2017-11-24 09:24:41 +0000
+++ settings.py	2017-12-23 09:18:54 +0000
@@ -94,6 +94,7 @@
     'wlmaps',
     'wlscreens',
     'wlggz',
+    'wlscheduling',
     'check_input',
     'haystack', # search engine; see option HAYSTACK_CONNECTIONS
 

=== modified file 'templates/navigation.html'
--- templates/navigation.html	2017-10-28 18:18:18 +0000
+++ templates/navigation.html	2017-12-23 09:18:54 +0000
@@ -55,6 +55,7 @@
 			<li><a href="https://bugs.launchpad.net/widelands-website"; target="_blank">Website Bugtracker</a></li>
 		</ul>
 	</li>
+	<li><a href="{% url 'scheduling_main' %}">Scheduling</a></li>
 </ul>
 <div class="searchBox posRight">
 	<form method="post" action="/search/">

=== 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-12-23 09:18:54 +0000
@@ -0,0 +1,9 @@
+{% extends "base.html" %}
+{% block extra_head %}
+
+<link  rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/jquery-ui.multidatespicker.css" >
+<link  rel="stylesheet" type="text/css" href="{{ MEDIA_URL }}css/scheduling.css" >
+
+<script src="{{ MEDIA_URL }}js/jquery-ui.multidatespicker.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-12-23 09:18:54 +0000
@@ -0,0 +1,46 @@
+
+
+{% 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>
+    {% include "wlscheduling/timezone-msg.html" %}
+    <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. Don't hesitate to signal to other when you'll be available <a href="{% url 'scheduling_scheduling' %}">here</a></div>
+
+</div>
+
+<!-- TODO entirely in JS? -->
+<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 'templates/wlscheduling/main.html'
--- templates/wlscheduling/main.html	1970-01-01 00:00:00 +0000
+++ templates/wlscheduling/main.html	2017-12-23 09:18:54 +0000
@@ -0,0 +1,22 @@
+
+{% extends "wlscheduling/base.html" %}
+{% comment %}
+   vim:ft=htmldjango
+{% endcomment %}
+
+{% block content %}
+
+
+<div class="blogEntry">
+    <h1>Welcome in the scheduling module!</h1>
+    <div id="main-choices" class="main-choices">
+        <a href="{% url 'scheduling_scheduling' %}" >
+            <button title="You know when you will be available to play and want to display when you're available and find other players that will be available at these hours">Define your playtime and find a game</button>
+        </a>
+        <a href="{% url 'scheduling_find' %}" >
+            <button title="You just want to know when everybody is going to be playing" >Show other users playtime</button>
+        </a>
+    </div>
+</div>
+
+{% endblock %}
\ No newline at end of file

=== added file 'templates/wlscheduling/scheduling.html'
--- templates/wlscheduling/scheduling.html	1970-01-01 00:00:00 +0000
+++ templates/wlscheduling/scheduling.html	2017-12-23 09:18:54 +0000
@@ -0,0 +1,76 @@
+
+{% extends "wlscheduling/base.html" %}
+{% comment %}
+   vim:ft=htmldjango
+{% endcomment %}
+
+
+
+{% block content %}
+<script type="text/javascript">
+document.addEventListener('DOMContentLoaded', function(){ 
+    var calendar = createCalandar();
+    addTimeZoneWarningIfNeeded();
+    addPreviousDateFromUser(calendar);
+    addOtherUsersAvailabilities();
+    
+    //Validate btn
+    document.getElementById('validate-btn').onclick = function () {
+        sendDataAsForm(calendar);
+    }
+}, false);
+</script>
+
+<h1>Multiplayer</h1>
+
+<div class="blogEntry">
+    <h2>Select the dates you'll be available on</h2>
+    {% include "wlscheduling/timezone-msg.html" %}
+    <div id="scheduling-datepicker"></div>
+
+    <div id="second-step" hidden="hidden">
+        <h2>Select the hours you'll be available on these dates</h2>
+        <div id="days-wrapper"></div>
+        <h2>Players that are currently available at the same hours as you</h2>
+        <div id="other-users-wrapper"></div>
+        <div id="no-user-to-display" hidden="hidden">Sorry, there are currently no players available at these hours. But your availabilities are noted, and another user might want to play with you!</div>
+        <button id="validate-btn">Update</button>
+    </div>
+</div>
+
+<!-- Templates for the js script -->
+<!-- Template for the days of avaibility of the current user  -->
+<div id="day-template" class="day" hidden="hidden">
+    <div class="day-title"></div>
+    <div class="hours-title-wrapper">
+        {% for i in "xxxxxxxxxxxxxxxxxxxxxxxxx" %}
+            <p>{{ forloop.counter0 }}</p>
+        {% endfor %}
+    </div>
+    <div class="hours-wrapper">
+    {% for i in "xxxxxxxxxxxxxxxxxxxxxxxx" %}
+        <div class="hours"></div>
+    {% endfor %}
+        <div class="hidden-hour"></div>
+    </div>
+</div>
+
+<!-- Template for the days of avaibility of the other users  -->
+<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>
+
+<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}}" day-to-fill = "{{ current_user_availabilities }}" users-to-fill="{{other_users_availabilities}}" csrf-token = "{% csrf_token %}"></div>
+
+
+{% endblock %}

=== added file 'templates/wlscheduling/timezone-msg.html'
--- templates/wlscheduling/timezone-msg.html	1970-01-01 00:00:00 +0000
+++ templates/wlscheduling/timezone-msg.html	2017-12-23 09:18:54 +0000
@@ -0,0 +1,4 @@
+<h3 id="timezone-msg">You are currently noted as UTC <span class="profil-time"></span></h3>
+<div id="timezone-error" class="errormessage" hidden="hidden">WARNING! The time indicated by your browser (UTC <span id="browser-time"></span>) is different from the one stored in your profile (UTC <span class="profil-time"></span>). By default we will use the one stored in your profile.<br /> 
+    <a href="{% url 'profile_edit' %}">Edit Profile</a>
+</div>

=== modified file 'urls.py'
--- urls.py	2017-11-24 09:24:41 +0000
+++ urls.py	2017-12-23 09:18:54 +0000
@@ -66,6 +66,7 @@
     url(r'^screenshots/', include('wlscreens.urls')),
     url(r'^ggz/', include('wlggz.urls')),
     url(r'^moderated/', include('check_input.urls')),
+    url(r'^scheduling/', include('wlscheduling.urls')),
 ]
 
 try:

=== added directory 'wlscheduling'
=== added file 'wlscheduling/__init__.py'
=== added directory 'wlscheduling/migrations'
=== added file 'wlscheduling/migrations/0001_initial.py'
--- wlscheduling/migrations/0001_initial.py	1970-01-01 00:00:00 +0000
+++ wlscheduling/migrations/0001_initial.py	2017-12-23 09:18:54 +0000
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+from django.conf import settings
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Availabilities',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('avail_time', models.DateTimeField(default=0, verbose_name=b'one hour of availability')),
+                ('user', models.ForeignKey(related_name='availabilities', to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+    ]

=== added file 'wlscheduling/migrations/__init__.py'
=== added file 'wlscheduling/models.py'
--- wlscheduling/models.py	1970-01-01 00:00:00 +0000
+++ wlscheduling/models.py	2017-12-23 09:18:54 +0000
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+from django.db import models
+from django.contrib.auth.models import User
+
+
+class Availabilities(models.Model):
+    user = models.ForeignKey(User, related_name='availabilities')
+    avail_time = models.DateTimeField(
+        ('one hour of availability'), default=0)
\ No newline at end of file

=== added file 'wlscheduling/urls.py'
--- wlscheduling/urls.py	1970-01-01 00:00:00 +0000
+++ wlscheduling/urls.py	2017-12-23 09:18:54 +0000
@@ -0,0 +1,10 @@
+#!/usr/bin/env python -tt
+# encoding: utf-8
+from django.conf.urls import *
+from views import scheduling, scheduling_main, scheduling_find
+
+urlpatterns = [
+    url(r'^scheduling/$', scheduling, name='scheduling_scheduling'),
+    url(r'^main/$', scheduling_main, name='scheduling_main'),
+    url(r'^find/$', scheduling_find, name='scheduling_find'),
+]

=== added file 'wlscheduling/views.py'
--- wlscheduling/views.py	1970-01-01 00:00:00 +0000
+++ wlscheduling/views.py	2017-12-23 09:18:54 +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:
+            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):
+        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


Follow ups