Compare commits
7 Commits
5468f6d3e7
...
0bf7841345
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bf7841345 | |||
| 965573c03b | |||
| b5d8d2d775 | |||
| 0e69cdeca4 | |||
| e797876c16 | |||
| 1301d19c08 | |||
| ca3effcad1 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,7 +5,10 @@ __pycache__
|
||||
credentials.json
|
||||
credentials
|
||||
local_settings.py
|
||||
local.mk
|
||||
.coverage
|
||||
.lint
|
||||
.deploy
|
||||
Session.vim
|
||||
poetry.lock
|
||||
/env
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
FROM alpine:latest
|
||||
|
||||
ENV TARGET=/opt/polyphonic
|
||||
#ENV RELEASE=polyphonic-0.8.4-py3-none-any.whl
|
||||
ENV RELEASE=git+https://gitea.tfconsulting.com.au/projects/polyphonic.git
|
||||
ENV RELEASE=polyphonic-0.8.4-py3-none-any.whl
|
||||
#ENV RELEASE=git+https://gitea.tfconsulting.com.au/projects/polyphonic.git
|
||||
|
||||
RUN apk add --no-cache python3 py3-pip git ghostscript sqlite
|
||||
|
||||
@ -11,7 +11,7 @@ WORKDIR /root
|
||||
RUN python3 -m venv ${TARGET}
|
||||
ENV PATH="${TARGET}/bin:$PATH"
|
||||
|
||||
#COPY dist/${RELEASE} .
|
||||
COPY dist/${RELEASE} .
|
||||
RUN pip3 install ${RELEASE} --no-cache-dir
|
||||
RUN pip3 install gunicorn whitenoise
|
||||
|
||||
|
||||
22
Makefile
22
Makefile
@ -1,18 +1,32 @@
|
||||
PYTHON=env/bin/python
|
||||
DROPZONE=5.7.0
|
||||
|
||||
test:
|
||||
VERSION=0.8.4
|
||||
|
||||
export DJANGO_SETTINGS_MODULE=polyphonic.config.settings.dev
|
||||
|
||||
-include local.mk
|
||||
|
||||
test: .coverage
|
||||
|
||||
check: .lint
|
||||
|
||||
pre-commit: check test
|
||||
|
||||
.coverage: polyphonic
|
||||
poetry run coverage run --include "polyphonic/*" --omit "*/migrations/*" polyphonic/manage.py test polyphonic
|
||||
poetry run coverage html
|
||||
poetry run coverage report
|
||||
|
||||
check:
|
||||
.lint: polyphonic
|
||||
poetry run ruff check polyphonic
|
||||
poetry run ruff format --check polyphonic
|
||||
touch $@
|
||||
|
||||
pre-commit: check test
|
||||
|
||||
build:
|
||||
build: dist/polyphonic-${VERSION}-py3-none-any.whl
|
||||
|
||||
dist/polyphonic-${VERSION}-py3-none-any.whl: polyphonic
|
||||
poetry build
|
||||
|
||||
dev-setup:
|
||||
|
||||
@ -26,4 +26,31 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
async function background_api(el) {
|
||||
console.log(el.dataset["api"]);
|
||||
let text = el.innerHTML;
|
||||
el.disabled = true;
|
||||
el.innerHTML = "Running..."
|
||||
const response = await fetch(el.dataset.api);
|
||||
|
||||
console.log(response);
|
||||
|
||||
el.innerHTML = text;
|
||||
|
||||
if(!response.ok) {
|
||||
el.disabled = false;
|
||||
throw new Error(`Response: ${response.status}`);
|
||||
}
|
||||
|
||||
if (el.dataset.success) {
|
||||
text = el.dataset.success;
|
||||
}
|
||||
|
||||
el.innerHTML = text;
|
||||
const data = await response.json();
|
||||
|
||||
console.log(data);
|
||||
|
||||
}
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
<script src="{% static 'interface/js/interface.js' %}"></script>
|
||||
<script src="//unpkg.com/alpinejs" defer></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" defer></script>
|
||||
<script src="//kit.fontawesome.com/c837098e5b.js" crossorigin="anonymous" defer></script>
|
||||
<!-- script src="//kit.fontawesome.com/c837098e5b.js" crossorigin="anonymous" defer></script -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
|
||||
<title>{% block title %}Polyphonic{% endblock %}</title>
|
||||
{% block media %}{% endblock %}
|
||||
<style>{% block style %}{% endblock %}</style>
|
||||
@ -23,7 +24,7 @@
|
||||
<nav class="navbar" role="navigation">
|
||||
<div class="navbar-brand has-text-primary">
|
||||
<a class="navbar-item" href="/">
|
||||
<span class="icon fancy is-size-2 is-size-4-mobile mx-4"><i class="fas fa-random"></i></span>
|
||||
<span class="icon fancy mx-4"><span class="material-symbols-outlined is-size-1 is-size-3-mobile">groups</span></span>
|
||||
<span class="fancy is-size-2 is-size-4-mobile">Polyphonic</span>
|
||||
</a>
|
||||
<span class="navbar-item is-hidden-mobile fancy is-size-5">Musical Ensemble Manager</span>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
{% crispy_field field 'class' 'file-input'%}
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
<span class="material-symbols-outlined">file_upload</span>
|
||||
</span>
|
||||
<span class="file-label">
|
||||
Choose a file…
|
||||
@ -25,4 +25,4 @@
|
||||
fileName.textContent = fileInput.files[0].name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load md2 %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
<a href="{% url 'project_create' object.slug %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
{{ "add_notes"|icon }}
|
||||
<span>Add project</span>
|
||||
</a>
|
||||
{% if inactive %}
|
||||
<a href="?" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-archive"></i></span>
|
||||
{{ "preview_off"|icon }}
|
||||
<span>Hide old</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="?inactive" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-archive"></i></span>
|
||||
{{ "preview"|icon }}
|
||||
<span>Show all</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -54,4 +55,4 @@ Contacts:
|
||||
<a href="{% url 'forget_resource' 'ensemble' ensemble.slug %}">Forget this ensemble</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
{% comment %}
|
||||
<div class="admin-tools is-pulled-right">
|
||||
<a class="button is-link" href="{% url 'register' %}">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
{% icon "add_file" %}
|
||||
<span>Register another</span>
|
||||
</a>
|
||||
</div>
|
||||
@ -25,10 +25,13 @@
|
||||
<img src="https://www.gravatar.com/avatar/{{ ensemble.email }}?d=mp" alt="Placeholder image">
|
||||
</figure>
|
||||
</div>
|
||||
<div class="media-content" style="min-height: 60px">
|
||||
<div class="media-content" style="min-height: 100px">
|
||||
<a href="{% url 'ensemble_detail' ensemble.slug %}">
|
||||
<p class="title is-4">{{ ensemble.name }}</p>
|
||||
</a>
|
||||
<div class="mt-3">
|
||||
{{ ensemble.details|markdown }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -45,4 +48,4 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
|
||||
{% block admin %}
|
||||
<a href="{% url 'wiki_create' project=project.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-file"></i></span>
|
||||
<span>Add Page</span>
|
||||
{{ "add_notes"|icon }}
|
||||
<span>Add Page</span>
|
||||
</a>
|
||||
<a href="{% url 'project_edit' project=project.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-edit"></i></span>
|
||||
{{ "edit"|icon }}
|
||||
<span>Edit</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
@ -33,4 +33,4 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load md2 %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
<a class="button is-link" href="{% url 'resource_create' project=project.pk %}">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
{% icon "add_notes" %}
|
||||
<span>Add new</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
@ -27,11 +28,11 @@
|
||||
|
||||
<div class="card-header-icon">
|
||||
{% if request.is_admin %}
|
||||
<a href="{% url 'resource_upload' project=project.pk pk=resource.pk %}" class="icon" title="Upload">
|
||||
<i class="fas fa-upload"></i>
|
||||
<a href="{% url 'resource_upload' project=project.pk pk=resource.pk %}" title="Upload">
|
||||
{% icon "upload_file" %}
|
||||
</a>
|
||||
<a href="{% url 'resource_edit' project=project.pk pk=resource.pk %}" class="icon" title="Edit">
|
||||
<i class="fas fa-edit"></i>
|
||||
<a href="{% url 'resource_edit' project=project.pk pk=resource.pk %}" title="Edit">
|
||||
{% icon "edit" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
<a href="{% url 'wiki_edit' project=project.pk pk=wikipage.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-edit"></i></span>
|
||||
<span>Edit</span>
|
||||
{{ "edit"|icon }}
|
||||
<span>Edit</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
@ -12,4 +13,4 @@
|
||||
<div class="box content wiki-page">
|
||||
{{ wiki_html|safe }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -1,9 +1,27 @@
|
||||
from django import template
|
||||
from django.utils import timesince
|
||||
from django.utils.html import format_html
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter("icon", is_safe=True)
|
||||
def material_icon(value):
|
||||
return f'<span class="icon"><span class="material-symbols-outlined">{value}</span></span>'
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def icon(name, element="span", classes=[]):
|
||||
classes = ["icon"] + classes
|
||||
return format_html(
|
||||
'<{} class="{}"><span class="material-symbols-outlined">{}</span></{}>',
|
||||
element,
|
||||
" ".join(classes),
|
||||
name,
|
||||
element,
|
||||
)
|
||||
|
||||
|
||||
def roughtimesince(value):
|
||||
return timesince.timesince(value, depth=1)
|
||||
|
||||
|
||||
@ -14,6 +14,13 @@ def sync_work(work: Work):
|
||||
|
||||
logger.info("Syncing '%s' from %r", work.name, folder_id)
|
||||
|
||||
results = {
|
||||
"folder_id": folder_id,
|
||||
"added": [],
|
||||
"skipped": [],
|
||||
"missing": [],
|
||||
}
|
||||
|
||||
storage = UserStorage.objects.get(name="gdrive").instance()
|
||||
|
||||
existing = set(
|
||||
@ -27,6 +34,8 @@ def sync_work(work: Work):
|
||||
_, files = storage.listdir(folder_id)
|
||||
logger.debug("Remote files: %r", files)
|
||||
|
||||
results["files"] = len(files)
|
||||
|
||||
for file in files:
|
||||
if file.id in existing:
|
||||
logger.debug("%30s: Skipping existing (%s)", file.name, file.id)
|
||||
@ -35,22 +44,29 @@ def sync_work(work: Work):
|
||||
|
||||
if not file.name.lower().endswith(".pdf"):
|
||||
logger.debug("%40s: Not a PDF", file.name)
|
||||
results["skipped"].append({"file": file.name, "file_id": file.id})
|
||||
continue
|
||||
|
||||
logger.info("%40s: Adding", file.name)
|
||||
results["added"].append({"file": file.name, "file_id": file.id})
|
||||
doc = work.docs.create(upload=f"gdrive:{file}", doctype=Document.DOCTYPE_PDF)
|
||||
doc.auto_tag()
|
||||
|
||||
for uri in existing:
|
||||
results["missing"].append({"uri": uri})
|
||||
logger.warning("Local entry not in folder: %s", uri)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def sync_partial_collection(collection: Collection, sync_existing: bool = True):
|
||||
|
||||
works = Work.objects.filter(collection=collection, meta_info__name="folderid")
|
||||
|
||||
result = []
|
||||
|
||||
for work in works:
|
||||
sync_work(work)
|
||||
result.append(sync_work(work))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def sync_collection(collection: Collection, sync_existing: bool = False):
|
||||
@ -69,6 +85,12 @@ def sync_collection(collection: Collection, sync_existing: bool = False):
|
||||
storage = collection.storage.instance()
|
||||
folders, _ = storage.listdir(folder)
|
||||
|
||||
results = {
|
||||
"folders": len(folders),
|
||||
"added": [],
|
||||
"missing": [],
|
||||
}
|
||||
|
||||
for folder in folders:
|
||||
if folder[0] == "_":
|
||||
continue
|
||||
@ -84,7 +106,11 @@ def sync_collection(collection: Collection, sync_existing: bool = False):
|
||||
work = Work(name=folder.name, collection=collection)
|
||||
work.save()
|
||||
work.meta_info.create(name="folderid", value=folder.id)
|
||||
results["added"].append({"work": work.pk, "folder_id": folder.id})
|
||||
sync_work(work)
|
||||
|
||||
for folderid, work in existing:
|
||||
for folderid, work in existing.items():
|
||||
results["missing"].append({"work": work, "folder_id": folderid})
|
||||
logger.warning("Folder for work %d no longer in drive (%s)", work, folderid)
|
||||
|
||||
return results
|
||||
|
||||
@ -69,20 +69,26 @@ class GDriveLinkStorage(Storage):
|
||||
if path == "":
|
||||
return [], []
|
||||
|
||||
folder = self.parse_resource(path)
|
||||
url = f"{FILES_API}?q='{folder.id}'+in+parents&key={self.api_key}"
|
||||
data = self.get_json(url, folder)
|
||||
files = []
|
||||
folders = []
|
||||
for x in data["files"]:
|
||||
if x["mimeType"] == "application/vnd.google-apps.folder":
|
||||
# folders.append(f"{x['id']}/{x['name']}")
|
||||
folders.append(DriveObject(x["id"], x.get("resourceKey"), x["name"]))
|
||||
else:
|
||||
# files.append(f"{x['id']}/{x['name']}")
|
||||
files.append(DriveObject(x["id"], x.get("resourceKey"), x["name"]))
|
||||
|
||||
return folders, files
|
||||
folder = self.parse_resource(path)
|
||||
url = f"{FILES_API}?q='{folder.id}'+in+parents&key={self.api_key}"
|
||||
|
||||
data = self.get_json(url, folder)
|
||||
while True:
|
||||
for x in data["files"]:
|
||||
if x["mimeType"] == "application/vnd.google-apps.folder":
|
||||
folders.append(
|
||||
DriveObject(x["id"], x.get("resourceKey"), x["name"])
|
||||
)
|
||||
else:
|
||||
files.append(DriveObject(x["id"], x.get("resourceKey"), x["name"]))
|
||||
|
||||
token = data.get("nextPageToken")
|
||||
if token is None:
|
||||
return folders, files
|
||||
data = self.get_json(f"{url}&pageToken={token}", folder)
|
||||
|
||||
def get_meta(self, name):
|
||||
file_resource = self.parse_resource(name)
|
||||
|
||||
@ -19,6 +19,10 @@ cb Double Bass
|
||||
mall Mallet Percussion
|
||||
vln Violin
|
||||
vla Viola
|
||||
kit Drumkit
|
||||
asax Alto Sax
|
||||
tsax Tenor Sax
|
||||
bsax Bari Sax
|
||||
|
||||
acc Accordion
|
||||
afl Alto flute
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block page %}
|
||||
<h3 class="title">Library collections for {% firstof request.user.first_name request.user.username %}</h3>
|
||||
@ -10,7 +11,9 @@
|
||||
<input class="input" name="q" type="text" placeholder="Find a work" value="{{ request.GET.filter }}"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button" href="?"><i class="fas fa-times"></i></a>
|
||||
<a class="button" href="?">
|
||||
{% icon "close" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -47,4 +50,5 @@
|
||||
<div>
|
||||
<small>{{ ensemble.ensemble_code }}</small>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
<a href="#" onclick="saveTags()" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-save"></i></span>
|
||||
{% icon "save" %}
|
||||
<span>Save</span>
|
||||
</a>
|
||||
<a href="{% url 'work_detail' collection=collection.pk pk=object.work_id %}" class="button is-link is-light">
|
||||
{% icon "backspace" %}
|
||||
<span>Cancel</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
@ -76,7 +78,11 @@
|
||||
</div>
|
||||
<ul id="unassigned-area">
|
||||
{% for tag, inst in document.work.music_tags %}
|
||||
<li class="is-clickable" onclick="assignInstrument('{{tag}}', this)")>{{ inst }}</li>
|
||||
<li>
|
||||
<span class="is-clickable" onclick="assignInstrument('{{tag}}', this)")>{{ inst }}</span>
|
||||
|
||||
<span class="is-clickable" onclick="addNumberedInstrument('{{tag}}', this)">...</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<a onclick="document.getElementById('add-modal').classList.add('is-active')">Add Tag</a>
|
||||
@ -275,6 +281,16 @@
|
||||
document.getElementById('add-instrument-name').value = "";
|
||||
document.getElementById('add-instrument-variant').value = "";
|
||||
}
|
||||
|
||||
function addNumberedInstrument(tag, e) {
|
||||
let modal = document.getElementById('add-modal');
|
||||
|
||||
document.getElementById("add-instrument-name").value = data.instruments[tag];
|
||||
document.getElementById("add-instrument-variant").value = 1;
|
||||
document.getElementById("add-instrument-variant").focus();
|
||||
|
||||
modal.classList.add('is-active');
|
||||
}
|
||||
|
||||
function addInstrument() {
|
||||
let name = document.getElementById('add-instrument-name');
|
||||
@ -326,7 +342,7 @@
|
||||
|
||||
let setStart = document.createElement('span');
|
||||
setStart.className = "icon is-action";
|
||||
setStart.innerHTML = '<i class="fas fa-sort-amount-down" title="Set start page"></i>';
|
||||
setStart.innerHTML = '<span class="material-symbols-outlined" title="Set start page">vertical_align_top</span>';
|
||||
setStart.addEventListener('click', () => setTagStart(el));
|
||||
el.appendChild(setStart);
|
||||
|
||||
@ -336,22 +352,20 @@
|
||||
let name = document.createElement('b');
|
||||
name.innerHTML = get_instrument(tag);
|
||||
label.appendChild(name);
|
||||
el.appendChild(label);
|
||||
|
||||
let del = document.createElement('span');
|
||||
del.className = "icon is-action";
|
||||
del.innerHTML = '<i class="fas fa-trash-alt" title="Remove this tag"></i>';
|
||||
del.innerHTML = '<span class="material-symbols-outlined" title="Remove this tag">delete</span>';
|
||||
del.addEventListener('click', () => {
|
||||
el.remove();
|
||||
dirty=true;
|
||||
});
|
||||
label.appendChild(del)
|
||||
|
||||
el.appendChild(label);
|
||||
|
||||
el.appendChild(del)
|
||||
|
||||
let setEnd = document.createElement('span');
|
||||
setEnd.className = "icon is-action";
|
||||
setEnd.innerHTML = '<i class="fas fa-sort-amount-up" title="Set end page"></i>';
|
||||
setEnd.innerHTML = '<span class="material-symbols-outlined" title="Set end page">vertical_align_bottom</span>';
|
||||
setEnd.addEventListener('click', () => setTagEnd(el));
|
||||
el.appendChild(setEnd);
|
||||
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
{% load path_filters %}
|
||||
{% load polyphonic %}
|
||||
<tr>
|
||||
<td><a href="{{ doc.upload.url }}" target="_blank">
|
||||
{{ doc.upload.name|basename }}</a></td>
|
||||
<td style="white-space: nowrap">
|
||||
<a href="{{ doc.upload.url }}" target="_blank">
|
||||
{{ doc.upload.name|basename }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{% for section in doc.sections.all %}
|
||||
<a class="tag is-{{ section.bulma_class }}" target="_blank" href="{% url 'part_download' collection.pk section.pk section.filename %}">{{ section.name }}</a>
|
||||
@ -10,10 +13,13 @@
|
||||
<td class="has-text-right" style="white-space: nowrap;">
|
||||
{% if request.is_admin %}
|
||||
{% if doc.doctype == 1 %}
|
||||
<a href="{% url 'document_annotate' collection.pk doc.pk %}"><i class="fas fa-tags"
|
||||
title="Manage Tags"></i></a>
|
||||
<a href="{% url 'document_annotate' collection.pk doc.pk %}" title="Annotate">
|
||||
{% icon "toc" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="{% url 'document_delete' collection.pk doc.pk %}"><i class="fas fa-trash-alt" title="Delete Document"></i></a>
|
||||
<a href="{% url 'document_delete' collection.pk doc.pk %}" title="Delete">
|
||||
{% icon "delete" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
|
||||
@ -1,12 +1,28 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
|
||||
{% if meta.folderid %}
|
||||
<button class="button is-link" data-api="{% url 'work_sync' work=object.pk %}" data-success="Synced" onclick="background_api(this)">
|
||||
|
||||
{% icon "sync" %}
|
||||
<span>Sync with Drive</span>
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url 'work_detail' collection=collection.pk pk=object.pk %}" class="button is-link is-light">
|
||||
{% icon "backspace" %}
|
||||
<span>Back to work</span>
|
||||
</a>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block page %}
|
||||
<h2 class="title">
|
||||
{% icon "add_to_drive" %}
|
||||
<span>Google Drive - Shared Links
|
||||
</h2>
|
||||
<h3 class="subtitle"><a href="{% url 'work_detail' collection.pk object.pk %}">{{ object.name }}</a></h3>
|
||||
<div class="m-3">
|
||||
<p>This page lets you link a work to a google drive folder. You can either paste a public link to a folder to enable syncing or a file to add individually</p>
|
||||
@ -14,28 +30,28 @@
|
||||
<div class="m-3">
|
||||
<p>
|
||||
{% if meta.folderid %}
|
||||
This work is currently linked to <b>{{ meta.folderid }}</b>. Pasting a new folder link will overwrite this.
|
||||
This work is currently linked to folder <span class="tag is-success">{{ meta.folderid }}</span><br/>
|
||||
<em>Pasting a new folder link will overwrite this.</em>
|
||||
{% else %}
|
||||
There is currently no shared drive folder linked to this work - paste one here to enable syncing.
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mt-5">
|
||||
<form method="post">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input name="link" class="input is-expanded" type="text" placeholder="Shared link">
|
||||
</div>
|
||||
{% for error in form.errors.link %}
|
||||
<p class="help is-danger">{{ error }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<button class="button is-info" type="submit">Add</button>
|
||||
</div>
|
||||
<div class="field has-addons mx-6">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" name="link" type="text" placeholder="Paste shared link">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button" type="submit">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% csrf_token %}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block page %}
|
||||
<form action="" method="post" target="_blank">
|
||||
@ -31,12 +32,6 @@
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="control">
|
||||
<button type="submit" class="button is-primary">
|
||||
<span class="icon"><i class="fas fa-copy"></i></span>
|
||||
<span>Get My Parts!</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -45,6 +40,7 @@
|
||||
<tr>
|
||||
<th/>
|
||||
<th>Piece</th>
|
||||
<th>Composer</th>
|
||||
<th class="is-hidden-mobile">Running time</th>
|
||||
<th>Part</th>
|
||||
<th/>
|
||||
@ -61,6 +57,7 @@
|
||||
{{ item.work.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="is-hidden-mobile">{{ item.work.composer }}</td>
|
||||
<td class="is-hidden-mobile">{% firstof item.work.running_time "------" %}</td>
|
||||
<td class="select-cell">
|
||||
<input type="hidden" name="works" value="{{ item.work.pk }}"/>
|
||||
@ -74,8 +71,9 @@
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="is-action" onclick="downloadPart({{ item.work.collection_id }}, {{ item.work.pk }})">
|
||||
<i class="fas fa-download" title="Download Part"></i>
|
||||
<span class="button is-link is-small" onclick="downloadPart({{ item.work.collection_id }}, {{ item.work.pk }})">
|
||||
{% icon "download" %}
|
||||
<span>Get</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
@ -85,13 +83,17 @@
|
||||
<tr>
|
||||
<td/>
|
||||
<td/>
|
||||
<td>{% firstof running_time "------" %}</td>
|
||||
<td/>
|
||||
<td>
|
||||
<!--
|
||||
<button class="button is-link is-small" type="submit"><span class="icon"><i class="fas fa-copy"></i></span><span>Single combined PDF</span></button>
|
||||
<a class="button is-link is-small"><span class="icon"><i class="fas fa-archive"></i></span><span>Individual files (zipped)</span></a>
|
||||
-->
|
||||
<td>{% firstof running_time "------" %}</td>
|
||||
<td colspan="2">
|
||||
<button class="button is-link is-small" type="submit" name="action" value="pdf">
|
||||
{% icon "two_pager" %}
|
||||
<span> Single combined PDF</span>
|
||||
</button>
|
||||
<button class="button is-link is-small" type="submit" name="action" value="zip">
|
||||
{% icon "folder_zip" %}
|
||||
<span> Individual files (zipped)</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
<a href="{% url 'item_list_append' project.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
{% icon "add_circle" %}
|
||||
<span>Add</span>
|
||||
</a>
|
||||
<a href="#" onclick="save()" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-save"></i></span>
|
||||
{% icon "save" %}
|
||||
<span>Save</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
@ -26,9 +27,15 @@
|
||||
<td>{{ item.work.name }}</td>
|
||||
<td>{{ item.work.duration }}</td>
|
||||
<td style="text-align: center;">
|
||||
<i class="fas fa-arrow-up clickable" title="Move up" onclick="moveItem({{ item.pk }}, -1)"></i>
|
||||
<i class="fas fa-arrow-down clickable" title="Move down" onclick="moveItem({{ item.pk }}, 1)"></i>
|
||||
<i class="fas fa-trash clickable" title="Remove" onClick="moveItem({{ item.pk }}, 0)"></i>
|
||||
<span class="clickable" title="Move up" onclick="moveItem({{ item.pk }}, -1)">
|
||||
{% icon "arrow_upward" %}
|
||||
</span>
|
||||
<span class="clickable" title="Move down" onclick="moveItem({{ item.pk }}, 1)">
|
||||
{% icon "arrow_downward" %}
|
||||
</span>
|
||||
<span class="clickable" title="Remove" onClick="moveItem({{ item.pk }}, 0)">
|
||||
{% icon "delete" %}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@ -106,4 +113,4 @@ function checkSaved(e) {
|
||||
window.addEventListener('beforeunload', checkSaved);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
{% extends 'interface/project_base.html' %}
|
||||
{% load path_filters %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block media %}
|
||||
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
|
||||
@ -8,12 +9,12 @@
|
||||
|
||||
{% block admin %}
|
||||
<a href="{% url 'work_edit' collection.pk work.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-edit"></i></span>
|
||||
<span>Edit</span>
|
||||
{% icon "edit" %}
|
||||
<span>Edit</span>
|
||||
</a>
|
||||
<a href="{% url 'work_add_to_project' collection.pk work.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
<span>Add to project</span>
|
||||
{% icon "add_ad" %}
|
||||
<span>Add to project</span>
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
@ -68,8 +69,8 @@
|
||||
<div class="column is-half">
|
||||
<div class="box">
|
||||
<h4 class="subtitle is-size-4">
|
||||
<span class="icon"><i class="fas fa-book"></i></span>
|
||||
Printed Parts
|
||||
{% icon "menu_book" %}
|
||||
<span>Printed Parts</span>
|
||||
</h4>
|
||||
<div class="tags">
|
||||
{% for inst, c in work.physical_parts %}
|
||||
@ -81,8 +82,8 @@
|
||||
</div>
|
||||
<div class="box">
|
||||
<h4 class="subtitle is-size-4">
|
||||
<span class="icon"><i class="fas fa-print"></i></span>
|
||||
Digital Parts
|
||||
{% icon "file_copy" %}
|
||||
<span>Digital Parts</span>
|
||||
</h4>
|
||||
<div class="tags">
|
||||
{% with sections=work.digital_parts %}
|
||||
@ -105,8 +106,8 @@
|
||||
<div class="box">
|
||||
<div class="level">
|
||||
<h4 class="subtitle is-size-4">
|
||||
<span class="icon"><i class="fas fa-file"></i></span>
|
||||
Files
|
||||
{% icon "file_present" %}
|
||||
<span>Files<span>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="columns">
|
||||
@ -136,9 +137,9 @@
|
||||
{% endif %}
|
||||
{% if "gdrive" in methods %}
|
||||
<div class="has-text-centered mt-3">
|
||||
<a class="button button-primary is-size-7" href="{% url 'work_gdrive' collection.pk object.pk %}">
|
||||
<span class="icon"><i class="fa-brands fa-google-drive"></i></span>
|
||||
Link Google Drive Files
|
||||
<a class="button button-is-primary is-size-7" href="{% url 'work_gdrive' collection.pk object.pk %}">
|
||||
{% icon "add_to_drive" %}
|
||||
<span>Google Drive</span>
|
||||
</a><br/>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -152,12 +153,14 @@
|
||||
<div class="box">
|
||||
<div class="level">
|
||||
<h4 class="is-size-4">
|
||||
<span class="icon"><i class="fas fa-book-reader"></i></span>
|
||||
Loans
|
||||
{{ "folder_open"| icon }}
|
||||
Projects
|
||||
</h4>
|
||||
<span class="level-right">
|
||||
<a class="icon-text" href="{% url 'work_add_to_project' collection.pk work.pk %}"><span class="icon"><i
|
||||
class="fas fa-plus-circle"></i></span> Checkout</a>
|
||||
<a class="icon-text" href="{% url 'work_add_to_project' collection.pk work.pk %}">
|
||||
{% icon "shopping_cart_checkout" %}
|
||||
<span>Checkout</span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<table class="table is-fullwidth">
|
||||
@ -181,7 +184,7 @@
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td>No current loans</td>
|
||||
<td>No current assignments</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load url_tools %}
|
||||
{% load polyphonic %}
|
||||
|
||||
{% block admin %}
|
||||
|
||||
{% if collection %}
|
||||
|
||||
|
||||
<button class="button is-link" data-api="{% url 'collection_sync' collection=collection.pk %}" data-success="Synced" onclick="background_api(this)">
|
||||
{% icon "sync" %}
|
||||
<span>Sync Collection</span>
|
||||
</button>
|
||||
|
||||
<a href="{% url 'work_add' collection.pk %}" class="button is-link">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
{% icon "add_notes" %}
|
||||
<span>Add a work</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block page %}
|
||||
@ -18,7 +28,9 @@
|
||||
<input class="input" name="q" type="text" placeholder="Filter" value="{{ request.GET.q }}"/>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button" href="?"><i class="fas fa-times"></i></a>
|
||||
<a class="button" href="?">
|
||||
{% icon "close" %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -65,6 +77,7 @@
|
||||
{% else %}
|
||||
disabled
|
||||
{% endif %}>
|
||||
{% icon "arrow_back" %}
|
||||
Previous</a>
|
||||
<a class="pagination-next"
|
||||
{% if page_obj.has_next %}
|
||||
@ -73,6 +86,7 @@
|
||||
disabled
|
||||
{% endif %}>
|
||||
Next
|
||||
{% icon "arrow_forward" %}
|
||||
</a>
|
||||
<ul class="pagination-list">
|
||||
{% for page in page_range %}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load polyphonic %}
|
||||
|
||||
|
||||
|
||||
@ -35,13 +36,14 @@
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<button class="button is-link">
|
||||
<span class="icon"><i class="fas fa-print"></i></span>
|
||||
{% icon "print" %}
|
||||
<span>Print Set</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-link is-light" href="{% url 'work_detail' collection.pk object.pk %}">
|
||||
<span>Cancel</span>
|
||||
{% icon "backspace" %}
|
||||
<span>Cancel</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,4 +52,4 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@ -126,4 +126,14 @@ urlpatterns = [
|
||||
api.CollectionImportView.as_view(),
|
||||
name="collection_import",
|
||||
),
|
||||
path(
|
||||
"api/collections/<int:collection>/sync",
|
||||
api.CollectionSyncView.as_view(),
|
||||
name="collection_sync",
|
||||
),
|
||||
path(
|
||||
"api/works/<int:work>/sync",
|
||||
api.WorkSyncView.as_view(),
|
||||
name="work_sync",
|
||||
),
|
||||
]
|
||||
|
||||
@ -38,7 +38,8 @@ class ProjectItemListView(ProjectMixin, ListView):
|
||||
|
||||
project_works = self.project.works.all()
|
||||
|
||||
instruments = request.POST.getlist("instruments")
|
||||
print(request.POST)
|
||||
instruments = request.POST.getlist("instrument-selection")
|
||||
works = request.POST.getlist("works")
|
||||
|
||||
request.session["part"] = request.POST.get("part", "")
|
||||
@ -65,13 +66,16 @@ class ProjectItemListView(ProjectMixin, ListView):
|
||||
(part.doc.upload.path, part.doc.work.name, part.start, part.end, 1)
|
||||
)
|
||||
|
||||
result = extract_and_concat(sections)
|
||||
action = request.POST.get("action")
|
||||
if action == "pdf":
|
||||
result = extract_and_concat(sections)
|
||||
download_name = f"{self.project.name}.pdf"
|
||||
|
||||
download_name = f"{self.project.name}.pdf"
|
||||
response = FileResponse(result, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'inline; filename="{download_name}"'
|
||||
return response
|
||||
|
||||
response = FileResponse(result, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'inline; filename="{download_name}"'
|
||||
return response
|
||||
return HttpResponse(f"Unknown action: {action}", status=400)
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
|
||||
@ -3,9 +3,12 @@ from polyphonic.interface.views import AuthorizedResourceMixin
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework import generics
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
|
||||
from polyphonic.library.models import Collection, Work, Document, Section, WorkMeta
|
||||
|
||||
from polyphonic.library.gdrive import sync_collection, sync_work
|
||||
|
||||
import requests
|
||||
import urllib
|
||||
@ -208,3 +211,25 @@ class CollectionImportView(AuthorizedResourceMixin, generics.CreateAPIView):
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(collection_id=self.kwargs["pk"])
|
||||
|
||||
|
||||
class WorkSyncView(AuthorizedResourceMixin, APIView):
|
||||
admin_required = True
|
||||
|
||||
def get(self, request, work, format=None):
|
||||
obj = Work.objects.get(pk=work)
|
||||
|
||||
result = sync_work(obj)
|
||||
|
||||
return Response(result)
|
||||
|
||||
|
||||
class CollectionSyncView(AuthorizedResourceMixin, APIView):
|
||||
admin_required = True
|
||||
|
||||
def get(self, request, collection, format=None):
|
||||
obj = Collection.objects.get(pk=collection)
|
||||
|
||||
result = sync_collection(obj)
|
||||
|
||||
return Response(result)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user