diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 427010ca9..dbe4b843c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -16,6 +16,10 @@ updates: - 0.13.0 - 0.13.1 - 0.13.2 + - dependency-name: "boto3" + - dependency-name: "boto3-stubs" + - dependency-name: "botocore" + - dependency-name: "botocore-stubs" - dependency-name: lxml versions: - 4.6.2 diff --git a/base-requirements.txt b/base-requirements.txt index fc018561c..4877f78c3 100644 --- a/base-requirements.txt +++ b/base-requirements.txt @@ -1,8 +1,8 @@ dj-database-url==0.5.0 -django-pipeline==3.0.0 # 3.0.0 is first version that supports Django 4.2 +django-pipeline==3.1.0 # 3.0.0 is first version that supports Django 4.2 django-sitetree==1.18.0 # >=1.17.1 is (?) first version that supports Django 4.2 django-apptemplates==1.5 -django-admin-interface==0.24.2 +django-admin-interface==0.28.9 django-translation-aliases==0.1.0 Django==4.2.16 docutils==0.21.2 @@ -11,10 +11,10 @@ cmarkgfm==0.6.0 Pillow==10.4.0 psycopg2-binary==2.9.9 python3-openid==3.2.0 -python-decouple==3.4 +python-decouple==3.8 # lxml used by BeautifulSoup. lxml==5.2.2 -cssselect==1.1.0 +cssselect==1.2.0 feedparser==6.0.11 beautifulsoup4==4.12.3 icalendar==4.0.7 @@ -26,7 +26,7 @@ django-imagekit==5.0 # 5.0 is first version that supports Django 4.2 django-haystack==3.2.1 elasticsearch>=7,<8 # TODO: 0.14.0 only supports Django 1.8 and 1.11. -django-tastypie==0.14.6 # 0.14.6 is first version that supports Django 4.2 +django-tastypie==0.14.7 # 0.14.6 is first version that supports Django 4.2 pytz==2021.1 python-dateutil==2.8.2 diff --git a/dev-requirements.txt b/dev-requirements.txt index 8d61d0f9d..1ee11a333 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,7 +2,7 @@ # Required for running tests -factory-boy==3.2.1 +factory-boy==3.3.1 Faker==0.8.1 tblib==1.7.0 responses==0.13.3 diff --git a/docker-compose.yml b/docker-compose.yml index 4406c0ff5..1f5ea36b4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3.9" - services: postgres: image: postgres:15.3-bullseye diff --git a/docs/source/index.rst b/docs/source/index.rst index 76a201828..cfde87586 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -10,7 +10,6 @@ General information :Issue tracker: https://github.com/python/pythondotorg/issues :Mailing list: pydotorg-www_ :IRC: ``#pydotorg`` on Freenode -:Staging site: https://staging.python.org/ (``main`` branch) :Production configuration: https://github.com/python/psf-salt :GitHub Actions: .. image:: https://github.com/python/pythondotorg/actions/workflows/ci.yml/badge.svg diff --git a/docs/source/install.md b/docs/source/install.md index d6c29d295..c91d1bd59 100644 --- a/docs/source/install.md +++ b/docs/source/install.md @@ -55,7 +55,7 @@ web_1 | Starting development server at http://0.0.0.0:8000/ web_1 | Quit the server with CONTROL-C. ``` -You can view these results in your local web browser at: `http://localhost:8000` +You can view these results in your local web browser at: To reset your local environment, run: @@ -88,7 +88,7 @@ This is a simple wrapper around running `python manage.py` in the container, all Manual setup ------------ -First, install [PostgreSQL](https://www.postgresql.org/download/) on your machine and run it. *pythondotorg* currently uses Postgres 10.21. +First, install [PostgreSQL](https://www.postgresql.org/download/) on your machine and run it. *pythondotorg* currently uses Postgres 15.x. Then clone the repository: @@ -99,7 +99,7 @@ $ git clone git://github.com/python/pythondotorg.git Then create a virtual environment: ``` -$ python3.9 -m venv venv +$ python3 -m venv venv ``` And then you'll need to install dependencies. You don't need to use `pip3` inside a Python 3 virtual environment: diff --git a/events/models.py b/events/models.py index b41d92b22..1f2ab2cb0 100644 --- a/events/models.py +++ b/events/models.py @@ -211,8 +211,15 @@ def previous_time(self): return None @property - def next_or_previous_time(self): - return self.next_time or self.previous_time + def next_or_previous_time(self) -> models.Model: + """Return the next or previous time of the event OR the occurring rule.""" + if next_time := self.next_time: + return next_time + + if previous_time := self.previous_time: + return previous_time + + return self.occurring_rule if hasattr(self, "occurring_rule") else None @property def is_past(self): diff --git a/events/tests/test_views.py b/events/tests/test_views.py index 691817036..1291252a5 100644 --- a/events/tests/test_views.py +++ b/events/tests/test_views.py @@ -35,11 +35,50 @@ def setUpTestData(cls): finish=cls.now - datetime.timedelta(days=1), ) + # Future event + cls.future_event = Event.objects.create(title='Future Event', creator=cls.user, calendar=cls.calendar, featured=True) + RecurringRule.objects.create( + event=cls.future_event, + begin=cls.now + datetime.timedelta(days=1), + finish=cls.now + datetime.timedelta(days=2), + ) + + # Happening now event + cls.current_event = Event.objects.create(title='Current Event', creator=cls.user, calendar=cls.calendar) + RecurringRule.objects.create( + event=cls.current_event, + begin=cls.now - datetime.timedelta(hours=1), + finish=cls.now + datetime.timedelta(hours=1), + ) + + # Just missed event + cls.just_missed_event = Event.objects.create(title='Just Missed Event', creator=cls.user, calendar=cls.calendar) + RecurringRule.objects.create( + event=cls.just_missed_event, + begin=cls.now - datetime.timedelta(hours=3), + finish=cls.now - datetime.timedelta(hours=1), + ) + + # Past event + cls.past_event = Event.objects.create(title='Past Event', creator=cls.user, calendar=cls.calendar) + RecurringRule.objects.create( + event=cls.past_event, + begin=cls.now - datetime.timedelta(days=2), + finish=cls.now - datetime.timedelta(days=1), + ) + def test_events_homepage(self): url = reverse('events:events') response = self.client.get(url) + events = response.context['object_list'] + event_titles = [event.title for event in events] + self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['object_list']), 1) + self.assertEqual(len(events), 6) + + self.assertIn('Future Event', event_titles) + self.assertIn('Current Event', event_titles) + self.assertIn('Past Event', event_titles) def test_calendar_list(self): calendars_count = Calendar.objects.count() @@ -54,7 +93,7 @@ def test_event_list(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['object_list']), 1) + self.assertEqual(len(response.context['object_list']), 3) url = reverse('events:event_list_past', kwargs={"calendar_slug": 'unexisting'}) response = self.client.get(url) @@ -66,7 +105,7 @@ def test_event_list_past(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['object_list']), 1) + self.assertEqual(len(response.context['object_list']), 4) def test_event_list_category(self): category = EventCategory.objects.create( @@ -114,7 +153,7 @@ def test_event_list_date(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertEqual(response.context['object'], dt.date()) - self.assertEqual(len(response.context['object_list']), 2) + self.assertEqual(len(response.context['object_list']), 6) def test_eventlocation_list(self): venue = EventLocation.objects.create( @@ -150,12 +189,20 @@ def test_event_detail(self): self.assertEqual(self.event, response.context['object']) def test_upcoming_tag(self): - self.assertEqual(len(get_events_upcoming()), 1) - self.assertEqual(len(get_events_upcoming(only_featured=True)), 0) + self.assertEqual(len(get_events_upcoming()), 3) + self.assertEqual(len(get_events_upcoming(only_featured=True)), 1) self.rule.begin = self.now - datetime.timedelta(days=3) self.rule.finish = self.now - datetime.timedelta(days=2) self.rule.save() - self.assertEqual(len(get_events_upcoming()), 0) + self.assertEqual(len(get_events_upcoming()), 2) + + def test_context_data(self): + url = reverse("events:events") + response = self.client.get(url) + + self.assertIn("events_just_missed", response.context) + self.assertIn("upcoming_events", response.context) + self.assertIn("events_now", response.context) class EventSubmitTests(TestCase): diff --git a/events/views.py b/events/views.py index 2490626e3..56df88dcb 100644 --- a/events/views.py +++ b/events/views.py @@ -40,10 +40,21 @@ def get_context_data(self, **kwargs): class EventHomepage(ListView): """ Main Event Landing Page """ - template_name = 'events/event_list.html' + template_name = "events/event_list.html" - def get_queryset(self): - return Event.objects.for_datetime(timezone.now()).order_by('occurring_rule__dt_start') + def get_queryset(self) -> Event: + """Queryset to return all events, ordered by START date.""" + return Event.objects.all().order_by("-occurring_rule__dt_start") + + def get_context_data(self, **kwargs: dict) -> dict: + """Add more ctx, specifically events that are happening now, just missed, and upcoming.""" + context = super().get_context_data(**kwargs) + context["events_just_missed"] = Event.objects.until_datetime(timezone.now())[:2] + context["upcoming_events"] = Event.objects.for_datetime(timezone.now()) + context["events_now"] = Event.objects.filter( + occurring_rule__dt_start__lte=timezone.now(), + occurring_rule__dt_end__gte=timezone.now())[:2] + return context class EventDetail(DetailView): @@ -68,11 +79,13 @@ def get_context_data(self, **kwargs): class EventList(EventListBase): def get_queryset(self): - return Event.objects.for_datetime(timezone.now()).filter(calendar__slug=self.kwargs['calendar_slug']).order_by('occurring_rule__dt_start') + return Event.objects.for_datetime(timezone.now()).filter(calendar__slug=self.kwargs['calendar_slug']).order_by( + 'occurring_rule__dt_start') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['events_today'] = Event.objects.until_datetime(timezone.now()).filter(calendar__slug=self.kwargs['calendar_slug'])[:2] + context['events_today'] = Event.objects.until_datetime(timezone.now()).filter( + calendar__slug=self.kwargs['calendar_slug'])[:2] context['calendar'] = get_object_or_404(Calendar, slug=self.kwargs['calendar_slug']) return context diff --git a/successstories/forms.py b/successstories/forms.py index f623001b0..45ec1dfd3 100644 --- a/successstories/forms.py +++ b/successstories/forms.py @@ -24,6 +24,10 @@ class Meta: labels = { 'name': 'Story name', } + help_texts = { + "content": "Note: Submissions in Markdown " + "are strongly preferred and can be processed faster.", + } def clean_name(self): name = self.cleaned_data.get('name') diff --git a/templates/events/event_list.html b/templates/events/event_list.html index bbfb764d2..8d7b0d60f 100644 --- a/templates/events/event_list.html +++ b/templates/events/event_list.html @@ -8,73 +8,110 @@ {% block header_content %} {% if featured %} - {% endif %} {% endblock header_content %} - -{# Based on python/events.html #} - {% block content %} - {% if calendar %} + {% if calendar %}

from the {{ calendar.name }}

- {% endif %} + {% endif %} -
+
+ {% if events_now %}
-

Upcoming Events

- {% if page_obj.has_next %} -

More

- {% endif %} +

Happening Now

+
+ {% endif %} + +
+

Upcoming Events

+ {% if page_obj.has_next %} +

More

+ {% endif %} + -
- - {% if events_today %} -

You just missed...

- - {% endif %}
+ + {% if events_just_missed %} +
+

You just missed...

+ +
+ {% endif %} +
{% endblock content %} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy