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 # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*.pyc
# C extensions # C extensions
*.so *.so
@ -51,3 +52,8 @@ docs/_build/
*__pycache__/* *__pycache__/*
*.pyc *.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 = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.sqlite3', 'ENGINE': 'django.db.backends.postgresql',
'NAME': BASE_DIR / 'db.sqlite3', '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' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = '/' LOGIN_REDIRECT_URL = '/'
LOGOUT_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.contrib import admin
from django.urls import path, include 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 import EventView
from api.views.event_division import EventDivisionView from api.views.event_division import EventDivisionView
from api.views.event_comment import EventCommentView from api.views.event_comment import EventCommentView
@ -16,7 +17,10 @@ urlpatterns = [
path('api/event_division/', EventDivisionView.as_view(), name='event_division'), path('api/event_division/', EventDivisionView.as_view(), name='event_division'),
path("", views.index, name="index"), path("", views.index, name="index"),
path("accounts/", include("django.contrib.auth.urls")), path("accounts/", include("django.contrib.auth.urls")),
#path("event/<int:event_id>", views.event, name="event"), path("event/<int:event_id>", views.event, name="event"),
#path('change_password/', views.change_password, name='change_password'), path('change_password/', views.change_password, name='change_password'),
#path('password_change_done/', views.password_change_done, name='password_change_done') 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 soupsieve==2.6
sqlparse==0.5.1 sqlparse==0.5.1
urllib3==2.3.0 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) id = models.AutoField(primary_key=True)
name = models.TextField(blank=False, null=False) name = models.TextField(blank=False, null=False)
description = 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) division = models.ForeignKey(EventDivision, on_delete=models.CASCADE)
start_date = models.DateTimeField(blank=False, null=False) start_date = models.DateTimeField(blank=False, null=False)
end_date = models.DateTimeField(blank=False, null=False) end_date = models.DateTimeField(blank=False, null=False)

View File

@ -20,6 +20,9 @@
</div> </div>
<div style="float:right;"> <div style="float:right;">
<a href="{% url 'index' %}">home</a> | <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> </div>

View File

@ -1,28 +1,36 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="container"> <table class="table">
<thead>
<div class="accordion"> <tr>
{% for event in events%} <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">
<div class="accordion-item"> <div class="accordion-item">
<h2 class="accordion-header" id="event-{{event.id}}"> <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}}"> <button class="accordion-button collapsed"
{{event.name}} type="button"
</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> </h2>
<div id="event-collapse-{{event.id}}" class="accordion-collapse collapse" aria-labelledby="event-{{event.id}}" data-bs-parent="#accordionExample"> <div id="event-collapse-{{ event.id }}"
<div class="accordion-body"> class="accordion-collapse collapse"
aria-labelledby="event-{{ event.id }}"
{{event.description}} data-bs-parent="#accordionExample">
<div class="accordion-body">{{ event.description }}</div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endfor %} {% endfor %}
</div> </tbody>
</div> </table>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,6 @@
from datetime import timedelta from datetime import timedelta
from django.contrib.auth import update_session_auth_hash from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth.forms import PasswordChangeForm
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from scripts.bp_deactivate import deactivate from scripts.bp_deactivate import deactivate
@ -10,10 +9,148 @@ from web.models import Event, Availability
@login_required() @login_required()
def index(request): def index(request):
user = User.objects.filter(username='dominic').first() deactivate()
parsed_events = []
context = { for active_event in Event.objects.filter(active=True).order_by('start_date')[:10]:
'events': Event.objects.filter(created_by=user).all() # TODO: How to determine user? 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()])
} }
return render(request, template_name='index.html', context=context) parsed_events.append(data)
context = {
'events': parsed_events
}
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()