diff --git a/events/models.py b/events/models.py index 3dbbf4412..3cf7634ea 100644 --- a/events/models.py +++ b/events/models.py @@ -14,11 +14,16 @@ import asyncio from asgiref.sync import sync_to_async import aiohttp +import hashlib +import base64 # Create your models here. class EventManager(models.Manager): + def hashing(self, string): + return base64.b64encode(hashlib.blake2b(string.encode(), digest_size=6).hexdigest().encode()).decode() + async def read_ical(self, name, file, create_function): try: #print("Requesting " + name) @@ -59,17 +64,22 @@ async def read_wp_events_api(self, name, api, categorize): print("Failed requesting to " + name) async def wp_events_api_create_event(self, event_json, api, host, categorize): - if not await self.filter(event_url=event_json['link']).aexists(): + if not await self.filter(event_url=event_json['link'], start_time=datetime.fromisoformat(event_json['start']).astimezone(timezone.get_current_timezone())).aexists(): event = await self.acreate( title=event_json['title'], event_url=event_json['link'], + hash=self.hashing(event_json['link'] + str(event_json['start'])) ) else: - event = await self.filter(event_url=event_json['link']).afirst() + event = await self.filter(event_url=event_json['link'], start_time=datetime.fromisoformat(event_json['start']).astimezone(timezone.get_current_timezone())).afirst() if event.update_mode != 2: return None event.title = str(event_json['title']['rendered'].encode('utf-8'), 'UTF-8') + + if event.hash == "": + event.hash = self.hashing(event_json['link'] + str(event_json['start'])) + event.description = str(event_json['excerpt']['rendered'].encode('utf-8'), 'UTF-8') event.start_time = datetime.fromisoformat(event_json['start']).astimezone(timezone.get_current_timezone()) @@ -89,19 +99,43 @@ async def wp_events_api_create_event(self, event_json, api, host, categorize): event.event_url=event_json['link'] event.category = categorize['default'] + categories = ['entertainment', 'seminar', 'community', 'sports'] + for category in categories: + categorize_key = category + '_type' + if categorize_key in categorize: + for event_type in event_json['event-type']: + + for category_type in categorize[categorize_key]: + if event_type == category_type: + event.category = category + break + + if event.category != categorize['default']: + break + + if event.category != categorize['default']: + break - if 'seminar_ids' in categorize: - for id in event_json['event-type']: + event.hidden=False + if 'hidden_topics' in categorize: + for id in event_json['event-topic']: - for seminar_id in categorize['seminar_ids']: - if id == seminar_id: - event.category = "seminar" + for topic in categorize['hidden_topics']: + if id == topic: + event.hidden = True break - if event.category != categorize['default']: + if event.hidden == True: break - - event.hidden=False + + if event.hidden == False and 'hidden_title_terms' in categorize: + for term in categorize['hidden_title_terms']: + if term.lower() in event.title.lower(): + event.hidden = True + break + + if timedelta(days=14) < event.end_time - event.start_time: + event.hidden=True req = Request(api, headers={'User-Agent': "The Ubyssey https://ubyssey.ca/"}) if req.host in event_json['link']: @@ -133,12 +167,17 @@ async def ubcevents_create_event(self, ical_component): event = await self.acreate( title=ical_component.get('summary'), event_url=ical_component.decoded('url'), + hash=self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) ) else: event = await self.filter(event_url=ical_component.get('url')).afirst() if event.update_mode != 2: return None + + if event.hash == "": + event.hash = self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) + # Split location and address location = ical_component.get('location') address = "" @@ -199,9 +238,9 @@ async def ubcevents_judge_hidden(self, event, ical): return True # Hide events with certain terms in the title - # The two listed right now are on an inaccurate repeating schedule - for i in ['coffee hour', 'advanced research computing summer school']: - if i in title: + # The first two listed right now are on an inaccurate repeating schedule, the last was an advertisment for a sale that lasted too long + for i in ['coffee hour', 'advanced research computing summer school', 'Student Indoor Plant Sale at UBC Botanical Garden']: + if i.lower() in title.lower(): return True # Default to showing events when there are no categories listed @@ -275,16 +314,22 @@ def ubcevents_category(self, event): async def gothunderbirds_create_event(self, ical_component): - if not await self.filter(event_url=ical_component.get('url').replace("&", "__AND__")).aexists(): + if not await self.filter(event_url=ical_component.get('url')).aexists(): event = await self.acreate( title=ical_component.get('summary'), - event_url=ical_component.decoded('url').replace("&", "__AND__"), + event_url=ical_component.decoded('url'), + hash=self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) ) else: - event = await self.filter(event_url=ical_component.get('url').replace("&", "__AND__")).afirst() + event = await self.filter(event_url=ical_component.get('url')).afirst() if event.update_mode != 2: return None + + if event.hash == "": + event.hash = self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) + + # Split location and address address = ical_component.get('location') location = address.replace('Vancouver, B.C., ', '') @@ -321,7 +366,7 @@ async def gothunderbirds_create_event(self, ical_component): event.address=address event.location=location - event.event_url=ical_component.decoded('url').replace("&", "__AND__") + event.event_url=ical_component.decoded('url') event.category='sports' event.hidden=self.gothunderbirds_judge_hidden(ical_component) @@ -395,12 +440,16 @@ async def phas_ubc_create_event(self, event_component): event = await self.acreate( title=title, event_url=event_url, + hash=self.hashing(title + str(event_component.find('span', class_='start').get_text(strip=True))) ) else: event = await self.filter(event_url=event_url).afirst() if event.update_mode != 2: return None + if event.hash == "": + event.hash = self.hashing(title + str(event_component.find('span', class_='start').get_text(strip=True))) + # Extract start time start_time_str = event_component.find('span', class_='start').get_text(strip=True) parsed_start_time = datetime.fromisoformat(start_time_str) @@ -450,6 +499,7 @@ async def cs_ubc_create_event(self, ical_component): event = await self.acreate( title=ical_component.get('summary'), event_url=ical_component.decoded('url'), + hash=self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) ) else: event = await self.filter(event_url=ical_component.get('url')).afirst() @@ -457,6 +507,10 @@ async def cs_ubc_create_event(self, ical_component): return None event.title=ical_component.get('summary') + + if event.hash == "": + event.hash = self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) + event.description= "
" + ical_component.get('description').replace("&", "&") if "
Name:" in event.description and "\nTitle" in event.description: if event.description.index("
Name:") < event.description.index("\nTitle"): @@ -526,6 +580,7 @@ async def stats_ubc_create_event(self, ical_component): event = await self.acreate( title=ical_component.get('summary'), event_url=ical_component.decoded('url'), + hash=self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) ) else: event = await self.filter(event_url=ical_component.get('url')).afirst() @@ -536,6 +591,9 @@ async def stats_ubc_create_event(self, ical_component): event.title=ical_component.get('summary') + if event.hash == "": + event.hash = self.hashing(ical_component.get('summary') + str(ical_component.decoded('dtstart'))) + # Clean up event description because they are so messy and have unnecessary information description = str(ical_component.decoded('description'), 'UTF-8') safety = 10 @@ -639,6 +697,12 @@ class Event(models.Model): null=True, blank=True, ) + hash = models.CharField( + max_length=50, + blank=False, + null=True, + default='' + ) image = models.CharField( max_length=255, blank=True, @@ -672,6 +736,7 @@ class Event(models.Model): FieldPanel("address"), FieldPanel("host"), FieldPanel("event_url"), + FieldPanel("hash"), FieldPanel("image"), FieldPanel( "category", diff --git a/events/views.py b/events/views.py index 9bf086f5c..c5582b54b 100644 --- a/events/views.py +++ b/events/views.py @@ -72,6 +72,18 @@ def react(self, request): 'description': "Events Around Campus collected by The Ubyssey", 'url': 'https://ubyssey.ca/events/', } + + if request.GET.get("event"): + if Event.objects.filter(hash=request.GET.get("event")).exists(): + event = Event.objects.filter(hash=request.GET.get("event")).first() + + meta = { + 'title': event.title, + 'description': event.description, + 'url': "https://ubyssey.ca/events/?event=" + event.hash, + 'noindex': True, + } + return render(request, "events/event_page_react.html", {'ical':ical, 'rss':rss, 'meta':meta}) def landing(self, request): @@ -259,7 +271,8 @@ async def update_events(request): 'api': 'https://anth.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [633, 632, 634, 528, 530], + 'seminar_type': [633, 632, 634, 528, 530], + 'hidden_title_terms': ['coffee hour'] }, }, @@ -267,7 +280,7 @@ async def update_events(request): 'api': 'https://asia.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [570, 572, 571, 574, 739], + 'seminar_type': [570, 572, 571, 574, 739], }, }, @@ -275,7 +288,8 @@ async def update_events(request): 'api': 'https://cenes.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [552, 554, 559, 553, 677, 558] + 'seminar_type': [552, 554, 559, 553, 677, 558], + 'hidden_title_terms': ['fika', 'plauder', 'kaffeestunde'] }, }, @@ -283,7 +297,7 @@ async def update_events(request): 'api': 'https://english.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [512, 515, 510, 513] + 'seminar_type': [512, 515, 510, 513] }, }, @@ -291,7 +305,7 @@ async def update_events(request): 'api': 'http://fhis.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [534, 537, 532] + 'seminar_type': [534, 537, 532] }, }, @@ -299,7 +313,7 @@ async def update_events(request): 'api': 'https://grsj.arts.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [512, 514, 632] + 'seminar_type': [512, 514, 632] }, }, @@ -307,7 +321,7 @@ async def update_events(request): 'api': 'https://history.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [531, 525, 527, 526, 530] + 'seminar_type': [531, 525, 527, 526, 530] }, }, @@ -322,7 +336,7 @@ async def update_events(request): 'api': 'https://psych.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [433, 384, 906, 792, 377, 931, 560, 559] + 'seminar_type': [433, 384, 906, 792, 377, 931, 560, 559] }, }, @@ -330,7 +344,92 @@ async def update_events(request): 'api': 'https://socialwork.ubc.ca/wp-json/wp/v2/', 'categorize': { 'default': 'community', - 'seminar_ids': [520, 527, 525] + 'seminar_type': [520, 527, 525] + }, + }, + + {'name': 'UBC Faculty of Arts', + 'api': 'https://www.arts.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'community', + 'entertainment_type': [802], + 'seminar_type': [1962, 1785], + 'hidden_topics': [1783, 1950, 1996, 2378, 2379, 1995, 2375] + }, + }, + + {'name': 'UBC School of Information', + 'api': 'https://ischool.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + }, + }, + + {'name': 'UBC Art History, Visual Art & Theory', + 'api': 'https://ahva.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'community', + 'seminar_type': [820, 926, 817, 930, 824, 825] + }, + }, + + {'name': 'UBC Ancient Mediterranean and Near Eastern Studies', + 'api': 'https://amne.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'community', + 'seminar_type': [718, 568, 570, 569] + }, + }, + + {'name': 'UBC Coordinated Arts Programs', + 'api': 'https://cap.arts.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + }, + }, + + {'name': 'UBC School of Public Policy and Global Affairs', + 'api': 'https://sppga.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + }, + }, + + {'name': 'UBC Geography', + 'api': 'https://geog.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + 'hidden_title_terms': ['green day'], + }, + }, + + {'name': 'UBC Linguistics', + 'api': 'https://linguistics.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + }, + }, + + {'name': 'UBC Philosophy', + 'api': 'https://philosophy.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + }, + }, + + {'name': 'UBC Political Science', + 'api': 'https://politics.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'seminar', + 'community_type': [544, 546, 549] + }, + }, + + {'name': 'UBC Theatre & Film', + 'api': 'https://politics.ubc.ca/wp-json/wp/v2/', + 'categorize': { + 'default': 'entertainment', + 'seminar_type': [1262, 1263, 1265] }, }, ] @@ -339,6 +438,10 @@ async def update_events(request): for a in wp_apis: # Event.objects.wp_events_api_get_type_ids(a['api'], terms) # Uncomment to print the event-type id for types wuth the terms above in their name. Used for categorizing the events tasks.append(asyncio.create_task(Event.objects.read_wp_events_api(a['name'], a['api'], a['categorize']))) + if len(tasks) >= 15: + await asyncio.gather(*tasks) + tasks = [] + ical_files = [ @@ -361,6 +464,9 @@ async def update_events(request): for f in ical_files: tasks.append(asyncio.create_task(Event.objects.read_ical(f['name'], f['file'], f['create_function']))) + if len(tasks) >= 15: + await asyncio.gather(*tasks) + tasks = [] await asyncio.gather(*tasks) @@ -499,7 +605,7 @@ def item_link(self, item): class EventsSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Event - fields = ['id', 'title', 'description', 'start_time', 'end_time', 'location', 'address', 'host', 'email', 'event_url', 'category'] + fields = ['id', 'title', 'description', 'start_time', 'end_time', 'location', 'address', 'host', 'email', 'event_url', 'hash', 'category'] class EventsViewSet(viewsets.ModelViewSet): serializer_class = EventsSerializer diff --git a/home/templates/home/stream_blocks/links.html b/home/templates/home/stream_blocks/links.html index 308c38325..02268b6af 100644 --- a/home/templates/home/stream_blocks/links.html +++ b/home/templates/home/stream_blocks/links.html @@ -21,7 +21,7 @@

Events Around Campus

- + @@ -30,7 +30,7 @@

Upcoming

{% for event in events %} {% if not forloop.counter0 == 0 %}
  • - + {{event.display_time}} {{event.title}}
  • diff --git a/ubyssey/static_src/src/js/components/Events/calendar.jsx b/ubyssey/static_src/src/js/components/Events/calendar.jsx index 71a3fb377..73768d335 100644 --- a/ubyssey/static_src/src/js/components/Events/calendar.jsx +++ b/ubyssey/static_src/src/js/components/Events/calendar.jsx @@ -15,10 +15,10 @@ function getDateString(date) { } else { str = str + String(date.getMonth()+1) + "-"; } - if (String(date.getDay()).length < 2) { - str = str + "0" + String(date.getDay()); + if (String(date.getDate()).length < 2) { + str = str + "0" + String(date.getDate()); } else { - str = str + String(date.getDay()); + str = str + String(date.getDate()); } return str; } @@ -221,9 +221,10 @@ function EventsOptions() { 'description': capitalize(category) + " Around Campus collected by The Ubyssey", 'url': 'https://ubyssey.ca/events/?category=' + category, }; - } + document.getElementsByTagName("title")[0].innerHTML = meta.title + return ( <>
    @@ -428,11 +429,11 @@ function EventsCalendar({events}) { {day.day_of_week} {day.day}.