Compare commits

..

5 Commits

Author SHA1 Message Date
bfc2950a1d postgres 2025-04-06 15:28:56 -04:00
8c9bc213b0 merge 2025-04-06 13:47:21 -04:00
e288cd3f31 image update 2025-04-06 13:39:48 -04:00
10db56805f Merge pull request 'db-update' (#1) from add-mad-to-db into api-first
Reviewed-on: #1
Reviewed-by: dominic <me@domdit.com>
2025-04-03 20:00:12 -04:00
794bf82a1c db-update 2025-04-03 20:01:19 -04:00
41 changed files with 219 additions and 34 deletions

6
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*.pyc
# C extensions
*.so
@ -51,3 +52,8 @@ docs/_build/
*__pycache__/*
*.pyc
run.sh
venv/
*.bak
scratch*

Binary file not shown.

Binary file not shown.

View File

@ -82,8 +82,12 @@ WSGI_APPLICATION = 'korabo.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASS'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT'),
}
}
@ -131,3 +135,6 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

View File

@ -1,6 +1,7 @@
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from api.views.event import EventView
from api.views.event_division import EventDivisionView
from api.views.event_comment import EventCommentView
@ -16,7 +17,10 @@ urlpatterns = [
path('api/event_division/', EventDivisionView.as_view(), name='event_division'),
path("", views.index, name="index"),
path("accounts/", include("django.contrib.auth.urls")),
#path("event/<int:event_id>", views.event, name="event"),
#path('change_password/', views.change_password, name='change_password'),
#path('password_change_done/', views.password_change_done, name='password_change_done')
path("event/<int:event_id>", views.event, name="event"),
path('change_password/', views.change_password, name='change_password'),
path('password_change_done/', views.password_change_done, name='password_change_done')
]
if settings.DEBUG: # new
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -12,3 +12,4 @@ requests==2.32.3
soupsieve==2.6
sqlparse==0.5.1
urllib3==2.3.0
pillow==11.1.0

Binary file not shown.

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.16 on 2025-04-06 01:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0004_remove_event_participants_alter_eventcomment_event_and_more'),
]
operations = [
migrations.AddField(
model_name='event',
name='cover_image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='event_cover_images/'),
),
]

View File

@ -10,6 +10,7 @@ class Event(BaseModel):
id = models.AutoField(primary_key=True)
name = models.TextField(blank=False, null=False)
description = models.TextField(blank=False, null=False)
cover_image = models.ImageField(blank=True, null=True, default=None, upload_to='event_cover_images/')
division = models.ForeignKey(EventDivision, on_delete=models.CASCADE)
start_date = models.DateTimeField(blank=False, null=False)
end_date = models.DateTimeField(blank=False, null=False)

View File

@ -20,6 +20,9 @@
</div>
<div style="float:right;">
<a href="{% url 'index' %}">home</a> |
<a href="{% url 'change_password' %}">change password</a> |
<a href="{% url 'logout' %}">logout</a> |
{% if request.user.is_authenticated %}Hello {{ request.user.username }} {% endif %}
</div>
@ -35,4 +38,4 @@
</div>
</body>
</html>
</html>

View File

@ -1,28 +1,36 @@
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="accordion">
{% for event in events%}
<div class="accordion-item">
<div class="accordion-item">
<h2 class="accordion-header" id="event-{{event.id}}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#event-collapse-{{event.id}}" aria-expanded="true" aria-controls="event-collapse-{{event.id}}">
{{event.name}}
</button>
</h2>
<div id="event-collapse-{{event.id}}" class="accordion-collapse collapse" aria-labelledby="event-{{event.id}}" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{event.description}}
<table class="table">
<thead>
<tr>
<td>Event Name</td>
<td>Already Responded</td>
<td>Start Date</td>
<td>End Date</td>
<td>img</td>
</tr>
</thead>
<tbody>
{% for event in events %}
<div class="accordion-item">
<div class="accordion-item">
<h2 class="accordion-header" id="event-{{ event.id }}">
<button class="accordion-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#event-collapse-{{ event.id }}"
aria-expanded="true"
aria-controls="event-collapse-{{ event.id }}">{{ event.name }}</button>
</h2>
<div id="event-collapse-{{ event.id }}"
class="accordion-collapse collapse"
aria-labelledby="event-{{ event.id }}"
data-bs-parent="#accordionExample">
<div class="accordion-body">{{ event.description }}</div>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -1,7 +1,6 @@
from datetime import timedelta
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordChangeForm
from django.shortcuts import get_object_or_404, redirect, render
from scripts.bp_deactivate import deactivate
@ -10,10 +9,148 @@ from web.models import Event, Availability
@login_required()
def index(request):
user = User.objects.filter(username='dominic').first()
deactivate()
parsed_events = []
for active_event in Event.objects.filter(active=True).order_by('start_date')[:10]:
data = {
'id': active_event.id,
'name': active_event.name,
'start_date': active_event.start_date.date(),
'end_date': active_event.end_date.date(),
'cover_image': active_event.cover_image,
'responses': ', '.join([x.user.username for x in active_event.availability_set.all()])
}
parsed_events.append(data)
context = {
'events': Event.objects.filter(created_by=user).all() # TODO: How to determine user?
'events': parsed_events
}
return render(request, template_name='index.html', context=context)
return render(request, "index.html", context)
def change_password(request):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
user = form.save()
update_session_auth_hash(request, user)
return redirect('password_change_done')
else:
form = PasswordChangeForm(request.user)
return render(request, 'change-password/change_password.html', {'form': form})
def password_change_done(request):
return render(request, 'change-password/password_change_done.html')
@login_required()
def event(request, event_id):
event = get_object_or_404(Event, pk=event_id)
if request.method == 'POST':
handle_availability_update(event, request)
return redirect('event', event_id=event_id)
elif request.method == 'GET':
day_count = (event.end_date - event.start_date).days + 1
morning = {}
noon = {}
night = {}
all_availabilities = {}
requesting_user_submitted = False
context = {
'event': event,
'morning': morning,
'noon': noon,
'night': night,
'user_availability': None,
'active_dates': None,
'user_responses': 0,
'best_days': {},
'no_overlap_message': ''
}
user_availability = Availability.objects.filter(event_id=event)
if user_availability:
for user_avail in user_availability:
context['user_responses'] += 1
for time in user_avail.time_table.get('active', []):
if time not in all_availabilities:
all_availabilities[time] = []
all_availabilities[time].append(user_avail.user.username)
if request.user == user_avail.user:
requesting_user_submitted = True
context['active_dates'] = user_avail.time_table['active']
context['all_availabilities'] = all_availabilities
for i in range(day_count):
date = event.start_date + timedelta(days=i)
human_readable_date = date.strftime('%m/%d/%Y %A')
for idx, time_period_array in enumerate([morning, noon, night]):
date_id = date.strftime(f'%m/%d/%Y %A-{idx}')
users = ', '.join(all_availabilities.get(date_id, []))
if not requesting_user_submitted:
users = ''
time_period_array[human_readable_date] = (date_id, users)
if all_availabilities:
most_avail = None
for day in sorted(all_availabilities, key=lambda k: len(all_availabilities[k]), reverse=True):
if not most_avail:
most_avail = len(all_availabilities[day])
if len(all_availabilities[day]) >= most_avail - 1 and len(all_availabilities[day]) != 1:
day_arr = day.split('-')
if day_arr[-1] == '0':
hday = day_arr[0] + ' Morning'
if day_arr[-1] == '1':
hday = day_arr[0] + ' Noon'
if day_arr[-1] == '2':
hday = day_arr[0] + ' Night'
context['best_days'][hday] = ', '.join(all_availabilities[day])
if not requesting_user_submitted:
all_availabilities = {}
if context['best_days'] == {}:
context['no_overlap_message'] = 'There are currently no overlapping times or dates!'
return render(request, "event.html", context)
def handle_availability_update(event, request):
availability_data = {
'active': []
}
for date, value in request.POST.items():
if date != 'csrfmiddlewaretoken':
availability_data['active'].append(date)
user_availability = Availability.objects.filter(event_id=event).filter(user=request.user)
if not user_availability:
a = Availability(
event_id=event,
user=request.user,
time_table=availability_data
)
a.save()
else:
avail = user_availability[0]
avail.time_table = availability_data
avail.save()