← Back to team overview

widelands-dev team mailing list archive

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

 

Thanks :-) Looks better now.

I did a first round of proofreading and added some comments, mainly nits.

I am not sure if we should show the 'Scheduling' in the main menu, which has already many entrys. Since both links in that view needs logging in, i think showing 'Scheduling' should  also be only shown if a user has logged in? Not sure though.

I have no time left for now...

Diff comments:

> 
> === added file 'media/css/scheduling.css'
> --- media/css/scheduling.css	1970-01-01 00:00:00 +0000
> +++ media/css/scheduling.css	2018-01-17 19:07:26 +0000
> @@ -0,0 +1,230 @@
> +/* Main */
> +.main-choices {
> +    display: flex;
> +    justify-content: space-around;
> +    margin-top: 40px;
> +}
> +
> +.main-choices button {
> +    padding: 40px;
> +    font-size: 1em;
> +}
> +
> +/* actual scheduling module */
> +#calandar {

shouldn't this be calender?

> +    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;

Please add a space after the colon. Also on some places below.

> +    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;;

Double semi-colon

> +}
> +
> +.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;
> +}
> +
> +
> +
> +
> +/**********  clock **********/
> +
> +#clocks-wrapper {
> +    display:flex;
> +    height: 210px;
> +    margin-top: 20px;
> +    background-image: url("../img/black50.png");
> +}
> +
> +#clocks-wrapper h1{
> +    text-align: center;
> +}
> +
> +svg.clock-svg {
> +    width: 150px;
> +    height: 150px;
> +}
> +
> +.hour-area {
> +    stroke-linejoin:round;
> +    stroke-width:8;
> +    fill: #312925;
> +    stroke: black;
> +}
> +
> +.hour-area.active {
> +    fill: #118811;
> +}
> +
> +
> +.number-wrapper {
> +    line-height:100%;
> +    fill: white;
> +    
> +    stroke:black;
> +    stroke-width:2.7;
> +}
> +
> +.number {
> +    -inkscape-font-specification: "sans-serif";
> +    font-family:"sans-serif";
> +}
> +
> +.circle {
> +    fill: #312925;
> +}
> \ 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	2018-01-17 19:07:26 +0000
> @@ -0,0 +1,296 @@
> +// global variables
> +var lastSelectedDates = []
> +
> +// Create calandar and return it as an object. Useful for callbacks.
> +function createCalandar() {

calander -> calendar? In both lines above.

> +    $('#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

disparency -> difference?
Please also add a space after // and the comments text. Also on some other places. This improves readability.

> +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");
> +        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);
> +
> +    // for the current user hours display
> +    hourDiv = dateDiv.getElementsByClassName('hours');
> +    for (var hourInDiv in hourDiv) {
> +        if (hourInDiv == hour) {
> +            hourDiv[hourInDiv].className += ' selected';
> +        }
> +    }
> +
> +    // for the other users hours display (the svg clock)
> +    hoursArea = dateDiv.getElementsByClassName('hour-area');
> +    for (var hourArea in hoursArea) {
> +        if (hourArea == hour) {
> +            hoursArea[hourArea].className.baseVal += ' active';
> +        }
> +    }
> +}
> +
> +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
> 
> === added file 'templates/wlscheduling/find.html'
> --- templates/wlscheduling/find.html	1970-01-01 00:00:00 +0000
> +++ templates/wlscheduling/find.html	2018-01-17 19:07:26 +0000
> @@ -0,0 +1,37 @@
> +
> +
> +{% 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>
> +
> +<!-- Templates for the js script -->
> +{% include "wlscheduling/other-users-date.html" %}
> +
> +<div id="other-user-template" class="other-user-div" hidden="hidden">
> +    <div class="title"></div>
> +</div>
> +
> +
> +

Two empty lines. I think one empty line is enough? Same below.

> +<!-- 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/scheduling.html'
> --- templates/wlscheduling/scheduling.html	1970-01-01 00:00:00 +0000
> +++ templates/wlscheduling/scheduling.html	2018-01-17 19:07:26 +0000
> @@ -0,0 +1,70 @@
> +
> +{% extends "wlscheduling/base.html" %}
> +{% comment %}
> +   vim:ft=htmldjango
> +{% endcomment %}
> +
> +
> +
> +{% block content %}
> +<script type="text/javascript">
> +document.addEventListener('DOMContentLoaded', function(){ 
> +    var calendar = createCalandar();

createCalander -> createCalendar?

> +    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  -->
> +{% include "wlscheduling/other-users-date.html" %}
> +
> +<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 'wlscheduling/migrations/__init__.py'
> === added file 'wlscheduling/models.py'
> --- wlscheduling/models.py	1970-01-01 00:00:00 +0000
> +++ wlscheduling/models.py	2018-01-17 19:07:26 +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)

('one hour of availability') -> I do not understand what this is intended to do? Maybe should be:
help_text="one hour of availability" ?

> \ No newline at end of file


-- 
https://code.launchpad.net/~trimardio/widelands-website/module_scheduling/+merge/335570
Your team Widelands Developers is requested to review the proposed merge of lp:~trimardio/widelands-website/module_scheduling into lp:widelands-website.


References