Skip to content

Commit

Permalink
add: blinking and unknown
Browse files Browse the repository at this point in the history
  • Loading branch information
Friedjof committed Oct 14, 2024
1 parent 61e80f5 commit 43b6368
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 69 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ RUN chmod +x /app/start.sh
EXPOSE 8000

# Start the application using the start.sh script
CMD ["/app/start.sh"]
CMD ["/app/start.sh"]
1 change: 1 addition & 0 deletions LaundryTracker/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
STATIC_ROOT = BASE_DIR / 'staticfiles'
else:
STATICFILES_DIRS = []
STATIC_ROOT = BASE_DIR / 'static'
Expand Down
8 changes: 0 additions & 8 deletions LaundryTracker/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,4 @@

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'LaundryTracker.settings')

# Import and run the command
from django.core.management import call_command

try:
call_command('clear_old_tasks')
except Exception as e:
print(f'Error while clearing old tasks: {e}')

application = get_wsgi_application()
2 changes: 1 addition & 1 deletion cron/update
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# update (Crontab-File)
* * * * * root python /app/manage.py update >> /var/log/cron.log 2>&1
* * * * * export SQLITE_PATH=/data/db.sqlite3 && /usr/local/bin/python /app/manage.py update >> /var/log/cron.log 2>&1
54 changes: 38 additions & 16 deletions static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,49 @@ footer {
height: 80px;
margin: 10px;
border-radius: 50%;
border: 2px solid black; /* Black border */
border: 2px solid black;
display: inline-block;
text-align: center;
line-height: 80px;
font-size: 18px;
color: black;
cursor: pointer;
}
.machine-button.off {
background-color: #6d6e53; /* Green color for available machines */
color: white; /* White text for better contrast */

@keyframes slow-blink {
0%, 100% {
box-shadow: 0 0 20px 2px #f37933;
}
50% {
box-shadow: 0 0 5px 1px #f37933;
}
}

.machine-button.A {
background-color: #6d6e53;
color: white;
font-weight: bold;
}
.machine-button.on {
background-color: #baefba; /* Muted grey for finished machines */
.machine-button.F {
background-color: #baefba;
}
.machine-button.running {
background-color: #f37933; /* Softer yellow for running machines */
.machine-button.R {
background-color: #f37933;
box-shadow: 0 0 20px 2px #f37933;
animation: slow-blink 2s infinite;
}
.machine-button.defect {
background-color: #dc3545; /* Light grey for defect machines */
color: #a9a9a9; /* Dark grey text */
cursor: not-allowed; /* Change cursor to indicate non-interactive */
.machine-button.D {
background-color: #dc3545;
color: #a9a9a9;
cursor: not-allowed;
}
.machine-button.B {
background-color: #f0c66e;
color: #616161;
cursor: not-allowed;
animation: slow-blink 2s infinite;
}

.machine-button-label {
font-size: 14px;
color: #003f5c; /* Darker color for text */
Expand All @@ -62,22 +80,26 @@ footer {
padding-bottom: 5px;
}

.machine-header.off {
.machine-header.A {
border-bottom-color: #4caf50; /* Green color for available machines */
}

.machine-header.running {
.machine-header.R {
border-bottom-color: #ff9800; /* Orange color for running machines */
}

.machine-header.on {
.machine-header.F {
border-bottom-color: #2196f3; /* Blue color for finished machines */
}

.machine-header.defect {
.machine-header.D {
border-bottom-color: #f44336; /* Red color for defect machines */
}

.machine-header.B {
border-bottom-color: #ffc107; /* Yellow color for blocked machines */
}

.container {
text-align: center;
margin-top: 50px;
Expand Down
82 changes: 64 additions & 18 deletions static/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ document.addEventListener('DOMContentLoaded', function () {
timerModalLabel.innerText = `Machine ${machineNumber} (${getMachineTypeDisplay(machineType)}) is already running`;
} else if (machineStatus === 'D') {
timerModalLabel.innerText = `Machine ${machineNumber} (${getMachineTypeDisplay(machineType)}) is defect`;
} else if (machineStatus === 'B') {
timerModalLabel.innerText = `Machine ${machineNumber} (${getMachineTypeDisplay(machineType)}) is blinking`;
} else if (machineStatus === 'U') {
timerModalLabel.innerText = `Machine ${machineNumber} (${getMachineTypeDisplay(machineType)}) is unknown`;
} else {
timerModalLabel.innerText = `Machine ${machineNumber} (${getMachineTypeDisplay(machineType)})`;
}
Expand All @@ -39,7 +43,14 @@ document.addEventListener('DOMContentLoaded', function () {
const isDefectNoteLabel = document.getElementById('isDefectNoteLabel');
if (defectLink && isDefectNote && isDefectNoteLabel) {
if (machineStatus === 'D') {
defectLink.innerText = `${getMachineTypeDisplay(machineType)} is defect`;
defectLink.innerText = `Mark ${getMachineTypeDisplay(machineType)} as repaired`;
defectLink.style.color = 'green';

// hide defect note
isDefectNote.style.display = 'none';
isDefectNoteLabel.style.display = 'none';
} else if (machineStatus === 'B') {
defectLink.innerText = `Mark ${getMachineTypeDisplay(machineType)} as not blinking`;
defectLink.style.color = 'green';

// hide defect note
Expand All @@ -64,9 +75,15 @@ document.addEventListener('DOMContentLoaded', function () {
if (machineStatus === 'D') {
document.getElementById('setDefectBtn').style.display = 'none';
document.getElementById('repairBtn').style.display = 'block';
document.getElementById('isBlinkingBtn').innerText = 'Mark as blinking';
} else if (machineStatus === 'B') {
document.getElementById('setDefectBtn').style.display = 'none';
document.getElementById('repairBtn').style.display = 'none';
document.getElementById('isBlinkingBtn').innerText = 'Mark as available';
} else {
document.getElementById('setDefectBtn').style.display = 'block';
document.getElementById('repairBtn').style.display = 'none';
document.getElementById('isBlinkingBtn').innerText = 'Machine is blinking';
}

const timerDuration = document.getElementById('timerDuration');
Expand Down Expand Up @@ -216,6 +233,36 @@ document.addEventListener('DOMContentLoaded', function () {
});
});

document.getElementById('isBlinkingBtn').addEventListener('click', function() {
const machineId = document.getElementById('machineId').value;
const buildingId = document.getElementById('building_id').value;
const csrfToken = document.getElementById('csrfToken').value;

fetch(`/${buildingId}/laundry/${machineId}/blinking/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrfToken
},
body: JSON.stringify({ machine_id: machineId })
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
updateMachineStatus();
const modalInstance = bootstrap.Modal.getInstance(document.getElementById('timerModal'));
if (modalInstance) {
modalInstance.hide();
}
} else {
console.error('Failed to set machine as blinking');
}
})
.catch(error => {
console.error('Error:', error);
});
});

document.getElementById('defectLink').addEventListener('click', function(event) {
event.preventDefault();
const form = document.getElementById('defectForm');
Expand Down Expand Up @@ -254,8 +301,8 @@ document.addEventListener('DOMContentLoaded', function () {
// Update machine button class
const machineButton = document.getElementById(`button-${machine.identifier}`);
if (machineButton) {
machineButton.classList.remove('off', 'running', 'on', 'defect');
machineButton.classList.add(getMachineClass(machine.machine_status));
machineButton.classList.remove('A', 'R', 'F', 'D', 'B', 'U');
machineButton.classList.add(machine.machine_status);
machineButton.setAttribute('data-machine-status', machine.machine_status);
machineButton.setAttribute('data-machine-notes', machine.notes);
machineButton.title = getMachineTitle(machine.machine_status, machine.notes);
Expand All @@ -266,8 +313,8 @@ document.addEventListener('DOMContentLoaded', function () {
// border color
const machineHeader = document.getElementById(`header-${machine.identifier}`);
if (machineHeader) {
machineHeader.classList.remove('off', 'running', 'on', 'defect');
machineHeader.classList.add(getMachineClass(machine.machine_status));
machineHeader.classList.remove('R', 'F', 'D', 'B', 'U');
machineHeader.classList.add(machine.machine_status);
} else {
console.error(`Element with ID header-${machine.identifier} not found`);
}
Expand All @@ -285,17 +332,8 @@ document.addEventListener('DOMContentLoaded', function () {

// Helper function to get the machine type display
function getMachineTypeDisplay(type) {
if (type === 'W') return 'Washer';
if (type === 'D') return 'Dryer';
return '';
}

// Helper function to get the CSS class for machine status
function getMachineClass(status) {
if (status === 'A') return 'off';
if (status === 'R') return 'running';
if (status === 'F') return 'on';
if (status === 'D') return 'defect';
if (type === 'W') return 'washer';
if (type === 'D') return 'dryer';
return '';
}

Expand All @@ -304,6 +342,8 @@ document.addEventListener('DOMContentLoaded', function () {
if (status === 'A') return 'click here';
if (status === 'F') return 'done';
if (status === 'D') return 'defect';
if (status === 'B') return 'blinking';
if (status === 'U') return 'unknown';
return '';
}

Expand All @@ -313,14 +353,18 @@ document.addEventListener('DOMContentLoaded', function () {
if (status === 'R') return '<span class="badge bg-warning">Running</span>';
if (status === 'F') return '<span class="badge bg-primary">Finished</span>';
if (status === 'D') return '<span class="badge bg-danger">Defect</span>';
if (status === 'B') return '<span class="badge bg-warning">Blinking</span>';
if (status === 'U') return '<span class="badge bg-secondary">Unknown</span>';
return '';
}

// Helper function to get the end time or last used text
// Helper function to get the end time text
function getEndTimeText(status, endTime) {
if (status === 'F') return `Finished at:<br>${endTime}`;
if (status === 'R') return `End time:<br>${endTime}`;
return `Last used:<br>${endTime}`;
if (status === 'D') return `Defect since:<br>${endTime}`;
if (status === 'B') return `Will be available soon:<br>${endTime}`;
return `Updated at:<br>${endTime}`;
}

// Helper function to get the correct machine title
Expand All @@ -330,6 +374,8 @@ document.addEventListener('DOMContentLoaded', function () {
if (status === 'A') return 'Click here to start machine';
if (status === 'F') return 'Machine is available';
if (status === 'D') return `Machine is defect: ${note}`;
if (status === 'B') return 'Machine is blinking';
if (status === 'U') return 'Machine status is unknown';
return '';
}

Expand Down
49 changes: 45 additions & 4 deletions timer/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.contrib import admin
from django.conf import settings
from django.utils import timezone
from django.utils.html import format_html
from django.contrib.auth.models import Group
from django.apps import AppConfig
Expand Down Expand Up @@ -33,7 +34,7 @@ class Meta:
@admin.register(Building)
class BuildingAdmin(ImportExportModelAdmin):
resource_class = BuildingResource
list_display = ('identifier', 'name', 'building_link')
list_display = ('identifier', 'name', 'building_link', 'machine_count')

def building_link(self, obj):
protocol = 'https' if settings.TLS_ACTIVE else 'http'
Expand All @@ -51,6 +52,11 @@ def get_queryset(self, request):

return Building.objects.none()

def machine_count(self, obj):
return obj.machine_set.count()

machine_count.short_description = 'Number of Machines'

building_link.short_description = 'Building Link'

class BuildingFilter(admin.SimpleListFilter):
Expand Down Expand Up @@ -83,10 +89,12 @@ class Meta:
export_order = ('identifier', 'number', 'building', 'machine_type', 'machine_status', 'timer', 'timer_start', 'notes', 'notes_date')


from datetime import timedelta

@admin.register(Machine)
class MachineAdmin(ImportExportModelAdmin):
resource_class = MachineResource
list_display = ('number', 'building', 'machine_type', 'machine_status', 'timer_minutes', 'timer_start', 'notes', 'notes_date')
list_display = ('number', 'building_link', 'machine_type', 'machine_status', 'timer_minutes', 'timer_start', 'remaining_time', 'notes', 'notes_date')
list_filter = (BuildingFilter, 'machine_type', 'machine_status')
search_fields = ('number', 'building__name', 'machine_type')
ordering = ('number',)
Expand All @@ -101,15 +109,40 @@ class MachineAdmin(ImportExportModelAdmin):
}),
)

def building_link(self, obj):
protocol = 'https' if settings.TLS_ACTIVE else 'http'
domain = settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost'
url = f"{protocol}://{domain}/{obj.building.identifier}"
return format_html('<a href="{}" target="_blank">{}</a>', url, obj.building.name)

building_link.short_description = 'Building'

def timer_minutes(self, obj):
if obj.timer is None:
return 'None'
elif obj.timer == 1:
return f"{obj.timer} minute"
else:
return f"{obj.timer} minutes"

timer_minutes.short_description = 'Timer (minutes)'

def remaining_time(self, obj):
if obj.timer_start and obj.timer:
end_time = obj.timer_start + timedelta(minutes=obj.timer)
remaining = end_time - timezone.now()

if remaining.total_seconds() > 0:
return str(remaining).split('.')[0]

if obj.machine_status == 'R':
return 'Expired (not updated)'

return 'Expired'
return 'Undefined' if obj.timer != 0 else 'Expired'

remaining_time.short_description = 'Remaining Time'

def get_queryset(self, request):
if request.user.is_superuser:
return super().get_queryset(request)
Expand All @@ -131,16 +164,24 @@ class Meta:
@admin.register(BuildingAssignment)
class BuildingAssignmentAdmin(ImportExportModelAdmin):
resource_class = BuildingAssignmentResource
list_display = ('user', 'building')
list_display = ('user', 'building_link')
list_filter = ('building',)
search_fields = ('user__username', 'building__name')
ordering = ('user', 'building')

def building_link(self, obj):
protocol = 'https' if settings.TLS_ACTIVE else 'http'
domain = settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost'
url = f"{protocol}://{domain}/{obj.building.identifier}"
return format_html('<a href="{}" target="_blank">{}</a>', url, obj.building.name)

building_link.short_description = 'Building'

def get_queryset(self, request):
if request.user.is_superuser:
return super().get_queryset(request)

if request.user.groups.filter(name='Moderator').exists():
return BuildingAssignment.objects.filter(user=request.user)

return BuildingAssignment.objects.none()
return BuildingAssignment.objects.none()
Loading

0 comments on commit 43b6368

Please sign in to comment.