| # route for displaying all books in database | # route for displaying all books in database | ||||
| @book.route('/books') | @book.route('/books') | ||||
| def get_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 | # get introductory paragraph Markdown | ||||
| with open('content/books.md', 'r') as f: | with open('content/books.md', 'r') as f: | ||||
| intro_text = f.read() | intro_text = f.read() | ||||
| intro_text = markdown.markdown(intro_text) | 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(): | for key in request.args.keys(): | ||||
| if key != 'view': | |||||
| if key != 'view' and key != 'page': | |||||
| if (key == 'practice' and request.args.get(key) != ''): | 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_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)) | books_2 = books_query.join(Relationship, Relationship.second_resource_id == Resource.id, isouter=True).filter(Relationship.first_resource_id==request.args.get(key)) | ||||
| if (key != 'practice' and request.args.get(key) != ''): | if (key != 'practice' and request.args.get(key) != ''): | ||||
| kwargs = {key: request.args.get(key)} | kwargs = {key: request.args.get(key)} | ||||
| books_query = books_query.filter_by(**kwargs) | 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 | ||||
| practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all() | practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all() | ||||
| # year | # year | ||||
| year_filter = get_filter_values('year', resource_type) | year_filter = get_filter_values('year', resource_type) | ||||
| # typology | # typology | ||||
| typology_filter = get_filter_values('typology', resource_type) | 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 | # route for displaying a single book based on the ID in the database | ||||
| @book.route('/books/<int:book_id>') | @book.route('/books/<int:book_id>') |
| # route for displaying all practices in database | # route for displaying all practices in database | ||||
| @practice.route('/practices') | @practice.route('/practices') | ||||
| def get_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 | # 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 = f.read() | ||||
| intro_text = markdown.markdown(intro_text) | 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_( | or_( | ||||
| Resource.id==53, | Resource.id==53, | ||||
| Resource.id==56, | Resource.id==56, | ||||
| Resource.id==65, | Resource.id==65, | ||||
| Resource.id==66 | 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': | 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 | # route for displaying a single practice based on the ID in the database | ||||
| @practice.route('/practices/<int:practice_id>') | @practice.route('/practices/<int:practice_id>') |
| resources[index] = append_relationships(resource) | resources[index] = append_relationships(resource) | ||||
| return resources | 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 | # function to add a relationship to a linked resource | ||||
| def add_relationship(resource_id, linked_resource_id): | def add_relationship(resource_id, linked_resource_id): | ||||
| first_resource_id = resource_id | first_resource_id = resource_id |
| .slider.round:before { | .slider.round:before { | ||||
| border-radius: 50%; | border-radius: 50%; | ||||
| } | |||||
| hr { | |||||
| border:0; | |||||
| border-bottom: 2px solid; | |||||
| width:50%; | |||||
| margin:4ch; | |||||
| margin-left:0; | |||||
| } | } |
| max-width: 1536px; | 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 { | ||||
| pointer-events: none; | pointer-events: none; | ||||
| } | } | ||||
| .m-1 { | .m-1 { | ||||
| margin: 0.25rem; | margin: 0.25rem; | ||||
| } | } | ||||
| .mx-2 { | |||||
| margin-left: 0.5rem; | |||||
| margin-right: 0.5rem; | |||||
| } | |||||
| .mx-4 { | .mx-4 { | ||||
| margin-left: 1rem; | margin-left: 1rem; | ||||
| margin-right: 1rem; | margin-right: 1rem; | ||||
| } | } | ||||
| .my-8 { | |||||
| margin-top: 2rem; | |||||
| margin-bottom: 2rem; | |||||
| } | |||||
| .my-4 { | .my-4 { | ||||
| margin-top: 1rem; | margin-top: 1rem; | ||||
| margin-bottom: 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 { | .mb-2 { | ||||
| margin-bottom: 0.5rem; | margin-bottom: 0.5rem; | ||||
| } | } | ||||
| .mt-3 { | .mt-3 { | ||||
| margin-top: 0.75rem; | margin-top: 0.75rem; | ||||
| } | } | ||||
| .mt-4 { | |||||
| margin-top: 1rem; | |||||
| } | |||||
| .block { | .block { | ||||
| display: block; | display: block; | ||||
| } | } | ||||
| --tw-text-opacity: 1; | --tw-text-opacity: 1; | ||||
| color: rgb(255 255 255 / var(--tw-text-opacity)); | color: rgb(255 255 255 / var(--tw-text-opacity)); | ||||
| } | } | ||||
| .opacity-30 { | |||||
| opacity: 0.3; | |||||
| } | |||||
| .opacity-40 { | .opacity-40 { | ||||
| opacity: 0.4; | opacity: 0.4; | ||||
| } | } | ||||
| .opacity-50 { | |||||
| opacity: 0.5; | |||||
| } | |||||
| .opacity-70 { | |||||
| opacity: 0.7; | |||||
| } | |||||
| .grayscale { | .grayscale { | ||||
| --tw-grayscale: grayscale(100%); | --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); | 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); | ||||
| gap: 1rem; | gap: 1rem; | ||||
| } | } | ||||
| .facts a { | |||||
| text-decoration: underline; | |||||
| } | |||||
| @media (min-width: 1024px) { | @media (min-width: 1024px) { | ||||
| .facts { | .facts { | ||||
| hyphens: auto; | hyphens: auto; | ||||
| } | } | ||||
| h2 { | |||||
| font-family: 'ag-fett'; | |||||
| font-size: 1.5rem; | |||||
| line-height: 1.1; | |||||
| } | |||||
| h3 { | h3 { | ||||
| font-family: 'ag-fett'; | font-family: 'ag-fett'; | ||||
| } | } | ||||
| border-radius: 50%; | border-radius: 50%; | ||||
| } | } | ||||
| hr { | |||||
| border:0; | |||||
| border-bottom: 2px solid; | |||||
| width:50%; | |||||
| margin:4ch; | |||||
| margin-left:0; | |||||
| } | |||||
| .hover\:opacity-60:hover { | .hover\:opacity-60:hover { | ||||
| opacity: 0.6; | opacity: 0.6; | ||||
| } | } | ||||
| margin: 1rem; | margin: 1rem; | ||||
| } | } | ||||
| .lg\:my-16 { | |||||
| margin-top: 4rem; | |||||
| margin-bottom: 4rem; | |||||
| } | |||||
| .lg\:my-8 { | .lg\:my-8 { | ||||
| margin-top: 2rem; | margin-top: 2rem; | ||||
| margin-bottom: 2rem; | margin-bottom: 2rem; | ||||
| grid-template-columns: repeat(3, minmax(0, 1fr)); | 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\] { | .lg\:grid-cols-\[52rem\2c 30rem\] { | ||||
| grid-template-columns: 52rem 30rem; | grid-template-columns: 52rem 30rem; | ||||
| } | } | ||||
| padding: 0px; | padding: 0px; | ||||
| } | } | ||||
| .lg\:py-0 { | |||||
| padding-top: 0px; | |||||
| padding-bottom: 0px; | |||||
| } | |||||
| .lg\:pr-8 { | .lg\:pr-8 { | ||||
| padding-right: 2rem; | padding-right: 2rem; | ||||
| } | } | ||||
| .lg\:opacity-100 { | .lg\:opacity-100 { | ||||
| opacity: 1; | 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; | |||||
| } | } |
| {% block content %} | {% 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> | </div> | ||||
| {% endblock %} | {% endblock %} |
| <form action="{{ url_for('search.basic_search') }}" class="hidden lg:block"> | <form action="{{ url_for('search.basic_search') }}" class="hidden lg:block"> | ||||
| <label id="search-label" for="siteSearch" class="sr-only">Search</label> | <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"> | hx-trigger="keyup changed delay:500ms, search" hx-target="main" hx-select="main"> | ||||
| <input class="hidden" type="submit" hidden /> | <input class="hidden" type="submit" hidden /> | ||||
| </form> | </form> | ||||
| {% macro resource_list(resource, loop, show_number=true) %} | {% macro resource_list(resource, loop, show_number=true) %} | ||||
| <div class="border-b-2 border-black"> | <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']) }}" {% | 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()"> | 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 "> | <div class="w-[4.5rem] shrink-0 lg:w-[12rem] text-center "> | ||||
| {% if show_number %} | {% if show_number %} | ||||
| {{loop.index}} / {{count}} | |||||
| {{ (page -1) * resources.per_page + loop.index }} / {{ resources.total }} | |||||
| {% else %} | {% else %} | ||||
| <div class="capitalize inline-block min-w-[5rem] px-4 ">{{ resource['type'] }}</div> | <div class="capitalize inline-block min-w-[5rem] px-4 ">{{ resource['type'] }}</div> | ||||
| {% endif %} | {% endif %} | ||||
| {% endmacro %} | {% endmacro %} | ||||
| {% macro resource_with_related(resource, loop, show_number=true) %} | {% 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="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="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 "> | <div class="w-[4.5rem] shrink-0 lg:w-[12rem] text-center px-2 py-4 "> | ||||
| {% if show_number %} | {% if show_number %} | ||||
| <p>{{loop.index}} / {{count}}</p> | |||||
| <p>{{ (page -1) * resources.per_page + loop.index }} / {{ resources.total }}</p> | |||||
| {% else %} | {% else %} | ||||
| <p class="capitalize">{{ resource['type'] }}</p> | <p class="capitalize">{{ resource['type'] }}</p> | ||||
| {% endif %} | {% endif %} | ||||
| {% endmacro %} | {% endmacro %} | ||||
| {% macro resource_lead(resource,size=1) %} | {% 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 | {% 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']=='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 | resource['type']=='practice' %} href="{{ url_for('practice.show_practice', practice_id=resource['id']) }}" {% endif | ||||
| {{ resource['description'] | truncate(150) | safe }} | {{ resource['description'] | truncate(150) | safe }} | ||||
| </div> | </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> | </a> | ||||
| {% if current_user.is_authenticated %} | {% if current_user.is_authenticated %} | ||||
| <div class=""> | <div class=""> |
| <div class="-m-4 mb-4 lg:m-0"> | <div class="-m-4 mb-4 lg:m-0"> | ||||
| {{ view_switch() }} | {{ view_switch() }} | ||||
| </div> | </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> | </form> | ||||
| # route for displaying all tools in database | # route for displaying all tools in database | ||||
| @tool.route('/tools') | @tool.route('/tools') | ||||
| def get_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 | # 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 = f.read() | ||||
| intro_text = markdown.markdown(intro_text) | 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(): | for key in request.args.keys(): | ||||
| if key != 'view': | |||||
| if key != 'view' and key != 'page': | |||||
| if (key == 'practice' and request.args.get(key) != ''): | 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_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)) | tools_2 = tools_query.join(Relationship, Relationship.second_resource_id == Resource.id, isouter=True).filter(Relationship.first_resource_id==request.args.get(key)) | ||||
| if ((key != 'practice' and key != 'scriptingLanguage') and request.args.get(key) != ''): | if ((key != 'practice' and key != 'scriptingLanguage') and request.args.get(key) != ''): | ||||
| kwargs = {key: request.args.get(key)} | kwargs = {key: request.args.get(key)} | ||||
| tools_query = tools_query.filter_by(**kwargs) | 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 | ||||
| practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all() | practices_filter = Resource.query.filter_by(type='practice').with_entities(Resource.id, Resource.name).all() | ||||
| # license | # license | ||||
| languages_filter = get_filter_values('scriptingLanguage', resource_type) | languages_filter = get_filter_values('scriptingLanguage', resource_type) | ||||
| # status | # status | ||||
| status_filter = get_filter_values('status', resource_type) | 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 | # route for displaying a single tool based on the ID in the database | ||||
| @tool.route('/tools/<int:tool_id>') | @tool.route('/tools/<int:tool_id>') |