Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

SECRET_KEY = ''
APP_SCRIPT_URL = 'https://script.google.com/macros/s/oooooooooooooooooooooooooo/exec'
DATABASE_URL = ''
DJANGO_DATABASE = 'default'
DJANGO_DATABASE = 'default'
DEBUG = 'False'
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,5 @@ fabric.properties


db-data
db
db
logs/
88 changes: 42 additions & 46 deletions HeroHours/admin.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
# Standard library imports
import csv
import json
from datetime import datetime
from types import SimpleNamespace

# Third-party imports
import django.contrib.auth.models as authModels
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.contrib.admin.utils import unquote
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied, BadRequest
from django.core.exceptions import PermissionDenied
from django.db.models import DurationField, ExpressionWrapper, F
from django.forms import model_to_dict
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.template.response import TemplateResponse
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.utils.text import capfirst
from HeroHours.forms import CustomActionForm
from . import models
from .models import Users, ActivityLog
from django.utils.translation import gettext_lazy as _
from rest_framework.authtoken.admin import TokenAdmin
from django.contrib.admin.utils import (unquote)
from django.contrib.admin.options import get_content_type_for_model
from django.template.response import TemplateResponse

# Local imports
from . import models
from .forms import CustomActionForm
from .models import ActivityLog, Users
# Register your models here.


Expand All @@ -31,18 +34,21 @@ def check_out(modeladmin, request, queryset):
getall = queryset.filter(Checked_In=True)
updated_users = []
updated_log = []
right_now = timezone.now()
for user in getall:
lognew = models.ActivityLog(
user_id=user.User_ID,
entered=user.User_ID,
operation='Check Out',
status='Success', # Initial status
status='Success',
)
user.Checked_In = False
user.Total_Hours = (
datetime.combine(datetime.today(), user.Total_Hours) + (timezone.now() - user.Last_In)).time()
#print((timezone.now() - user.Last_In).total_seconds())
user.Total_Seconds += round((timezone.now() - user.Last_In).total_seconds())
user.Last_Out = timezone.now()
if not user.Last_In:
user.Last_In = right_now
user.Total_Hours = ExpressionWrapper(F('Total_Hours') + (right_now - user.Last_In),
output_field=DurationField())
user.Total_Seconds = F('Total_Seconds') + round((right_now - user.Last_In).total_seconds())
user.Last_Out = right_now
updated_log.append(lognew)
updated_users.append(user)
models.Users.objects.bulk_update(updated_users, ["Checked_In", "Total_Hours", "Total_Seconds", "Last_Out"])
Expand All @@ -53,18 +59,20 @@ def check_out(modeladmin, request, queryset):
def check_in(modeladmin, request, queryset):
updated_users = []
updated_log = []
right_now = timezone.now()
getall = queryset.filter(Checked_In=False)
for user in getall:
lognew = models.ActivityLog(
user_id=user.User_ID,
entered=user.User_ID,
operation='Check In',
status='Success', # Initial status
status='Success',
)
user.Checked_In = True
user.Last_In = timezone.now()
user.Last_In = right_now
updated_log.append(lognew)
updated_users.append(user)
models.Users.objects.bulk_update(updated_users, ["Checked_In", "Total_Hours", "Total_Seconds", "Last_Out"])
models.Users.objects.bulk_update(updated_users, ["Checked_In", "Last_In"])
models.ActivityLog.objects.bulk_create(updated_log)

@admin.action(description="Reset Members")
Expand All @@ -74,8 +82,9 @@ def reset(modeladmin, request, queryset):
for user in queryset:
lognew = models.ActivityLog(
user_id=user.User_ID,
entered=user.User_ID,
operation='Reset',
status='Success', # Initial status
status='Success',
)
user.Total_Seconds = 0
user.Total_Hours = '0:00:00'
Expand All @@ -86,6 +95,7 @@ def reset(modeladmin, request, queryset):
updated_log.append(
models.ActivityLog(
user_id=user.User_ID,
entered=user.User_ID,
operation='Check Out',
status='Success',
)
Expand All @@ -95,8 +105,8 @@ def reset(modeladmin, request, queryset):
models.Users.objects.bulk_update(updated_users, ["Checked_In", "Total_Hours", "Total_Seconds", "Last_Out", "Last_In"])
models.ActivityLog.objects.bulk_create(updated_log)

@admin.action(description="Create a Staff User")
def create_staff_user_action(modeladmin, request, queryset):
print(request)
selected_user = queryset.first()
userdata = model_to_dict(selected_user)

Expand All @@ -105,9 +115,6 @@ def create_staff_user_action(modeladmin, request, queryset):
return render(request, 'admin/custom_action_form.html', {'form': form})


create_staff_user_action.short_description = "Create a Staff User"


class TotalHoursFilter(SimpleListFilter):
title = _('Total Hours') # Display title in the admin filter sidebar
parameter_name = 'total_hours' # URL parameter
Expand All @@ -121,7 +128,7 @@ def lookups(self, request, model_admin):
('25hours', _('Less than 25 hours')),

('o25hours', _('Over 25 hours')),
('o50hours',_('Over 50 hours'))
('o50hours', _('Over 50 hours'))
]

def queryset(self, request, queryset):
Expand Down Expand Up @@ -152,12 +159,12 @@ def export_as_csv(self, request, queryset):
field_names = [field.name for field in meta.fields]

response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
response['Content-Disposition'] = f'attachment; filename={meta}.csv'
writer = csv.writer(response)

writer.writerow(field_names)
for obj in queryset:
row = writer.writerow([getattr(obj, field) for field in field_names])
writer.writerow([getattr(obj, field) for field in field_names])

return response

Expand All @@ -170,12 +177,10 @@ class MemberAdmin(admin.ModelAdmin):
search_fields = ['User_ID', 'Last_Name', 'First_Name']
list_filter = ['Checked_In', TotalHoursFilter]

@admin.display(description="Total Hours", ordering="Total_Seconds")
def display_total_hours(self, obj):
return obj.get_total_hours()

display_total_hours.short_description = "Total Hours"
display_total_hours.admin_order_field = "Total_Seconds"


"""
Custom history view, modified from Django source
Expand Down Expand Up @@ -272,31 +277,28 @@ class ActivityAdminView(admin.ModelAdmin):
search_fields = ['timestamp']
actions = [export_as_csv]

@admin.display(description='Date')
def get_date_only(self, obj):
return timezone.localtime(obj.timestamp).date()

@admin.display(description='Entered')
def get_entered_data(self, obj):
return obj.entered
get_entered_data.short_description = 'Entered'


get_date_only.short_description = 'Date'

@admin.display(description='Name')
def get_name(self, obj):
if obj.user:
return f'{obj.user.First_Name} {obj.user.Last_Name}'
return 'None'
get_name.short_description = 'Name'
return 'None'

@admin.display(description='Status')
def get_status(self, obj):
return obj.status

get_status.short_description = 'Status'

@admin.display(description='Operation')
def get_op(self, obj):
return obj.operation

get_op.short_description = 'Operation'


def is_superuser(user):
return user.is_superuser
Expand All @@ -306,17 +308,14 @@ def is_superuser(user):
def add_user(request):
form_data_dict = request.POST.dict()
form_data = SimpleNamespace(**form_data_dict)
print(form_data)
username = form_data.username
password = form_data.password
hidden_data = json.loads(form_data.hidden_data)
fname = hidden_data['First_Name']
lname = hidden_data['Last_Name']
group_name = form_data.group_name

if authModels.User.objects.filter(username=username).exists():
print('User already exists')
else:
if not authModels.User.objects.filter(username=username).exists():
user = authModels.User.objects.create_user(username=username,
first_name=fname,
last_name=lname)
Expand All @@ -325,11 +324,8 @@ def add_user(request):
user.save()

group = authModels.Group.objects.get(name=group_name)
print(group)
user.groups.add(group)

print('nicely done')

return redirect('/admin/')


Expand Down
2 changes: 1 addition & 1 deletion HeroHours/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

class HeroHoursConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'HeroHours'
name = 'HeroHours'
48 changes: 38 additions & 10 deletions HeroHours/consumers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import json

from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.decorators import permission_required
from djangochannelsrestframework.decorators import action
from djangochannelsrestframework.observer import model_observer
from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin
from djangochannelsrestframework.permissions import WrappedDRFPermission, IsAuthenticated
from djangochannelsrestframework.permissions import IsAuthenticated
from rest_framework import serializers
from rest_framework.permissions import DjangoModelPermissions

from . import models
from djangochannelsrestframework import permissions
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from djangochannelsrestframework.mixins import (
ListModelMixin, RetrieveModelMixin,
Expand All @@ -21,10 +14,19 @@


class MemberSerializer(serializers.ModelSerializer):
"""Serializer for Users model used in WebSocket communications."""
class Meta:
model = Users
fields = ['User_ID', 'First_Name', 'Last_Name', 'Checked_In', 'Total_Seconds', 'Last_In', 'Last_Out']


class LiveConsumer(ObserverModelInstanceMixin, RetrieveModelMixin, ListModelMixin, GenericAsyncAPIConsumer):
"""
WebSocket consumer for real-time member updates.

Provides live updates when members check in/out via the update_activity observer.
Requires authentication to connect.
"""
queryset = Users.objects.all().order_by('Last_Name','First_Name')
serializer_class = MemberSerializer
permission_classes = [IsAuthenticated]
Expand All @@ -37,11 +39,31 @@ async def update_activity(
subscribing_request_ids=[],
**kwargs
):
"""
Observer that sends updates when Users model changes.

Args:
message: Serialized user data
observer: Observer instance
subscribing_request_ids: List of request IDs subscribed to updates
"""
await self.send_json({'data':message.data, 'request_ids':subscribing_request_ids})

@update_activity.serializer
def update_activity(self, instance: Users, action, **kwargs) -> MemberSerializer:
"""This will return the comment serializer"""
"""
Serializer for the update_activity observer.

Refreshes instance from database if any field contains F() expressions
to ensure accurate data is sent to subscribers.

Args:
instance: Users model instance that changed
action: Type of change (create, update, delete)

Returns:
MemberSerializer: Serialized user data
"""
for field in instance._meta.fields:
if isinstance(getattr(instance, field.name), BaseExpression):
instance.refresh_from_db()
Expand All @@ -50,4 +72,10 @@ def update_activity(self, instance: Users, action, **kwargs) -> MemberSerializer

@action()
async def subscribe_all(self, request_id, **kwargs):
await self.update_activity.subscribe(request_id=request_id)
"""
Action to subscribe to all Users model updates.

Args:
request_id: Unique request identifier for this subscription
"""
await self.update_activity.subscribe(request_id=request_id)
3 changes: 1 addition & 2 deletions HeroHours/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@


class CustomActionForm(forms.Form):
print("running form")
username = forms.CharField(label='Username', max_length=100)
password = forms.CharField(label='Password', widget=forms.PasswordInput)
group_name = forms.ChoiceField(label='Group Name', required=True)
Expand All @@ -16,4 +15,4 @@ def __init__(self, *args, **kwargs):

self.fields['group_name'].choices = group_choices # Set the choices for the field

hidden_data = forms.CharField(widget=forms.HiddenInput(), required=False)
hidden_data = forms.CharField(widget=forms.HiddenInput(), required=False)
22 changes: 10 additions & 12 deletions HeroHours/management/commands/bulk.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
import csv
import datetime
from datetime import datetime
from django.core.management.base import BaseCommand
from HeroHours.models import Users
from HeroHours.views import handle_bulk_updates # Replace 'yourapp' with your actual app name
from HeroHours.views import handle_bulk_updates

class Command(BaseCommand):
help = 'Run a comand at a specific time'
help = 'Run a command at a specific time'

def add_arguments(self, parser):
parser.add_argument('userID', type=str, help='The userID/Command to run')
parser.add_argument('time', type=str, help='The time to use')
parser.add_argument('time', type=str, help='The time to use (format: YYYY MM DD HH MM)')

def handle(self, *args, **options):
userID = options["userID"]
time_string = options["time"].split()
year = time_string[0]
month = time_string[1]
day = time_string[2]
hour = time_string[3]
minute = time_string[4]
handle_bulk_updates(user_id=userID,time=datetime(year,month,day,hour,minute))
year = int(time_string[0])
month = int(time_string[1])
day = int(time_string[2])
hour = int(time_string[3])
minute = int(time_string[4])
handle_bulk_updates(user_id=userID, at_time=datetime(year, month, day, hour, minute))

Loading