i want to implement an event calendar. i am faced with the problem of displaying the closest event to today's date. to find the nearest date, i use __gte in queryset, after queryset finds all the nearest dates, I want to highlight the first one with a different color here is my solution could you tell me what i'm doing wrong?
This is my Model
class Events(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
start_time = models.DateTimeField()
end_time = models.DateTimeField()
def __str__(self):
return self.title
#property
def get_html_url(self):
url = reverse('cal:events', args=(self.slug,))
return f'<a href="{url}">'
And my HTMLCalendar
from datetime import datetime, timedelta
from calendar import HTMLCalendar
from .models import Events
class Calendar(HTMLCalendar):
def __init__(self, year=None, month=datetime.now().month):
self.year = year
self.month = month
super(Calendar, self).__init__()
# formats a day as a td
# filter events by day
def formatday(self, day, events):
events_per_day = events.filter(start_time__day=day)
d = ''
if Events.objects.filter(start_time__day=day, start_time__month=self.month).exists():
for event in events_per_day:
d += f'{event.get_html_url}'
if day != 0:
ev = Events.objects.filter(start_time__gt=datetime.now()).first()
if ev:
return f"<td>{d}<span style='color:red;' class='date'>{day}</span></a></td>"
else:
return f"<td>{d}<span style='color:aliceblue;' class='date'>{day}</span></a></td>"
return '<td></td>'
else:
if day != 0:
return f"<td><b><span class='date'>{day}</span> </b></td>"
return '<td></td>'
# formats a week as a tr
def formatweek(self, theweek, events):
week = ''
for d, weekday in theweek:
week += self.formatday(d, events)
return f'<tr> {week} </tr>'
# formats a month as a table
# filter events by year and month
def formatmonth(self, withyear=True, ):
events = Events.objects.filter(start_time__year=self.year, start_time__month=self.month)
cal = f'<table border="0" cellpadding="0" cellspacing="0" class="calendar">\n'
cal += f'{self.formatmonthname(self.year, self.month, withyear=withyear)}\n'
cal += f'{self.formatweekheader()}\n'
for week in self.monthdays2calendar(self.year, self.month):
cal += f'{self.formatweek(week, events)}\n'
return cal
my solution is in the format day () function I am executing a query and if there is the first element I want to highlight it in red and paint over all the others with a different color
I'd consider moving the formatting to your template. You could then serve the first variable with the red formating and loop through the remaining values with the other formatting choice.
Use the view to generate the data you'll need in the template in the order you'd like to use it. Handle the presentation in the template.
Related
I'm trying to style my QcalendarWidget using CSS in PySide2, an set my maximum date to 22/12/2022. I'm able to change the color of text for next month to green and normal date to white, but is there any way to change the color for the date in between? (ie. from 22/12/2022 to 08/01/2023)
#qt_calendar_calendarview {
outline: 0px;
selection-background-color: #43ace6;
alternate-background-color: #2c313c;
background_color:rgb(170, 0, 0)
}
QCalendarWidget QAbstractItemView:!enabled {
color:"green"
}
QCalendarWidget QAbstractItemView:enabled{
color:"white"
}
Unfortunately, it's not possible using style sheets nor the palette.
There are some possible solutions, though.
Override paintCell()
This is the simplest possibility, as we can use paintCell() to draw the contents. Unfortunately, this has some limitations: we only get the painter, the rectangle and the date, meaning that it's our complete responsibility to choose how the cell and date would be drawn, and it may not be consistent with the rest of the widget (specifically, the headers).
Set the date text format
QCalendarWidget provides setDateTextFormat(), which allows setting a specific QTextCharFormat for any arbitrary date.
The trick is to set the format for dates outside the range within the minimum/maximum month: the assumption is that the calendar is not able to switch to a month that is outside the available date range, so we only need to set the formats for these specific days of the month boundaries.
class CustomCalendar(QCalendarWidget):
def fixDateFormats(self):
fmt = QTextCharFormat()
# clear existing formats
self.setDateTextFormat(QDate(), fmt)
fmt.setForeground(QBrush(QColor('green')))
for ref, delta in ((self.minimumDate(), -1), (self.maximumDate(), 1)):
month = ref.month()
date = ref.addDays(delta)
while date.month() == month:
self.setDateTextFormat(date, fmt)
date = date.addDays(delta)
def setDateRange(self, minimum, maximum):
super().setDateRange(minimum, maximum)
self.fixDateFormats()
def setMinimumDate(self, date):
super().setMinimumDate(date)
self.fixDateFormats()
def setMaximumDate(self, date):
super().setMaximumDate(date)
self.fixDateFormats()
The only drawback of this is that it doesn't allow to change the color of the cells that belong to another month, and while it's possible to use the stylesheet as written by the OP, this doesn't cover the exception of weekends.
Use a customized item delegate
This solution is a bit too complex, but is also the most ideal, as it's completely consistent with the widget and style, while also allowing some further customization.
Since the calendar is actually a composite widget that uses a QTableView to display the dates, this means that, just like any other Qt item view, we can override its delegate.
The default delegate is a QItemDelegate (the much simpler version of QStyledItemDelegates normally used in item views). While we could manually paint the content of the cell by completely overriding the delegate's paint(), but at that point the first solution would be much simpler. Instead we use the default painting and differentiate when/how the actual display value is shown: if it's within the calendar range, we leave the default behavior, otherwise we alter the QStyleOptionViewItem with our custom color and explicitly call drawDisplay().
class CalDelegate(QItemDelegate):
cachedOpt = QStyleOptionViewItem()
_disabledColor = None
def __init__(self, calendar):
self.calendar = calendar
self.view = calendar.findChild(QAbstractItemView)
super().__init__(self.view)
self.view.setItemDelegate(self)
self.dateReference = self.calendar.yearShown(), self.calendar.monthShown()
self.calendar.currentPageChanged.connect(self.updateReference)
def disabledColor(self):
return self._disabledColor or self.calendar.palette().color(
QPalette.Disabled, QPalette.Text)
def setDisabledColor(self, color):
self._disabledColor = color
self.view.viewport().update()
def updateReference(self, year, month):
self.dateReference = year, month
def dateForCell(self, index):
day = index.data()
row = index.row()
if self.calendar.horizontalHeaderFormat():
if row == 0:
return
row -= 1
col = index.column()
if self.calendar.verticalHeaderFormat():
if col == 0:
return
col -= 1
year, month = self.dateReference
if row < 1 and day > 7:
# previous month
month -= 1
if month < 1:
month = 12
year -= 1
elif row > 3 and day < 15:
# next month
month += 1
if month > 12:
month = 1
year += 1
return QDate(year, month, day)
def drawDisplay(self, qp, opt, rect, text):
if self.doDrawDisplay:
super().drawDisplay(qp, opt, rect, text)
else:
self.cachedOpt = QStyleOptionViewItem(opt)
def paint(self, qp, opt, index):
date = self.dateForCell(index)
self.doDrawDisplay = not bool(date)
super().paint(qp, opt, index)
if self.doDrawDisplay:
return
year, month = self.dateReference
if (
date.month() != month
or not self.calendar.minimumDate() <= date <= self.calendar.maximumDate()
):
self.cachedOpt.palette.setColor(
QPalette.Text, self.disabledColor())
super().drawDisplay(qp, self.cachedOpt,
self.cachedOpt.rect, str(index.data()))
app = QApplication([])
cal = QCalendarWidget()
delegate = CalDelegate(cal)
delegate.setDisabledColor(QColor('green'))
cal.setDateRange(QDate(2022, 12, 4), QDate(2023, 1, 27))
cal.show()
app.exec()
I am not sure of a way using css, it is possible using code though.
If you override the QCalenderWidget.paintCell method you can style each date individually.
For example:
class Calendar(QCalendarWidget):
def __init__(self, parent) -> None:
super().__init__(parent)
self.start_date = QDate(2022, 12, 22)
self.end_date = QDate(2023, 8, 1)
def paintCell(self, painter, rect, date):
if date.daysTo(self.end_date) > 0 and date.daysTo(self.start_date) < 0:
painter.setPen("green")
brush = painter.brush()
brush.setColor("black")
brush.setStyle(Qt.SolidPattern)
painter.setBrush(brush)
painter.drawRect(rect)
painter.drawText(rect, Qt.AlignmentFlag.AlignCenter, str(date.day()))
else:
super().paintCell(painter, rect, date)
I am new to Django and specifically Models and have been struggling to understand how to update data in the data base.
Models.py:
# Create your models here.
class logTimes(models.Model):
fast_finshed = models.BooleanField(default=False)
start_date_time = models.DateTimeField('start fast')
end_date_time = models.DateTimeField('end fast')
In my View, I have a function to add a row of data when submitting one of two forms that will either add a new row of data or update an existing row of data. I cannot figure out how to make the update I have tried too many things to write here. I have been reviewing the Django docs and not making progress at all.
Views.py:
#function to add a fast to the data base or update the last fast with an end date and time
def start_or_end_fast(request):
#If starting fast, update the data base with fast_finished = False
#A new fast start date and time using current date and time
#A specific end date and time set in the future
if request.method == 'POST' and 'start_fast' in request.POST:
l = logTimes(fast_finshed=False,start_date_time=datetime.now(),end_date_time=datetime(year=2023, month=1, day=1, hour=12, minute=00, second=00))
l.save()
print('fast started')
return render(request,'startandstoptimes/index.html')
#If ending a fast, find the last fast that has the specific end date and time
#And update end_date_time to the current date and time
elif request.method == 'POST' and 'end_fast' in request.POST:
#Update row where end_date_time = year=2023, month=1, day=1, hour=12, minute=00, second=00
#Change to current date and time
???????
print('Fast ended')
return render(request,'startandstoptimes/index.html')
#If Just refreshing the page
else:
return render(request,'startandstoptimes/index.html')
Any advice would be much appreciated, I find this a bit difficult to wrap my head around coming from using SQL.
Thank you!
#function to add a fast to the data base or update the last fast with an end date and time
def start_or_end_fast(request):
#If starting fast, update the data base with fast_finished = False
#A new fast start date and time using current date and time
#A specific end date and time set in the future
if request.method == 'POST' and 'start_fast' in request.POST:
l = logTimes(fast_finshed=False,start_date_time=datetime.now(),end_date_time=datetime(year=2023, month=1, day=1, hour=12, minute=00, second=00))
l.save()
print('fast started')
return render(request,'startandstoptimes/index.html')
#If ending a fast, find the last fast that has the specific end date and time
#And update end_date_time to the current date and time
elif request.method == 'POST' and 'end_fast' in request.POST:
#Update row where end_date_time = year=2023, month=1, day=1, hour=12, minute=00, second=00
#Change to current date and time
logTimes.objects.filter(
# this will be the WHERE part of the SQL query
end_date_time=datetime(year=2023, month=1, day=1, hour=12, minute=0, second=0)
).update(
# this will be the SET XXX VALUES XXX part of the query
end_date_time=datetime.now()
)
print('Fast ended')
return render(request,'startandstoptimes/index.html')
#If Just refreshing the page
else:
return render(request,'startandstoptimes/index.html')
For my E-commerce project, I am trying to generate a reference code that can be understandable yet unique at the same time:
I am trying to generate a reference code that after each purchase is made that includes that day, month, year, hour, minute and a digit that increases with a new transaction
DDMMYYHHMMXXX
Day, Month, Year, Hour, Minute,3 digits starting with 001 and increasing with each new order.
How do I do it?
My current code generated is:
def create_ref_code():
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=6))
model.py
class Order(models.Model):
ref_code = models.CharField(max_length=20, blank=True, null=True)
ordered_date = models.DateTimeField()
def __str__(self):
return self.user.username
This is how far I have reached but I am not sure how to increase the count with every new order
def create_ref_code():
now = datetime.now()
code = now.strftime("%y%m%d%H%M%S")
print(code)
count = + 1
digit = str(count).zfill(3)
my_code = (code, digit)
return ''.join(my_code)
for that you can extend the save method and retrieve all the order count and also you can use something like this to pad the leading zeroes on that count
str(1).zfill(3)
this will create 001 output in string and you need this in string format to concat the data so no need to convert that to integer again
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
count = ***retrieve you count of that orders using query*** + 1
digit = str(count).zfill(3)
self.reference_code = your logic to create reference code
updated:
you don't have to increment count like that
def create_ref_code():
now = datetime.now()
"""
'make query to count all todays order here'
count = Order.objects.filter(filter argument by date).count() + 1
"""
code = now.strftime("%y%m%d%H%M%S")
digit = str(count).zfill(3)
my_code = (code, digit)
return ''.join(my_code)
instead of DDMMYYHHMMXXX try UUID-4
code :
import uuid
uuid.uuid4()
I want to create a webiste with a calendar in Django. Therefor i have found a tutorial on the web. Here you have to overwrite the functions from the HTMLCalendar When I use the Code from there comes the Error :
File "/home/work/Desktop/Coden /Projects/Calendar/anotherone/cal/utils.py", line 18
d += f"<li> {event.title} </li>"
The tutorial - when it comes to overwriting the functions:
https://www.huiwenteo.com/normal/2018/07/24/django-calendar.html
This is just a Django Project. I code on Ubuntu in Visualstudio code. Here the start from the files. I think it occurs because of the " and the following HTML Code. As you can see, is this not once in the file it comes again and again. I hope someone can give me a solution for the whole file.
from datetime import datetime, timedelta
from calendar import HTMLCalendar
from .models import Event
class Calendar(HTMLCalendar):
def __init__(self, year=None, month=None):
self.year = year
self.month = month
super(Calendar, self).__init__()
# formats a day as a td
# filter events by day
def formatday(self, day, events):
events_per_day = events.filter(start_time__day=day)
d = ''
for event in events_per_day:
d += f"<li> {event.title} </li>"
if day != 0:
return f"<td><span class='date'>{day}</span><ul> {d} </ul></td>"
return '<td></td>'
I hope I can display the calendar after fixing the error.
I'm working on a calendar via Python's HTMLCalendar and Django. The functions I'm using to input data into the calendar are showing up as unbound, and therefore not working.
Here's the calendar code:
from www.wednesday.models import Event
import calendar
e = Event()
class EventCal(calendar.HTMLCalendar):
def formatday(self, day, weekday):
if day == 0:
return '<td class="noday"> </td>' # Day outside month
if day == e.dd():
return '<td class="%s">%d</p>%s</td>' % (self.cssclasses[weekday], day, e.link(), e.res())
else:
return '<td class="%s">%d</td>' % (self.cssclasses[weekday], day)
class rendCal:
c = EventCal(calendar.SUNDAY)
Here's my models.py:
from django.db import models
class Event(models.Model):
Restaurant = models.CharField(max_length=200)
LinkURL = models.CharField(max_length=200)
created = models.DateTimeField(auto_now_add=True)
DateDay = models.IntegerField(max_length=2)
def dd(self):
return '%i' % self.DateDay
def link(self):
return '%s' % self.LinkURL
def res(self):
return '%s' % self.Restaurant
And lastly, my views.py:
from django.shortcuts import render_to_response
import www.wednesday.models
from www.wednesday.cal import rendCal
import datetime as dt
def calendar(request):
now = dt.datetime.now()
cal = rendCal.c.formatmonth(now.year, now.month)
return render_to_response('cal.html', {'calendar': cal})
Everything works except for the functions from Event that are called inside the EventCal class.
Obviously I'm quite new at this.
Okay, #Marcin asked for an error, this is what I'm seeing, also I corrected the capitalization.
TypeError at /calendar/
unbound method dd() must be called with Event instance as first argument (got nothing instead)
cal.py in formatday, line 9
The environment variables in EventCal from Event are showing up blank, I'm pretty sure that's why I'd been getting the needs int not str error. When I change e.dd() to a static number, it returns everything but e.link() and e.res().
dd() is a method of an instance of the class.
You call it like this:
e = Event()
x = e.dd()
You can't apply dd to Event itself.
I am not sure what exactly you are trying to do, so I am not sure how you need to modify your code.
In formatday you have:
if day == 0:
but also:
if day == Event.dd():
and Event.dd() returns a string.
So, is day an int or a string?