[scrobbles] Clean up notes on webpages
This commit is contained in:
@ -50,20 +50,25 @@ class BaseLogData(JSONDataclass):
|
||||
def override_fields(cls) -> dict:
|
||||
return {}
|
||||
|
||||
def notes_as_str(self) -> str:
|
||||
note_str = ""
|
||||
def notes_as_str(self, separator: str = " | ") -> str:
|
||||
import re
|
||||
|
||||
if not self.notes:
|
||||
return note_str
|
||||
return ""
|
||||
|
||||
if self.notes:
|
||||
if isinstance(self.notes, str):
|
||||
note_str = self.notes.encode("utf-8").decode("unicode_escape")
|
||||
if isinstance(self.notes, list):
|
||||
note_str = (
|
||||
"\n".join(self.notes).encode("utf-8").decode("unicode_escape")
|
||||
)
|
||||
return note_str
|
||||
if isinstance(self.notes, str):
|
||||
note_str = self.notes.encode("utf-8").decode("unicode_escape")
|
||||
return re.sub(r"\s*\[start:\d+\|end:\d+\]$", "", note_str)
|
||||
|
||||
if isinstance(self.notes, list):
|
||||
cleaned_notes = []
|
||||
for note in self.notes:
|
||||
cleaned = note.encode("utf-8").decode("unicode_escape")
|
||||
cleaned = re.sub(r"\s*\[start:\d+\|end:\d+\]$", "", cleaned)
|
||||
cleaned_notes.append(cleaned)
|
||||
return separator.join(cleaned_notes)
|
||||
|
||||
return ""
|
||||
|
||||
def notes_as_html(self) -> str:
|
||||
if not self.notes:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from django import template
|
||||
@ -15,3 +16,20 @@ def add_class(field, css_class):
|
||||
@register.simple_tag
|
||||
def current_date(format):
|
||||
return datetime.now().strftime(format)
|
||||
|
||||
|
||||
@register.filter
|
||||
def note_position(note, article_length):
|
||||
if not article_length or article_length == 0:
|
||||
return ""
|
||||
match = re.search(r"start:(\d+)", note)
|
||||
if match:
|
||||
start = int(match.group(1))
|
||||
percentage = (start / article_length) * 100
|
||||
return f"style='top: {percentage}%'"
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter
|
||||
def note_text(note):
|
||||
return re.sub(r"\s*\[start:\d+\|end:\d+\]$", "", note)
|
||||
|
||||
@ -73,6 +73,10 @@ class WebPageDetailView(ScrobbleableDetailView):
|
||||
context["latest_scrobble_uuid"] = str(latest.uuid)
|
||||
notes = latest.log.get("notes", []) if latest.log else []
|
||||
context["latest_scrobble_notes"] = notes
|
||||
context["latest_scrobble_notes_html"] = latest.logdata.notes_as_html()
|
||||
context["article_length"] = (
|
||||
len(self.object.extract) if self.object.extract else 0
|
||||
)
|
||||
return context
|
||||
|
||||
|
||||
|
||||
@ -1,18 +1,77 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% load static %}
|
||||
{% load form_tags %}
|
||||
|
||||
{% block title %}{{object.title}}{% endblock %}
|
||||
|
||||
{% block head_extra %}
|
||||
<style>
|
||||
.webpage-metadata {border: 1px solid #aaa; border-top:none; border-right:none; line-height:0.65em;padding-top:10px;}
|
||||
.webpage-body {width:40em; text-align:justify; padding-top:20px; font-family:serif; font-size:1.25em; line-height:1.6em; border-right: 1px #ccc solid; padding-right: 2em;}
|
||||
.webpage-body {text-align:justify; padding-top:20px; font-family:serif; font-size:1.25em; line-height:1.6em; border-right: 1px #ccc solid; padding-right: 2em;}
|
||||
.webpage-body br { margin-bottom: 1em; }
|
||||
.webpage-notes {padding-top:20px; min-width: 220px;}
|
||||
.webpage-notes .sticky-notes-container {
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
min-height: 500px;
|
||||
}
|
||||
.webpage-notes .sticky-note {
|
||||
max-width: none;
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
<link href="{% static 'css/recogito.min.css' %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
(function() {
|
||||
var article = document.getElementById('article');
|
||||
var notesContainer = document.querySelector('.webpage-notes .sticky-notes-container');
|
||||
if (!article || !notesContainer) return;
|
||||
|
||||
var notes = [
|
||||
{% for note in latest_scrobble_notes %}
|
||||
{ text: "{{ note|escapejs }}", start: null, end: null },
|
||||
{% endfor %}
|
||||
];
|
||||
|
||||
var positionRe = /\[start:(\d+)\|end:(\d+)\]$/;
|
||||
|
||||
notes.forEach(function(noteObj) {
|
||||
var match = noteObj.text.match(positionRe);
|
||||
if (match) {
|
||||
noteObj.start = parseInt(match[1]);
|
||||
noteObj.end = parseInt(match[2]);
|
||||
}
|
||||
});
|
||||
|
||||
function positionNotes() {
|
||||
var articleText = article.innerText || article.textContent;
|
||||
var articleHeight = article.offsetHeight;
|
||||
|
||||
notesContainer.style.height = articleHeight + 'px';
|
||||
|
||||
notes.forEach(function(note, index) {
|
||||
if (note.start === null) return;
|
||||
|
||||
var noteEl = notesContainer.children[index];
|
||||
if (!noteEl) return;
|
||||
|
||||
var percentage = (note.start / articleText.length) * 100;
|
||||
var topPos = (percentage / 100) * articleHeight;
|
||||
|
||||
noteEl.style.top = topPos + 'px';
|
||||
});
|
||||
}
|
||||
|
||||
if (notes.length > 0) {
|
||||
positionNotes();
|
||||
window.addEventListener('resize', positionNotes);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="{% static 'js/recogito.min.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
@ -116,19 +175,6 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row webpage">
|
||||
<div class="webpage-metadata">
|
||||
<p>Source: <a href="{{object.url}}">{{object.domain}}</a></p>
|
||||
{% if object.date %}<p>Published: <em>{{object.date}}</em></p>{% endif %}
|
||||
<p>Time to read: {{object.estimated_time_to_read_in_minutes}} minutes</p>
|
||||
</div>
|
||||
{% if object.extract %}
|
||||
<div class="webpage-body" id="article">
|
||||
{{object.extract|linebreaks}}
|
||||
</div>
|
||||
{% endif %}
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md">
|
||||
<h3>Last scrobbles</h3>
|
||||
@ -144,7 +190,7 @@
|
||||
{% for scrobble in scrobbles.all %}
|
||||
<tr>
|
||||
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
||||
<td>{% if scrobble.logdata.notes %}{% for note in scrobble.logdata.notes %}{{note}}{% if not forloop.last %}; {% endif%}{% endfor %}{% endif %}
|
||||
<td>{% if scrobble.logdata.notes %}{{ scrobble.logdata.notes_as_str|safe }}{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@ -152,4 +198,31 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row webpage">
|
||||
<div class="webpage-metadata">
|
||||
<p>Source: <a href="{{object.url}}">{{object.domain}}</a></p>
|
||||
{% if object.date %}<p>Published: <em>{{object.date}}</em></p>{% endif %}
|
||||
<p>Time to read: {{object.estimated_time_to_read_in_minutes}} minutes</p>
|
||||
</div>
|
||||
{% if object.extract %}
|
||||
<div class="col">
|
||||
<div class="webpage-body" id="article">
|
||||
{{object.extract|linebreaks}}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="col-auto">
|
||||
<div class="webpage-notes">
|
||||
{% if latest_scrobble_notes %}
|
||||
<h5>Notes</h5>
|
||||
<div class="sticky-notes-container" style="position: relative; height: 100%;">
|
||||
{% for note in latest_scrobble_notes %}
|
||||
<div class="sticky-note" {{ note|note_position:article_length }}>{{ note|note_text }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user