Bläddra i källkod

Merge pull request #3 from COPIM/joel

Merging joel changes into main branch
practices
Simon Bowie 1 år sedan
förälder
incheckning
f90a3b2358
Inget konto är kopplat till bidragsgivarens mejladress
9 ändrade filer med 234 tillägg och 163 borttagningar
  1. +29
    -18
      web/app/book.py
  2. +23
    -14
      web/app/practice.py
  3. +6
    -0
      web/app/relationships.py
  4. +9
    -0
      web/app/static/src/main.css
  5. +49
    -65
      web/app/static/styles/main.css
  6. +8
    -6
      web/app/templates/about.html
  7. +17
    -9
      web/app/templates/base.html
  8. +66
    -35
      web/app/templates/resources.html
  9. +27
    -16
      web/app/tool.py

+ 29
- 18
web/app/book.py Visa fil

@@ -21,15 +21,23 @@ book = Blueprint('book', __name__)
# route for displaying all books in database
@book.route('/books')
def get_books():
# GET PARAMETERS
# get URL parameters for views and pages
view = request.args.get('view')
page = request.args.get('page', 1, type=int)
# set resource type
resource_type = 'book'
# get introductory paragraph Markdown
with open('content/books.md', 'r') as f:
intro_text = f.read()
intro_text = markdown.markdown(intro_text)
view = request.args.get('view')
resource_type = 'book'
books_query = Resource.query.filter_by(type=resource_type).order_by(func.random())

# DATABASE QUERY
books_query = Resource.query.filter_by(type=resource_type)

# FILTERING
for key in request.args.keys():
if key != 'view':
if key != 'view' and key != 'page':
if (key == 'practice' and request.args.get(key) != ''):
books_1 = books_query.join(Relationship, Relationship.first_resource_id == Resource.id, isouter=True).filter(Relationship.second_resource_id==request.args.get(key))
books_2 = books_query.join(Relationship, Relationship.second_resource_id == Resource.id, isouter=True).filter(Relationship.first_resource_id==request.args.get(key))
@@ -37,26 +45,29 @@ def get_books():
if (key != 'practice' and request.args.get(key) != ''):
kwargs = {key: request.args.get(key)}
books_query = books_query.filter_by(**kwargs)
# finalise the query
books = books_query.all()
# get number of books
count = len(books)
# reorder books by book name
books = sorted(books, key=lambda d: d.__dict__['name'].lower())
# render Markdown as HTML
for book in books:
book.description = markdown.markdown(book.description)
if view != 'list':
# append relationships to each book
append_relationships_multiple(books)
# get values for filters

# finalise the query and add pagination
books = books_query.order_by(Resource.name).paginate(page=page, per_page=25)

# FILTERS MENU
# get values for filter menu dropdowns
# practices
practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all()
# year
year_filter = get_filter_values('year', resource_type)
# typology
typology_filter = get_filter_values('typology', resource_type)
return render_template('resources.html', resources=books, type=resource_type, practices_filter=practices_filter, year_filter=year_filter, typology_filter=typology_filter, count=count, view=view, intro_text=intro_text)

# POST-FILTERING PROCESSING
# if view is 'expanded' then append relationships
if view != 'list':
# append relationships to each book
append_relationships_multiple_paginated(books)
# render Markdown as HTML
for book in books:
book.description = markdown.markdown(book.description)
return render_template('resources.html', resources=books, type=resource_type, practices_filter=practices_filter, year_filter=year_filter, typology_filter=typology_filter, view=view, page=page, intro_text=intro_text)

# route for displaying a single book based on the ID in the database
@book.route('/books/<int:book_id>')

+ 23
- 14
web/app/practice.py Visa fil

@@ -22,14 +22,22 @@ practice = Blueprint('practice', __name__)
# route for displaying all practices in database
@practice.route('/practices')
def get_practices():
# GET PARAMETERS
# get URL parameters for views and pages
view = request.args.get('view')
page = request.args.get('page', 1, type=int)
# set resource type
resource_type = 'practice'
# get introductory paragraph Markdown
with open('content/practices.md', 'r') as f:
with open('content/books.md', 'r') as f:
intro_text = f.read()
intro_text = markdown.markdown(intro_text)
view = request.args.get('view')
practices = Resource.query.filter_by(type='practice').order_by(func.random())
# temporarily removing incomplete practices from main list
practices = Resource.query.filter(

# DATABASE QUERY
practices_query = Resource.query.filter_by(type=resource_type)

# temporarily removing incomplete practices from main list
practices_query = Resource.query.filter(
or_(
Resource.id==53,
Resource.id==56,
@@ -40,16 +48,17 @@ def get_practices():
Resource.id==65,
Resource.id==66
))
# finalise the query
practices = practices.all()
# get number of practices
count = len(practices)
# reorder practices by practice name
practices = sorted(practices, key=lambda d: d.__dict__['name'].lower())
# finalise the query and add pagination
practices = practices_query.order_by(Resource.name).paginate(page=page, per_page=25)
# POST-FILTERING PROCESSING
# if view is 'expanded' then append relationships
if view != 'list':
# append relationships to each practice
append_relationships_multiple(practices)
return render_template('resources.html', resources=practices, type='practice', count=count, view=view, intro_text=intro_text)
# append relationships to each book
append_relationships_multiple_paginated(practices)

return render_template('resources.html', resources=practices, type='practice', view=view, page=page, intro_text=intro_text)

# route for displaying a single practice based on the ID in the database
@practice.route('/practices/<int:practice_id>')

+ 6
- 0
web/app/relationships.py Visa fil

@@ -69,6 +69,12 @@ def append_relationships_multiple(resources):
resources[index] = append_relationships(resource)
return resources

# function to append relationships to a pagination object of resources
def append_relationships_multiple_paginated(pagination):
for item in (pagination.items):
item = append_relationships(item)
return pagination

# function to add a relationship to a linked resource
def add_relationship(resource_id, linked_resource_id):
first_resource_id = resource_id

+ 9
- 0
web/app/static/src/main.css Visa fil

@@ -407,4 +407,13 @@ details[open]::details-content {

.slider.round:before {
border-radius: 50%;
}

hr {
border:0;
border-bottom: 2px solid;
width:50%;
margin:4ch;
margin-left:0;
}

+ 49
- 65
web/app/static/styles/main.css Visa fil

@@ -507,6 +507,17 @@ video {
max-width: 1536px;
}
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
.pointer-events-none {
pointer-events: none;
}
@@ -564,22 +575,22 @@ video {
.m-1 {
margin: 0.25rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mx-4 {
margin-left: 1rem;
margin-right: 1rem;
}
.my-8 {
margin-top: 2rem;
margin-bottom: 2rem;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.mx-1 {
margin-left: 0.25rem;
margin-right: 0.25rem;
}
.mb-2 {
margin-bottom: 0.5rem;
}
@@ -613,6 +624,9 @@ video {
.mt-3 {
margin-top: 0.75rem;
}
.mt-4 {
margin-top: 1rem;
}
.block {
display: block;
}
@@ -878,9 +892,18 @@ video {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.opacity-30 {
opacity: 0.3;
}
.opacity-40 {
opacity: 0.4;
}
.opacity-50 {
opacity: 0.5;
}
.opacity-70 {
opacity: 0.7;
}
.grayscale {
--tw-grayscale: grayscale(100%);
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
@@ -1114,10 +1137,6 @@ select {
gap: 1rem;
}

.facts a {
text-decoration: underline;
}

@media (min-width: 1024px) {

.facts {
@@ -1148,12 +1167,6 @@ h2,h3 {
hyphens: auto;
}

h2 {
font-family: 'ag-fett';
font-size: 1.5rem;
line-height: 1.1;
}

h3 {
font-family: 'ag-fett';
}
@@ -1456,6 +1469,15 @@ a.menuitem:hover {
border-radius: 50%;
}

hr {
border:0;
border-bottom: 2px solid;
width:50%;
margin:4ch;
margin-left:0;
}

.hover\:opacity-60:hover {
opacity: 0.6;
}
@@ -1490,6 +1512,11 @@ a.menuitem:hover {
margin: 1rem;
}

.lg\:my-16 {
margin-top: 4rem;
margin-bottom: 4rem;
}

.lg\:my-8 {
margin-top: 2rem;
margin-bottom: 2rem;
@@ -1563,6 +1590,10 @@ a.menuitem:hover {
grid-template-columns: repeat(3, minmax(0, 1fr));
}

.lg\:grid-cols-\[2fr\2c 1fr\] {
grid-template-columns: 2fr 1fr;
}

.lg\:grid-cols-\[52rem\2c 30rem\] {
grid-template-columns: 52rem 30rem;
}
@@ -1604,11 +1635,6 @@ a.menuitem:hover {
padding: 0px;
}

.lg\:py-0 {
padding-top: 0px;
padding-bottom: 0px;
}

.lg\:pr-8 {
padding-right: 2rem;
}
@@ -1621,46 +1647,4 @@ a.menuitem:hover {
.lg\:opacity-100 {
opacity: 1;
}
}

.sr-only{
position:absolute;
left:-10000px;
top:auto;
width:1px;
height:1px;
overflow:hidden;
}

#about {
display: inline-block;
width: 60%;
}

#colophon {
float: right;
width: 30%;
}

hr {
clear: both;
margin-bottom: 15%;
visibility: hidden;
}

blockquote {
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
}
blockquote:before {
color: #ccc;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline;
}

+ 8
- 6
web/app/templates/about.html Visa fil

@@ -2,12 +2,14 @@

{% block content %}

<div class="cell-margin text max-w-[65ch] lg:m-16 text-lg lg:text-xl">
{{ about_text|safe }}
</div>

<div id="colophon" class="cell-margin text">
{{ colophon_text | safe }}
<div class="grid lg:grid-cols-[2fr,1fr] gap-4 container">
<div class="cell-margin text max-w-[65ch] lg:m-16 text-lg lg:text-xl">
{{ about_text|safe }}
</div>

<div id="colophon" class="cell-margin text lg:my-16">
{{ colophon_text | safe }}
</div>
</div>

{% endblock %}

+ 17
- 9
web/app/templates/base.html Visa fil

@@ -109,8 +109,8 @@

<form action="{{ url_for('search.basic_search') }}" class="hidden lg:block">
<label id="search-label" for="siteSearch" class="sr-only">Search</label>
<input type="text" name="query" class="text-center h-full text-base border-l-2 border-black pl-1" aria-labelledby="search-label"
placeholder="Search" hx-get="{{ url_for('search.basic_search') }}"
<input type="text" name="query" class="text-center h-full text-base border-l-2 border-black pl-1"
aria-labelledby="search-label" placeholder="Search" hx-get="{{ url_for('search.basic_search') }}"
hx-trigger="keyup changed delay:500ms, search" hx-target="main" hx-select="main">
<input class="hidden" type="submit" hidden />
</form>
@@ -163,15 +163,15 @@

{% macro resource_list(resource, loop, show_number=true) %}
<div class="border-b-2 border-black">
<a class="flex gap-6 h-full py-2 hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}" {% if
resource['type']=='book' %} href="{{ url_for('book.show_book', book_id=resource['id']) }}" {% endif %} {% if
resource['type']=='tool' %} href="{{ url_for('tool.show_tool', tool_id=resource['id']) }}" {% endif %} {% if
<a class="flex gap-6 h-full py-2 {% if resource.status == 'no longer maintained' %} opacity-30 {% endif %} {% if resource.status == 'minimally maintained' %} opacity-70 {% endif %} hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}"
{% if resource['type']=='book' %} href="{{ url_for('book.show_book', book_id=resource['id']) }}" {% endif %} {%
if resource['type']=='tool' %} href="{{ url_for('tool.show_tool', tool_id=resource['id']) }}" {% endif %} {% if
resource['type']=='practice' %} href="{{ url_for('practice.show_practice', practice_id=resource['id']) }}" {%
endif %} hx-target="#modal-content" hx-select="main" hx-swap="innerHTML" @click="openModal()">

<div class="w-[4.5rem] shrink-0 lg:w-[12rem] text-center ">
{% if show_number %}
{{loop.index}} / {{count}}
{{ (page -1) * resources.per_page + loop.index }} / {{ resources.total }}
{% else %}
<div class="capitalize inline-block min-w-[5rem] px-4 ">{{ resource['type'] }}</div>
{% endif %}
@@ -193,12 +193,12 @@
{% endmacro %}

{% macro resource_with_related(resource, loop, show_number=true) %}
<div class="w-full border-b-2 border-black fade-right">
<div class="w-full border-b-2 border-black fade-right ">
<div class="content w-full py-8 overflow-x-auto">
<div class="grid lg:grid-rows-[auto,auto,auto] grid-flow-col w-fit pr-40 ">
<div class="w-[4.5rem] shrink-0 lg:w-[12rem] text-center px-2 py-4 ">
{% if show_number %}
<p>{{loop.index}} / {{count}}</p>
<p>{{ (page -1) * resources.per_page + loop.index }} / {{ resources.total }}</p>
{% else %}
<p class="capitalize">{{ resource['type'] }}</p>
{% endif %}
@@ -258,7 +258,7 @@
{% endmacro %}

{% macro resource_lead(resource,size=1) %}
<a class="block lead-inner-link overflow-hidden h-full hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}"
<a class="block lead-inner-link overflow-hidden h-full {% if resource.status == 'no longer maintained' %} opacity-30 {% endif %} {% if resource.status == 'minimally maintained' %} opacity-50 {% endif %} hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}"
{% if resource['type']=='book' %} href="{{ url_for('book.show_book', book_id=resource['id']) }}" {% endif %} {% if
resource['type']=='tool' %} href="{{ url_for('tool.show_tool', tool_id=resource['id']) }}" {% endif %} {% if
resource['type']=='practice' %} href="{{ url_for('practice.show_practice', practice_id=resource['id']) }}" {% endif
@@ -282,6 +282,14 @@
{{ resource['description'] | truncate(150) | safe }}
</div>

{% if resource.status == 'no longer maintained' %}
<div class="mt-4 mb-8">(No longer maintained)</div>
{% endif %}

{% if resource.status == 'minimally maintained' %}
<div class="mt-4 mb-8">(Minimally maintained)</div>
{% endif %}

</a>
{% if current_user.is_authenticated %}
<div class="">

+ 66
- 35
web/app/templates/resources.html Visa fil

@@ -67,41 +67,72 @@
<div class="-m-4 mb-4 lg:m-0">
{{ view_switch() }}
</div>
{% if practices_filter%}
{{ filter_dropdown('practice', practices_filter, 'Practices') }}
{% endif %}
{% if year_filter %}
{{ filter_dropdown_nokey('year', year_filter, 'Year') }}
{% endif %}
{% if typology_filter %}
{{ filter_dropdown_nokey('typology', typology_filter, 'Typologies') }}
{% endif %}
{% if languages_filter %}
{{ filter_dropdown_nokey('scriptingLanguage', languages_filter, 'Scripting languages') }}
{% endif %}
{% if licenses_filter %}
{{ filter_dropdown_nokey('license', licenses_filter, 'Licenses') }}
{% endif %}
{% if status_filter %}
{{ filter_dropdown_nokey('status', status_filter, 'Maintenance status') }}
{% endif %}
<a href="{{ url_for(request.endpoint) }}?view={{ view }}">Reset</a>
</div>
<div>
{% if view == 'list' %}
{% for resource in resources %}
{{ resource_list(resource, loop) }}
{% endfor %}
{% else %}
{% for resource in resources %}
{{ resource_with_related(resource, loop) }}
{% endfor %}
{% endif %}
</div>

{% if practices_filter%}
{{ filter_dropdown('practice', practices_filter, 'Practices') }}
{% endif %}
{% if year_filter %}
{{ filter_dropdown_nokey('year', year_filter, 'Year') }}
{% endif %}
{% if typology_filter %}
{{ filter_dropdown_nokey('typology', typology_filter, 'Typologies') }}
{% endif %}
{% if languages_filter %}
{{ filter_dropdown_nokey('scriptingLanguage', languages_filter, 'Scripting languages') }}
{% endif %}
{% if licenses_filter %}
{{ filter_dropdown_nokey('license', licenses_filter, 'Licenses') }}
{% endif %}
{% if status_filter %}
{{ filter_dropdown_nokey('status', status_filter, 'Maintenance status') }}
{% endif %}

{% if not practices_filter and not year_filter and not typology_filter and not languages_filter and not
licenses_filter and not status_filter %}
{% else %}
<a href="{{ url_for(request.endpoint) }}?view={{ view }}">Reset</a>
{% endif %}

</div>

<div>
{% if view == 'list' %}
{% for resource in resources %}
{{ resource_list(resource, loop) }}
{% endfor %}
{% else %}
{% for resource in resources %}
{{ resource_with_related(resource, loop) }}
{% endfor %}
{% endif %}
</div>

{% macro render_pagination(pagination) %}

<!-- <div class=page-items>
{{ pagination.first }} - {{ pagination.last }} of {{ pagination.total }}
</div> -->

<div class="pagination p-4">
<span class="mr-2">Page</span>
{% for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}

<a href="{{ url_for(request.endpoint, page=page) }}{% for key in request.args %}{% if key != 'page' %}&{{ key }}={{ request.args.get(key) }}{% endif %}{% endfor %}"
class="mx-1">{{
page }}</a>
{% else %}
<strong class="mx-1">{{ page }}</strong>
{% endif %}
{% else %}
<span class="ellipsis">…</span>
{% endif %}
{% endfor %}
</div>
{% endmacro %}

{{ render_pagination(resources) }}

</form>


+ 27
- 16
web/app/tool.py Visa fil

@@ -21,15 +21,23 @@ tool = Blueprint('tool', __name__)
# route for displaying all tools in database
@tool.route('/tools')
def get_tools():
# GET PARAMETERS
# get URL parameters for views and pages
view = request.args.get('view')
page = request.args.get('page', 1, type=int)
# set resource type
resource_type = 'tool'
# get introductory paragraph Markdown
with open('content/tools.md', 'r') as f:
with open('content/books.md', 'r') as f:
intro_text = f.read()
intro_text = markdown.markdown(intro_text)
view = request.args.get('view')
resource_type = 'tool'
tools_query = Resource.query.filter_by(type=resource_type).order_by(func.random())

# DATABASE QUERY
tools_query = Resource.query.filter_by(type=resource_type)

# FILTERING
for key in request.args.keys():
if key != 'view':
if key != 'view' and key != 'page':
if (key == 'practice' and request.args.get(key) != ''):
tools_1 = tools_query.join(Relationship, Relationship.first_resource_id == Resource.id, isouter=True).filter(Relationship.second_resource_id==request.args.get(key))
tools_2 = tools_query.join(Relationship, Relationship.second_resource_id == Resource.id, isouter=True).filter(Relationship.first_resource_id==request.args.get(key))
@@ -40,16 +48,12 @@ def get_tools():
if ((key != 'practice' and key != 'scriptingLanguage') and request.args.get(key) != ''):
kwargs = {key: request.args.get(key)}
tools_query = tools_query.filter_by(**kwargs)
# finalise the query
tools = tools_query.all()
# get number of tools
count = len(tools)
# reorder tools by tools name
tools = sorted(tools, key=lambda d: d.__dict__['name'].lower())
if view != 'list':
# append relationships to each tool
append_relationships_multiple(tools)
# get values for filters

# finalise the query and add pagination
tools = tools_query.order_by(Resource.name).paginate(page=page, per_page=25)

# FILTERS MENU
# get values for filter menu dropdowns
# practices
practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all()
# license
@@ -58,7 +62,14 @@ def get_tools():
languages_filter = get_filter_values('scriptingLanguage', resource_type)
# status
status_filter = get_filter_values('status', resource_type)
return render_template('resources.html', resources=tools, type=resource_type, practices_filter=practices_filter, licenses_filter=licenses_filter, languages_filter=languages_filter, status_filter=status_filter, count=count, view=view, intro_text=intro_text)
# POST-FILTERING PROCESSING
# if view is 'expanded' then append relationships
if view != 'list':
# append relationships to each book
append_relationships_multiple_paginated(tools)

return render_template('resources.html', resources=tools, type=resource_type, practices_filter=practices_filter, licenses_filter=licenses_filter, languages_filter=languages_filter, status_filter=status_filter, view=view, page=page, intro_text=intro_text)

# route for displaying a single tool based on the ID in the database
@tool.route('/tools/<int:tool_id>')

Laddar…
Avbryt
Spara