Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
rix0rrr committed Dec 27, 2024
1 parent a459ce9 commit f095090
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 123 deletions.
38 changes: 37 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from flask import (Flask, Response, abort, after_this_request, g, jsonify, make_response,
redirect, request, send_file, url_for, Blueprint,
send_from_directory, session, current_app)
from flask_babel import Babel
from flask_babel import Babel, format_timedelta
from website.flask_helpers import gettext_with_fallback as gettext
from website.flask_commonmark import Commonmark
from flask_compress import Compress
Expand Down Expand Up @@ -2708,11 +2708,47 @@ def chunk(x, size):

@app.app_template_filter()
def format_date(date):
"""Format a date/time as a full description of the date/time.
In HTML, render a timestamp with both absolute and relative parts, like this:
```
<span title="{{ date|format_date }}">
{{ date|format_date_rel }}
</span>
```
"""
if not isinstance(date, int):
return date
return utils.localized_date_format(date)


@app.app_template_filter()
def format_date_rel(date):
"""Format a date/time as a relative time.
In HTML, render a timestamp with both absolute and relative parts, like this:
```
<span title="{{ date|datetimeformat }}">
{{ date|format_date_rel }}
</span>
```
"""
if not isinstance(date, int):
return date
now = datetime.datetime.now(tz=datetime.UTC)
dt = datetime.datetime.fromtimestamp(date, tz=datetime.UTC)
# (dt - now) feels the wrong way around, but otherwise this formats "3 weeks from now"
return format_timedelta(dt - now, granularity='minute', add_direction=True)


@app.app_template_filter()
def jsts_to_unix(date):
"""Convert a JavaScript timestamp (in milliesconds since epoch) to a UNIX timestamp (in seconds since epoch)."""
return int(date / 1000)


@app.app_template_global()
def hedy_link(level_nr, assignment_nr, subpage=None):
"""Make a link to a Hedy page."""
Expand Down
8 changes: 8 additions & 0 deletions build-tools/heroku/tailwind/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -1014,4 +1014,12 @@ div[class^="ace_incorrect_hedy_code"] {

.sliding-content-open {
grid-template-rows: 1fr;
}

.adventure-item.selected {
@apply bg-blue-300;
}

.adventure-item.selected .text-gray-500 {
@apply text-white !important;
}
4 changes: 2 additions & 2 deletions hedy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import hedy_grammar
import hedy_translation
from utils import atomic_write_file
from hedy_content import ALL_KEYWORD_LANGUAGES
from hedy_content import ALL_KEYWORD_LANGUAGES, MAX_LEVEL
from collections import namedtuple
import re
import regex
Expand All @@ -35,7 +35,7 @@
from prefixes.music import present_in_notes_mapping
from prefixes.normal import get_num_sys

HEDY_MAX_LEVEL = 18
HEDY_MAX_LEVEL = MAX_LEVEL
HEDY_MAX_LEVEL_SKIPPING_FAULTY = 5
MAX_LINES = 100
LEVEL_STARTING_INDENTATION = 8
Expand Down
3 changes: 3 additions & 0 deletions hedy_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
from utils import customize_babel_locale
from website.yaml_file import YamlFile
from safe_format import safe_format
import hedy

logger = logging.getLogger(__name__)

COUNTRIES = static_babel_content.COUNTRIES

MAX_LEVEL = 18

# Define dictionary for available languages. Fill dynamically later.
ALL_LANGUAGES = {}
ALL_KEYWORD_LANGUAGES = {}
Expand Down
20 changes: 20 additions & 0 deletions static/css/generated.full.css
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,11 @@ a.green-btn-new:hover:active {
color: rgb(160 174 192 / var(--tw-text-opacity));
}

.adventure-item.selected .gray-btn {
--tw-text-opacity: 1 !important;
color: rgb(255 255 255 / var(--tw-text-opacity)) !important;
}

.gray-btn:hover {
--tw-bg-opacity: 1;
background-color: rgb(247 250 252 / var(--tw-bg-opacity));
Expand Down Expand Up @@ -1362,6 +1367,11 @@ a.green-btn-new:hover:active {
color: rgb(160 174 192 / var(--tw-text-opacity));
}

.adventure-item.selected .back-btn {
--tw-text-opacity: 1 !important;
color: rgb(255 255 255 / var(--tw-text-opacity)) !important;
}

.back-btn:hover {
--tw-bg-opacity: 1;
background-color: rgb(247 250 252 / var(--tw-bg-opacity));
Expand Down Expand Up @@ -345215,6 +345225,16 @@ div[class^="ace_incorrect_hedy_code"] {
grid-template-rows: 1fr;
}

.adventure-item.selected {
--tw-bg-opacity: 1;
background-color: rgb(144 205 244 / var(--tw-bg-opacity));
}

.adventure-item.selected .text-gray-500 {
--tw-text-opacity: 1 !important;
color: rgb(255 255 255 / var(--tw-text-opacity)) !important;
}

.selection\:bg-transparent *::-moz-selection {
background-color: transparent;
}
Expand Down
31 changes: 16 additions & 15 deletions templates/public-adventures2/body.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ <h1>{{_('public_adventures')}}</h1>

<!-- Filter form -->
<div>
<form hx-trigger="submit" hx-get="/public-adventures2/" hx-target="#public_adventures_div" hx-swap="morph:outerHTML">
<form id="filterform" hx-trigger="submit" hx-get="/public-adventures2/" hx-target="#public_adventures_page_div" hx-swap="morph:outerHTML">
<div class="flex flex-col items-stretch md:flex-row md:items-center gap-2 justify-around bg-blue-100 shadow-md rounded-lg p-4 mb-4">
<div class="flex-0">
<label for="language_select">{{_('language')}}</label>
Expand Down Expand Up @@ -75,24 +75,25 @@ <h1>{{_('public_adventures')}}</h1>
<input type="submit" value="{{_('search')}}" class="green-btn">
</div>
</div>
<input type="hidden" name="page" id="page_input" value="{{page}}">
</form>
</div>

<div id="search_results">
</div>

<div id="_body">
{% if adventures|length == 0 %}
<div class="grid p-5 text-center bg-white shadow-md rounded-lg transition-shadow hover:shadow-lg outline-blue-400 hover:outline hover:outline-2">
{{_('no_adventures_yet')}}
<button id="create_adventure_button" class="green-btn mt-4"
_="on click
window.open('/for-teachers/customize-adventure', '_self')"
data-cy="edit_link"{% if second_teacher and role == 'viewer' %}disabled{% endif %}>{{_('create_adventure')}}</button>
</div>
{% else %}
{% include "incl/adventure-tabs.html" %}
{% include "incl/editor-and-output.html" %}
{% endif %}
</div>
<div id="_body">
{% if adventures|length == 0 %}
<div class="grid p-5 text-center bg-white shadow-md rounded-lg transition-shadow hover:shadow-lg outline-blue-400 hover:outline hover:outline-2">
{{_('no_adventures_yet')}}
<button id="create_adventure_button" class="green-btn mt-4"
_="on click window.open('/for-teachers/customize-adventure', '_self')"
data-cy="edit_link"
{% if second_teacher and role == 'viewer' %}disabled{% endif %}
>{{_('create_adventure')}}</button>
</div>
{% else %}
{% include "public-adventures2/incl-adventure-list.html" %}
{% endif %}
</div>
</div>
1 change: 1 addition & 0 deletions templates/public-adventures2/htmx-preview-adventure.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hellooo {{ adventure.name }}
49 changes: 49 additions & 0 deletions templates/public-adventures2/incl-adventure-list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<div class="flex gap-4">
<div class="flex-0">
<div class="shadow-inner overflow-auto w-64 border border-gray-600" style="height: 40rem;">
{% for adventure in adventures %}
<div
class="adventure-item text-gray-800 flex flex-col cursor-pointer p-2 border-b border-gray-600"
tabindex="0"
_="on click remove .selected from .adventure-item then add .selected to me end on keyup[key is 'Enter'] send click to me"
hx-get="/public-adventures2/preview/{{ adventure.id }}" hx-target="#preview-div"
>
<div class="flex-1">
<span class="text-xl min-h-28">{{ adventure.name }}</span>
</div>
<div class="flex-0 text-xs">
<div class="flex">
<div class="flex-1 text-gray-500">{{ adventure.creator }}</div>
<div>
<span class="flex-1 text-gray-500" title="{{ adventure.date|jsts_to_unix|datetimeformat }}">
{{ adventure.date|jsts_to_unix|format_date_rel }}
</span>
</div>
</div>
<div class="text-gray-500">{{_('level')}} {{ adventure.levels|join(', ') }}</div>
{% if adventure.tags %}
<div>
{% for tag in adventure.tags %}
<span class="inline-block bg-pink-200 rounded-full px-2 text-xs text-gray-700 mr-1 mb-1">{{ tag }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
</div>
<div class="flex-1">
<div id="preview-div">
</div>
</div>
</div>

<div class="flex gap-2">
{% if prev_page_token %}
<button _="on click set (#page_input).value to '{{prev_page_token}}' then call (#filterform).requestSubmit()" class="green-btn mt-4">« Prev page</button>
{% endif %}
{% if next_page_token %}
<button _="on click set (#page_input).value to '{{next_page_token}}' then call (#filterform).requestSubmit()" class="green-btn mt-4">Next page »</button>
{% endif %}
</div>
2 changes: 1 addition & 1 deletion translations/en/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ msgid "no_accounts"
msgstr "There are no accounts to create."

msgid "no_adventures_yet"
msgstr "There are no public adventures yet..."
msgstr "There are on public adventures matching these criteria. Try searching for something else."

msgid "no_more_flat_if"
msgstr "Starting in level 8, the code after `{if}` needs to be placed on the next line and start with 4 spaces."
Expand Down
2 changes: 1 addition & 1 deletion translations/nl/LC_MESSAGES/messages.po
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ msgid "no_accounts"
msgstr "Er zijn geen accounts om aan te maken."

msgid "no_adventures_yet"
msgstr "Er zijn nog geen publieke avonturen..."
msgstr "Er zijn geen publieke avonturen die voldoen aan deze criteria. Probeer ergens anders op te zoeken."

msgid "no_more_flat_if"
msgstr "Vanaf level 8 moet de code na `{if}` op de volgende regel komen en beginnen met 4 spaties."
Expand Down
50 changes: 44 additions & 6 deletions website/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,14 @@ def only_in_dev(x):
# A custom teacher adventure
# - id (str): id of the adventure
# - content (str): adventure text
# - author (str): username (of a teacher account, hopefully)
# - creator (str): username (of a teacher account, hopefully)
# - date (int): timestamp of last update
# - level (int | str): level number, sometimes as an int, sometimes as a str
# - date (int): timestamp of last update (in milliseconds, JavaScript timestamp)
# - level (str): level number, sometimes as an int, usually as a str
# - levels: [str]: levels of the adventure
# - name (str): adventure name
# - public (int): 1 or 0 whether it can be shared
# - tags_id (str): id of tags that describe this adventure.
# - tags_id (str): list of tags that describe this adventure.
self.adventures = dynamo.Table(storage, "adventures", "id",
types=only_in_dev({
'id': str,
Expand All @@ -210,6 +212,7 @@ def only_in_dev(x):
indexes=[
dynamo.Index("creator"),
dynamo.Index("public"),
dynamo.Index("language", keys_only=True),
dynamo.Index("name", sort_key="creator", index_name="name-creator-index")
])
self.invitations = dynamo.Table(
Expand Down Expand Up @@ -789,6 +792,44 @@ def batch_get_adventures(self, adventure_ids):
def get_public_adventures(self):
return self.adventures.get_many({"public": 1})

def get_public_adventures_filtered(self, language: str, level: int=None, tag: str=None, q: str=None, pagination_token: str=None):
"""Return a page of the public adventures, filtered by language, level and tag, and with a search string.
Also returns the languages and tags that match the current filter.
FIXME: This is right now very poorly optimized, and needs more work.
"""

server_side_filter = {
'language': language,
}

def client_side_filter(adventure):
# levels are stored as strings T_T
if level and adventure.get('level', '') != str(level) and str(level) not in adventure.get('levels', []):
return False
if tag and tag not in adventure.get('tags', []):
return False
if q and (q not in adventure.get('name', '') and q not in adventure.get('content', '') and q not in adventure.get('author', '') and q not in adventure.get('creator', '')):
return False
return True

return self.adventures.get_page({"public": 1},
pagination_token=pagination_token,
limit=20,
server_side_filter=server_side_filter,
client_side_filter=client_side_filter)

def get_public_adventures_tags(self):
"""Return all tags for public adventures.
FIXME: This is right now very poorly optimized, and needs more work.
"""
ret = set([])
for adventure in dynamo.GetManyIterator(self.adventures, {"public": 1}):
ret |= set(adventure.get("tags", []))
return ret

def get_adventure_by_creator_and_name(self, name, username):
return self.adventures.get({"name": name, "creator": username})

Expand Down Expand Up @@ -867,9 +908,6 @@ def get_second_teacher_adventures(self, classes, teacher):
def all_adventures(self):
return self.adventures.scan()

def public_adventures(self):
return self.adventures.get_many({"public": 1})

def get_student_classes_ids(self, username):
ids = self.users.get({"username": username}).get("classes")
return list(ids) if ids else []
Expand Down
Loading

0 comments on commit f095090

Please sign in to comment.