Browse Source

newsletter done

main
Cailean Finn 5 months ago
parent
commit
8cf033360c
  1. 171
      app.py
  2. 84
      static/assets/styles.css
  3. 1
      templates/article.html
  4. 3
      templates/base.html
  5. 1
      templates/cn-home.html
  6. 51
      templates/newsletter.html
  7. 4
      templates/publications.html
  8. 26
      templates/test.html

171
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/<string:title>', methods=['GET'])(self.generate_newsletter)
self.route('/publications', methods=['GET'])(self.fetch_publications)
self.route('/meetups', methods=['GET'])(self.fetch_meetups)
self.route('/<string:title>', methods=['GET'])(self.page_content)
self.route('/favicon.ico')(self.favicon)
self.route('/archive/<string:collection>', 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)

84
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 {

1
templates/article.html

@ -6,7 +6,6 @@
<div class='article-cont'>
{{ content | safe }}
</div>
<div class='foot'></div>
</div>
{% endblock %}

3
templates/base.html

@ -23,8 +23,5 @@
{% endblock %}
</div>
</div>
<div class="footer">
<a href="https://wiki.conceptnull.org/" target="_blank"><div style="display: inline-flex; text-align: center; justify-content: center; align-items: center; line-height: 100%; padding: 3.3px; font-size: 12px; font-style: normal; object-fit: contain; background: rgb(255, 255, 255); color: rgb(0, 0, 0); text-shadow: rgb(8, 0, 255) 0px 3px 3px; flex-wrap: wrap;"><span style="display:flex;">hypertext connection<img src="https://i-love-everything.com/buttons/img/67.gif" style="max-height: 32px;"></span><span style="width:100%;text-shadow:0 0 5px #fff">𓆝 𓆟 𓆞 𓆝</span></div></a>
</div>
</body>
</html>

1
templates/cn-home.html

@ -6,6 +6,5 @@
<div class='article-cont'>
{{ content | safe }}
</div>
<div class='foot'></div>
</div>
{% endblock %}

51
templates/newsletter.html

@ -0,0 +1,51 @@
{% extends "base.html" %}
{% block title %}Ø | {{ title }}{% endblock %}
{% block content %}
<div class='content-cont'>
<h1 class='content-header'>{{ title }}</h1>
<div class='article-cont'>
{{ content | safe }}
</div>
<div class='event-cont'>
{% for key, value in events.items() %}
<hr>
<h2 class='event-heading'>Spotlight 🔦</h2>
<hr>
<div class='list-events'>
{% for data in value %}
<div class='event'>
<h4 class='event-name'><b>{{ data.name }}</b></h4>
<p class='event-org'>{{ data.org }}</p>
<p class='event-location'><b>Location: </b>{{ data.location }}</p>
<p class='event-deadline'><b>Deadline: </b>{{ data.deadline }}</p>
<p class='event-text'>{{ data.text }}</p>
<a href={{ data.source }} target='_blank'><p class='event-link'><b>Link ↗</b></p></a>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<div class='opp-cont'>
{% for key, value in opportunities.items() %}
<hr>
<h2 class='opp-type-heading'>{{ key }}</h2>
<hr>
<div class='list-opp'>
{% for data in value %}
<div class='opp'>
<h4 class='opp-name'><b>{{ data.name }}</b></h4>
<p class='opp-deadline'><b>Deadline:</b> {{ data.deadline }}</p>
<p class='opp-location'><i>{{ data.location }}</i></p>
<p class='opp-text'>{{ data.text }}</p>
<a href={{ data.source }} target='_blank'><p class='opp-link'><b>Link ↗</b></p></a>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
{% endblock %}

4
templates/publications.html

@ -6,9 +6,9 @@
<a class='section-header' href='/archive/newsletters'><div class='section-title'><div style="display: inline-flex; text-align: center; justify-content: center; align-items: center; line-height: 100%; padding: 2.2px; font-size: 25px; font-style: normal; object-fit: contain; font-family: monospace; color: black; width: 100%;">NEWSLETTERS<br><!--<img src="https://i-love-everything.com/buttons/img/34.gif" style="max-height: 32px;">--></div></div></a>
<div class='section-group'>
{% for key, values in newsletters.items() %}
<div class='section-element'><a href="{{ url_for('page_content', title=values.title) }}">↘ {{ values.title }}</a></div>
<div class='section-element'><a href="/newsletter{{ url_for('page_content', title=values.title) }}">↘ {{ values.title }}</a></div>
<div class='section-date'>{{ values.date }}</div>
<div class='section-img'><a href="{{ url_for('page_content', title=values.title) }}"><img src="{{ values.source}}"></a></div>
<div class='section-img'><a href="/newsletter{{ url_for('page_content', title=values.title) }}"><img src="{{ values.source}}"></a></div>
{% endfor %}
</div>
</div>

26
templates/test.html

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test</title>
<link rel="stylesheet" href="{{ url_for('static', filename='assets/styles.css') }}">
<body>
<div class='main-cont'>
<div class='nav-cont'>
<div class='header-title'>
<a href="/">CØNCEPTNULL</a>
</div>
<div class='nav-element-cont'>
<p>test</p>
</div>
</div>
<div class="content">
</div>
</div>
<div class="footer">
<a href="https://wiki.conceptnull.org/" target="_blank"><div style="display: inline-flex; text-align: center; justify-content: center; align-items: center; line-height: 100%; padding: 3.3px; font-size: 12px; font-style: normal; object-fit: contain; background: rgb(255, 255, 255); color: rgb(0, 0, 0); text-shadow: rgb(8, 0, 255) 0px 3px 3px; flex-wrap: wrap;"><span style="display:flex;">hypertext connection<img src="https://i-love-everything.com/buttons/img/67.gif" style="max-height: 32px;"></span><span style="width:100%;text-shadow:0 0 5px #fff">𓆝 𓆟 𓆞 𓆝</span></div></a>
</div>
</body>
</html>
Loading…
Cancel
Save