From 8cf033360ccb3ac74b2175f1716dad32aea1770f Mon Sep 17 00:00:00 2001 From: Cailean Finn Date: Fri, 10 May 2024 15:24:48 +0100 Subject: [PATCH] newsletter done --- app.py | 171 +++++++++++++++++++++++++++++++++--- static/assets/styles.css | 84 ++++++++++++++++-- templates/article.html | 1 - templates/base.html | 3 - templates/cn-home.html | 1 - templates/newsletter.html | 51 +++++++++++ templates/publications.html | 4 +- templates/test.html | 26 ++++++ 8 files changed, 313 insertions(+), 28 deletions(-) create mode 100644 templates/newsletter.html create mode 100644 templates/test.html diff --git a/app.py b/app.py index e633c63..9184678 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,9 @@ from flask import Flask, render_template, Response import requests +import re from bs4 import BeautifulSoup from datetime import datetime +from dateutil.relativedelta import relativedelta class WikiApp(Flask): @@ -14,12 +16,153 @@ class WikiApp(Flask): # Define routes # self.route('/', methods=['GET'])(self.homepage) self.route('/', methods=['GET'])(self.homepage_new) + self.route('/newsletter/', methods=['GET'])(self.generate_newsletter) self.route('/publications', methods=['GET'])(self.fetch_publications) self.route('/meetups', methods=['GET'])(self.fetch_meetups) self.route('/', methods=['GET'])(self.page_content) self.route('/favicon.ico')(self.favicon) self.route('/archive/', methods=['GET'])(self.get_collection) + + def generate_newsletter(self, title): + content, title, date = self.fetch_page(title) + given_date = datetime.strptime(date, "%Y-%m-%d") + new_date_opp = given_date + relativedelta(months=2) + new_date_events = given_date + relativedelta(weeks=2) + opportunites_dict = self.fetch_opportunities(given_date.date(), new_date_opp.date()) + events_dict = self.fetch_events(given_date.date(), new_date_events.date()) + return render_template('newsletter.html', nav_elements=self.get_nav_menu(), content=content, title=title, events=events_dict, opportunities=opportunites_dict) + + def fetch_opportunities(self, pub_date, future_date): + all_opportunities = self.fetch_all_opportunities(pub_date, future_date) + if not all_opportunities: + return {} + else: + titles = '' + for value in all_opportunities.values(): + if isinstance(value, list): + for entry in value: + titles += entry['pagetitle'] + '|' + + titles = titles[:-1] + resp = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={ + 'action': 'query', + 'titles': titles, + 'format': 'json', + 'prop': 'extracts', + 'exlimit': '20', + 'explaintext': 'true', + 'exintro': 'true' + }) + + data = resp.json() + opp_data = data.get('query', {}).get('pages', {}) + + for residency_entry in all_opportunities.values(): + for open_call_entry in opp_data.values(): + if residency_entry[0]['pagetitle'] == open_call_entry['title']: + residency_entry[0]['text'] = open_call_entry['extract'] + + sorted_data = {key: sorted(value, key=lambda x: x['deadline'], reverse=True) for key, value in all_opportunities.items()} + return sorted_data + + def fetch_all_opportunities(self, pub_date, future_date): + opp_page_list = {} + categories = ['Opportunities'] + for category in categories: + response = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={'action': 'ask', 'query': '[[Concept:'+category+']] [[Opportunities:Deadline::<=' + future_date.strftime("%Y-%m-%d") + ']] [[Opportunities:Deadline::>='+ pub_date.strftime("%Y-%m-%d") + ']] |?Opportunities:Deadline|?Opportunities:Name|?Opportunities:Location|?Opportunities:Organiser/s|?Opportunities:Type|?Opportunities:Source', 'format': 'json', 'formatversion': '2'}) + data = response.json() + + opp_info = {} + if not data['query']['results']: + return {} + else: + for page_title, page_data in data['query']['results'].items(): + if 'printouts' in page_data and 'Opportunities:Deadline' in page_data['printouts']: + type = page_data['printouts']['Opportunities:Type'][0] + name = page_data['printouts']['Opportunities:Name'][0] + deadline = page_data['printouts']['Opportunities:Deadline'][0]['raw'] + deadline = deadline[2:] + lol = datetime.strptime(deadline, "%Y/%m/%d") + formatted_deadline = lol.strftime("%d-%m-%Y") + location = page_data['printouts']['Opportunities:Location'][0] + source = page_data['printouts']['Opportunities:Source'][0] + org = page_data['printouts']['Opportunities:Organiser/s'][0]['fulltext'] + + opp_info = {'pagetitle': page_title, 'name': name, 'deadline': formatted_deadline, 'location': location, 'source' : source, 'org': org, 'text': ''} + + if type not in opp_page_list: + opp_page_list[type] = [] + + opp_page_list[type].append(opp_info) + + return opp_page_list + def fetch_events(self, pub_date, future_date): + all_events = self.fetch_all_events(pub_date, future_date) + if not all_events: + return {} + else: + titles = '' + + for value in all_events.values(): + if isinstance(value, list): + for entry in value: + titles += entry['pagetitle'] + '|' + + titles = titles[:-1] + resp = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={ + 'action': 'query', + 'titles': titles, + 'format': 'json', + 'prop': 'extracts', + 'exlimit': '20', + 'explaintext': 'true', + 'exintro': 'true' + }) + + data = resp.json() + opp_data = data.get('query', {}).get('pages', {}) + + for residency_entry in all_events.values(): + for open_call_entry in opp_data.values(): + if residency_entry[0]['pagetitle'] == open_call_entry['title']: + residency_entry[0]['text'] = open_call_entry['extract'] + + sorted_data = {key: sorted(value, key=lambda x: x['deadline'], reverse=False) for key, value in all_events.items()} + return sorted_data + + def fetch_all_events(self, pub_date, future_date): + opp_page_list = {} + categories = ['Events'] + for category in categories: + response = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={'action': 'ask', 'query': '[[Concept:'+category+']] [[Event:Date::<=' + future_date.strftime("%Y-%m-%d") + ']] [[Event:Date::>='+ pub_date.strftime("%Y-%m-%d") + ']] |?Event:Date|?Event:Name|?Event:Location|?Event:Organiser/s|?Event:Source', 'format': 'json', 'formatversion': '2'}) + data = response.json() + + opp_info = {} + if not data['query']['results']: + return {} + else: + for page_title, page_data in data['query']['results'].items(): + if 'printouts' in page_data and 'Event:Date' in page_data['printouts']: + type = 'Events' + name = page_data['printouts']['Event:Name'][0] + deadline = page_data['printouts']['Event:Date'][0]['raw'] + deadline = deadline[2:] + lol = datetime.strptime(deadline, "%Y/%m/%d") + formatted_deadline = lol.strftime("%d-%m-%Y") + location = page_data['printouts']['Event:Location'][0] + source = page_data['printouts']['Event:Source'][0] + org = page_data['printouts']['Event:Organiser/s'][0]['fulltext'] + + opp_info = {'pagetitle': page_title, 'name': name, 'deadline': formatted_deadline, 'location': location, 'source' : source, 'org': org, 'text': ''} + + if type not in opp_page_list: + opp_page_list[type] = [] + + opp_page_list[type].append(opp_info) + + return opp_page_list + def homepage_new(self): pages = ['Homepage'] homepage_content = '' @@ -39,20 +182,16 @@ class WikiApp(Flask): publication_page_list = self.fetch_all_pages(concepts) updated_cat_list = self.fetch_pages_cat(publication_page_list) projects = updated_cat_list.get('Projects', []) + sorted_prj = dict(sorted(projects.items(), key=lambda x: x[1]['date'])) newsletters = updated_cat_list.get('Newsletters', []) + sorted_nl = dict(sorted(newsletters.items(), key=lambda x: x[1]['date'])) nav_elements = self.get_nav_menu() - return render_template('publications.html', projects=projects, newsletters=newsletters, nav_elements=nav_elements) + return render_template('publications.html', projects=sorted_prj, newsletters=sorted_nl, nav_elements=nav_elements) def fetch_meetups(self): - concepts = ['Meetups'] - # publication_page_list = self.fetch_all_pages(concepts) - # updated_cat_list = self.fetch_pages_cat(publication_page_list) - # meetups = updated_cat_list.get('Meetups', []) - nav_elements = self.get_nav_menu() - meetup_content = self.fetch_page('Meetups') - - return render_template('meetups.html', content=meetup_content, nav_elements=nav_elements) + meetup_content, page_title = self.fetch_page('Meetups') + return render_template('meetups.html', content=meetup_content, nav_elements=self.get_nav_menu() ) def fetch_pages_cat(self, category_page_list): all_pages_string = '|'.join(page for pages in category_page_list.values() for page in pages) @@ -82,7 +221,7 @@ class WikiApp(Flask): def fetch_all_pages(self, categories): category_page_list = {} for category in categories: - response = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={'action': 'ask', 'query': '[[Concept:'+category+']]|?Article:Date', 'format': 'json', 'formatversion': '2'}) + response = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={'action': 'ask', 'query': '[[Concept:'+category+']]|?Article:Date|?Article:Draft', 'format': 'json', 'formatversion': '2'}) data = response.json() page_title_timestamps = {} for page_title, page_data in data['query']['results'].items(): @@ -91,7 +230,8 @@ class WikiApp(Flask): raw_timestamp = raw_timestamp[2:] lol = datetime.strptime(raw_timestamp, "%Y/%m/%d") formatted_date = lol.strftime("%d.%m.%Y") - page_title_timestamps[page_title] = {'date': formatted_date} + if(page_data['printouts']['Article:Draft'][0] == 'f'): + page_title_timestamps[page_title] = {'date': formatted_date, 'draft': page_data['printouts']['Article:Draft'][0]} category_page_list[category] = page_title_timestamps return category_page_list @@ -126,7 +266,13 @@ class WikiApp(Flask): page_title = data['parse']['title'] page_content = data['parse']['text']['*'] page_content = self.fix_html(page_content) - return page_content + page_date = re.search(r'\d{4}-\d{2}-\d{2}', data['parse']['text']['*']) + + if(page_date): + date = page_date.group(0) + return page_content, page_title, date + else: + return page_content, page_title def get_nav_menu(self): response = requests.get(self.MEDIAWIKI_BASE_URL + self.BASE_API, params={'action': 'ask', 'query': '[[Concept:MainNavigation]]', 'format': 'json', 'formatversion': '2'}) @@ -193,7 +339,6 @@ class WikiApp(Flask): resp = self.fetch_all_pages([collection]) data = self.fetch_pages_cat(resp) return render_template('collection.html', nav_elements=self.get_nav_menu(), title=collection, collection=resp[collection]) - # Route for favicon.ico to prevent Flask from raising an error def favicon(self): return Response('', status=200) diff --git a/static/assets/styles.css b/static/assets/styles.css index c0521f8..8278844 100644 --- a/static/assets/styles.css +++ b/static/assets/styles.css @@ -97,14 +97,25 @@ body { .content-cont { overflow: scroll; overflow-x: hidden; - height: 100vh; + height: calc(100vh - 100px); margin-left: 40px; margin-top: 100px; - margin-right: 40px; + /* margin-top: 100px; */ + /* margin-bottom: 100px; */ + /* margin-right: 40px; */ } .article-cont { width: 60%; + display: flex; + flex-direction: column; + gap: 20px; + margin-bottom: 20px; +} + +.article-cont p { + font-size: 17.5px; + line-height: 30px; } .content-cont h1{ @@ -114,12 +125,12 @@ body { .content-cont h2{ font-size: 30px; - text-decoration: underline; + text-decoration: none; } .content-cont h3{ font-size: 25px; - text-decoration: underline ; + text-decoration: none ; } .content-cont h4{ @@ -127,10 +138,10 @@ body { } -.content-cont p { +/* .content-cont p { font-size: 17.5px; line-height: 30px; -} +} */ .content-cont .article-cont a { color: red; @@ -138,6 +149,16 @@ body { font-style: oblique; } +.content-cont .opp-cont a { + color: red; + text-decoration: none; +} + +.content-cont .event-cont a { + color: red; + text-decoration: none; +} + .content-cont ul { font-size: 20px; border-style: none; @@ -162,9 +183,9 @@ body { } -.content-cont::-webkit-scrollbar { +/* .content-cont::-webkit-scrollbar { display: none; -} +} */ .content-header { /* padding-top: 160px; */ @@ -313,6 +334,53 @@ body { width: auto; } +/* Newsletter Styling*/ +.event-cont { + width: 60%; + font-size: 17.5px; +} + +.event { + margin-bottom: 10px; + display: block; +} + +.opp { + margin-bottom: 10px; + display: block; +} + +.list-events { + display: flex; + flex-direction: column; + gap: 10px; +} + +.list-opp { + display: flex; + flex-direction: column; + gap: 10px; +} + +.opp-cont{ + width: 60%; + font-size: 17.5px; + margin-bottom: 25px; +} + +hr { + border: 0; + border-top: thin solid #243588; + clear:both; + display:block; + width: 100%; + background-color:#000000; + height: 1px; +} + + + + /* Keyframes for the spin animation */ @keyframes spin { diff --git a/templates/article.html b/templates/article.html index f06c7f9..cacd529 100644 --- a/templates/article.html +++ b/templates/article.html @@ -6,7 +6,6 @@
{{ content | safe }}
-
{% endblock %} diff --git a/templates/base.html b/templates/base.html index 7947749..74fa9f8 100644 --- a/templates/base.html +++ b/templates/base.html @@ -23,8 +23,5 @@ {% endblock %} - diff --git a/templates/cn-home.html b/templates/cn-home.html index 1c8f8b4..f722035 100644 --- a/templates/cn-home.html +++ b/templates/cn-home.html @@ -6,6 +6,5 @@
{{ content | safe }}
-
{% endblock %} diff --git a/templates/newsletter.html b/templates/newsletter.html new file mode 100644 index 0000000..9c226e8 --- /dev/null +++ b/templates/newsletter.html @@ -0,0 +1,51 @@ +{% extends "base.html" %} +{% block title %}Ø | {{ title }}{% endblock %} +{% block content %} +
+

{{ title }}

+
+ {{ content | safe }} +
+
+ {% for key, value in events.items() %} +
+

Spotlight 🔦

+
+
+ {% for data in value %} +
+

{{ data.name }}

+

{{ data.org }}

+

Location: {{ data.location }}

+

Deadline: {{ data.deadline }}

+

{{ data.text }}

+ +
+ {% endfor %} +
+ {% endfor %} +
+ +
+ {% for key, value in opportunities.items() %} +
+

{{ key }}

+
+
+ {% for data in value %} +
+

{{ data.name }}

+

Deadline: {{ data.deadline }}

+

{{ data.location }}

+

{{ data.text }}

+ +
+ {% endfor %} +
+ {% endfor %} +
+ + +
+ +{% endblock %} diff --git a/templates/publications.html b/templates/publications.html index 1d755b3..5722354 100644 --- a/templates/publications.html +++ b/templates/publications.html @@ -6,9 +6,9 @@
NEWSLETTERS
{% for key, values in newsletters.items() %} - + -
+
{% endfor %}
diff --git a/templates/test.html b/templates/test.html new file mode 100644 index 0000000..85da479 --- /dev/null +++ b/templates/test.html @@ -0,0 +1,26 @@ + + + + + + test + + +
+ +
+
+
+ + +