diff --git a/.github/workflows/frontend_format.yml b/.github/workflows/frontend_format.yml index d4bb660..c24f9e9 100644 --- a/.github/workflows/frontend_format.yml +++ b/.github/workflows/frontend_format.yml @@ -5,6 +5,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Install Prettier + run: npm install prettier@3.1.0 - name: Run Prettier run: npx prettier --check "**/*.css" "**/*.js" --trailing-comma es5 - name: Install djlint diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index accb0b5..d402719 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,10 +5,10 @@ repos: - id: black - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.3.2 + rev: v3.1.0 hooks: - id: prettier - additional_dependencies: ["prettier@2.3.2"] + additional_dependencies: ["prettier@3.1.0"] files: "\\.(js|jsx|ts|tsx|css|scss|less|json|yaml|yml|md)$" - repo: https://github.com/djlint/djLint diff --git a/web-app/django/VIM/apps/instruments/management/commands/download_imgs.py b/web-app/django/VIM/apps/instruments/management/commands/download_imgs.py index 0aaf4d8..9c158fb 100644 --- a/web-app/django/VIM/apps/instruments/management/commands/download_imgs.py +++ b/web-app/django/VIM/apps/instruments/management/commands/download_imgs.py @@ -16,7 +16,7 @@ class Command(BaseCommand): OUTPUT_DIR = os.path.join( settings.STATIC_ROOT, "instruments", "images", "instrument_imgs" ) - CSV_PATH = "startup_data/all_instruments_16aug_2024.csv" + CSV_PATH = "startup_data/all_instruments_11oct_2024.csv" help = "Download images and create thumbnails for instruments" diff --git a/web-app/django/VIM/apps/instruments/management/commands/import_instruments.py b/web-app/django/VIM/apps/instruments/management/commands/import_instruments.py index 31aee1f..0328ded 100644 --- a/web-app/django/VIM/apps/instruments/management/commands/import_instruments.py +++ b/web-app/django/VIM/apps/instruments/management/commands/import_instruments.py @@ -15,7 +15,7 @@ class Command(BaseCommand): NOTE: For now, this script only imports instrument names in English and French. It also only imports a set of previously-curated instruments that have images available. - This list of instruments is stored in startup_data/vim_instruments_with_images-15sept.csv + This list of instruments is stored in startup_data/all_instruments_with_16aug_2024.csv """ help = "Imports instrument objects" @@ -48,6 +48,20 @@ def parse_instrument_data( ins_names: dict[str, str] = { value["language"]: value["value"] for key, value in ins_labels.items() } + + # Get available instrument descriptions + ins_descriptions: dict = instrument_data["descriptions"] + ins_descs: dict[str, str] = { + value["language"]: value["value"] for key, value in ins_descriptions.items() + } + + # Get available instrument aliases + ins_aliases: dict = instrument_data["aliases"] + ins_alias: dict[str, list[str]] = { + key: [value["value"] for value in values] + for key, values in ins_aliases.items() + } + # Get Hornbostel-Sachs and MIMO classifications, if available ins_hbs: Optional[list[dict]] = instrument_data["claims"].get("P1762") ins_mimo: Optional[list[dict]] = instrument_data["claims"].get("P3763") @@ -62,6 +76,8 @@ def parse_instrument_data( parsed_data: dict[str, str | dict[str, str]] = { "wikidata_id": instrument_id, "ins_names": ins_names, + "ins_descs": ins_descs, + "ins_alias": ins_alias, "hornbostel_sachs_class": hbs_class, "mimo_class": mimo_class, } @@ -80,7 +96,7 @@ def get_instrument_data(self, instrument_ids: list[str]) -> list[dict]: ins_ids_str: str = "|".join(instrument_ids) url = ( "https://www.wikidata.org/w/api.php?action=wbgetentities&" - f"ids={ins_ids_str}&format=json&props=labels|descriptions|" + f"ids={ins_ids_str}&format=json&props=labels|descriptions|aliases|" "claims&languages=en|fr" ) response = requests.get(url, timeout=10) @@ -104,14 +120,28 @@ def create_database_objects( thumbnail_img_path [str]: Path to the thumbnail of the instrument image """ ins_names = instrument_attrs.pop("ins_names") + ins_descs = instrument_attrs.pop("ins_descs") + ins_alias = instrument_attrs.pop("ins_alias") instrument = Instrument.objects.create(**instrument_attrs) for lang, name in ins_names.items(): + description = ins_descs.get(lang, "") + # Create InstrumentName object for "name" in "lang" with "description" InstrumentName.objects.create( instrument=instrument, language=self.language_map[lang], + description=description, name=name, source_name="Wikidata", ) + alias = ins_alias.get(lang, []) + # Create InstrumentName object for "alias" in language "lang" + for alias_name in alias: + InstrumentName.objects.create( + instrument=instrument, + language=self.language_map[lang], + name=alias_name, + source_name="Wikidata", + ) img_obj = AVResource.objects.create( instrument=instrument, type="image", @@ -130,7 +160,7 @@ def create_database_objects( def handle(self, *args, **options) -> None: with open( - "startup_data/all_instruments_16aug_2024.csv", + "startup_data/all_instruments_11oct_2024.csv", encoding="utf-8-sig", ) as csvfile: reader = csv.DictReader(csvfile) diff --git a/web-app/django/VIM/apps/instruments/migrations/0006_instrumentname_description.py b/web-app/django/VIM/apps/instruments/migrations/0006_instrumentname_description.py new file mode 100644 index 0000000..fb05de8 --- /dev/null +++ b/web-app/django/VIM/apps/instruments/migrations/0006_instrumentname_description.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.5 on 2024-10-21 15:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("instruments", "0005_remove_language_wikidata_id"), + ] + + operations = [ + migrations.AddField( + model_name="instrumentname", + name="description", + field=models.CharField( + blank=True, help_text="Description of the instrument name" + ), + ), + ] diff --git a/web-app/django/VIM/apps/instruments/models/instrument_name.py b/web-app/django/VIM/apps/instruments/models/instrument_name.py index b9d58c3..a8af53a 100644 --- a/web-app/django/VIM/apps/instruments/models/instrument_name.py +++ b/web-app/django/VIM/apps/instruments/models/instrument_name.py @@ -8,3 +8,6 @@ class InstrumentName(models.Model): source_name = models.CharField( max_length=50, blank=False, help_text="Who or what called the instrument this?" ) # Stand-in for source data; format TBD + description = models.CharField( + blank=True, help_text="Description of the instrument name" + ) # Stand-in for description diff --git a/web-app/django/VIM/apps/instruments/static/instruments/css/detail.css b/web-app/django/VIM/apps/instruments/static/instruments/css/detail.css deleted file mode 100644 index 7cb5e4f..0000000 --- a/web-app/django/VIM/apps/instruments/static/instruments/css/detail.css +++ /dev/null @@ -1,99 +0,0 @@ -.instrument-detail { - display: flex; - flex-wrap: nowrap; - align-items: center; - flex-direction: column; - padding: 50px; -} -.detail-header { - display: flex; - flex-direction: row; - justify-content: flex-start; - flex-wrap: wrap; - width: 100%; -} -.detail-header hr { - width: 100%; -} -.detail-body { - display: flex; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-evenly; - align-items: flex-start; - width: 100%; -} -.detail-image { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: flex-start; - align-items: flex-start; -} -.instrument-image { - max-width: 50%; - margin-right: 10px; -} -.detail-image-caption { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: flex-start; - align-items: flex-start; -} -.instrument-forms { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - width: 100%; -} -.name-form-item { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; - align-items: center; -} -.name-field { - width: 90%; -} -input.edit-field { - width: inherit; -} -th[scope='col'] { - width: 30%; -} -th[scope='row'] { - width: 20%; -} -.button-group { - display: flex; - flex-direction: row; -} -.edit-field, -.btn.cancel, -.btn.publish { - display: none; -} -.btn { - display: inline-block; - padding: 5px 10px; - cursor: pointer; - text-align: center; - text-decoration: none; - outline: none; - color: #fff; - border: none; - border-radius: 3px; - margin-right: 5px; -} -.btn.edit { - background-color: #ffc107; -} -.btn.cancel { - background-color: #dc3545; -} -.btn.publish { - background-color: #28a745; -} diff --git a/web-app/django/VIM/apps/instruments/static/instruments/css/index.css b/web-app/django/VIM/apps/instruments/static/instruments/css/index.css index cbd6a2f..155bee5 100644 --- a/web-app/django/VIM/apps/instruments/static/instruments/css/index.css +++ b/web-app/django/VIM/apps/instruments/static/instruments/css/index.css @@ -91,6 +91,83 @@ hr { background-color: #faf1e4; } +.instrument-img-container:hover .instrument-img { + opacity: 0.3; +} + +.instrument-img { + opacity: 1; + display: block; + width: 100%; + height: auto; + transition: 0.5s ease; + backface-visibility: hidden; +} + +.instrument-img-container:hover .middle-button-group { + opacity: 1; +} + +.middle-button-group { + transition: 0.5s ease; + opacity: 0; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + text-align: center; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + justify-content: space-around; + width: max-content; +} + +.middle-button-group .btn { + background-color: #435334; + border: 1px solid #435334; + color: white; + font-size: 14px; + margin-bottom: 6px; +} + +.middle-button-group .btn:hover { + background-color: #9eb384; + border: 1px solid #9eb384; + color: white; +} + +.modal-btn { + background-color: #435334; + border: 1px solid #435334; +} + +.modal-btn:hover { + background-color: #9eb384; + border: 1px solid #9eb384; +} + +#instrumentNameInModal { + font-weight: bold; + color: #435334; +} + +#previewImages { + display: flex; + flex-wrap: wrap; +} + +#previewImages .col-3 { + margin-bottom: 15px; +} + +#previewImages img { + width: 100%; + height: auto; + border-radius: 5px; +} + .card-title { color: #435334; } diff --git a/web-app/django/VIM/apps/instruments/static/instruments/js/AddName.js b/web-app/django/VIM/apps/instruments/static/instruments/js/AddName.js new file mode 100644 index 0000000..075b288 --- /dev/null +++ b/web-app/django/VIM/apps/instruments/static/instruments/js/AddName.js @@ -0,0 +1,370 @@ +// Get the modal element +var addNameModal = document.getElementById('addNameModal'); + +addNameModal.addEventListener('show.bs.modal', function (event) { + var button = event.relatedTarget; + var instrumentName = button.getAttribute('data-instrument-name'); + var instrumentWikidataId = button.getAttribute('data-instrument-wikidata-id'); + var instrumentNameInModal = addNameModal.querySelector( + '#instrumentNameInModal', + ); + instrumentNameInModal.textContent = instrumentName; + + var instrumentWikidataIdInModal = addNameModal.querySelector( + '#instrumentWikidataIdInModal', + ); + instrumentWikidataIdInModal.textContent = instrumentWikidataId; +}); + +// the number of rows in the modal +let rowIndex = 1; + +// Function to validate that the user has selected a valid language from the datalist +function isValidLanguage(inputElement) { + const datalistId = inputElement.getAttribute('list'); + const datalist = document.getElementById(datalistId); + const options = datalist.querySelectorAll('option'); + + // Check if the input value matches any option value in the datalist + for (let option of options) { + if (option.value === inputElement.value) { + return true; // Valid language selected + } + } + return false; // Invalid language input +} + +// Function to check if a name already exists in Wikidata for the given language +async function checkNameInWikidata(wikidataId, languageCode, languageLabel) { + const sparqlQuery = ` + SELECT ?nameLabel WHERE { + wd:${wikidataId} rdfs:label ?nameLabel . + FILTER(LANG(?nameLabel) = "${languageCode}") + } LIMIT 1 + `; + + const endpointUrl = 'https://query.wikidata.org/sparql'; + const queryUrl = `${endpointUrl}?query=${encodeURIComponent( + sparqlQuery, + )}&format=json`; + + try { + const response = await fetch(queryUrl); + const data = await response.json(); + + if (data.results.bindings.length > 0) { + return { exists: true, name: data.results.bindings[0].nameLabel.value }; + } else { + return { exists: false }; + } + } catch (error) { + console.error('Error querying Wikidata:', error); + throw new Error('Wikidata query failed'); + } +} + +// Reusable function to create a new row +function createRow(index) { + const row = document.createElement('div'); + row.classList.add('row', 'mb-1', 'name-row'); + + // Create datalist options dynamically using the global languages variable + let datalistOptions = languages + .map( + (language) => ` + + `, + ) + .join(''); + + row.innerHTML = ` +
+ + + + ${datalistOptions} + +
This instrument does not have a name in this language yet. You can add a new name.
+
This instrument already has a name in this language.
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ +
+ `; + + // Add event listener for remove button + row.querySelector('.remove-row-btn').addEventListener('click', function () { + row.remove(); + updateRemoveButtons(); // Ensure correct behavior when rows are removed + }); + + return row; +} + +// Function to update remove button visibility based on the number of rows +function updateRemoveButtons() { + const rows = document.querySelectorAll('.name-row'); + rows.forEach((row, index) => { + const removeButton = row.querySelector('.remove-row-btn'); + // Show the remove button only if there are more than one row + if (rows.length > 1) { + removeButton.style.display = 'inline-block'; + } else { + removeButton.style.display = 'none'; // Hide the button if only one row remains + } + }); +} + +// Function to validate and check all rows on form submission +document + .getElementById('addNameForm') + .addEventListener('submit', async function (event) { + event.preventDefault(); // Prevent form submission + + const nameRows = document.querySelectorAll('.name-row'); + let allValid = true; + let publishResults = ''; // Collect the results for confirmation + + // Iterate over each row and check if the name already exists in Wikidata + for (let row of nameRows) { + const languageInput = row.querySelector('input[list]'); + const nameInput = row.querySelector('.name-input input[type="text"]'); + const sourceInput = row.querySelector('.source-input input[type="text"]'); + const descriptionInput = row.querySelector( + '.description-input input[type="text"]', + ); + const aliasInput = row.querySelector('.alias-input input[type="text"]'); + + const languageCode = languageInput.value; + const selectedOption = row.querySelector( + `option[value="${languageCode}"]`, + ); + const languageLabel = selectedOption ? selectedOption.textContent : ''; + + // get feedback elements for valid and invalid inputs respectively for language and name + const languageFeedbackValid = row.querySelector( + '.language-input .valid-feedback', + ); + const languageFeedbackInvalid = row.querySelector( + '.language-input .invalid-feedback', + ); + const nameFeedbackInvalid = row.querySelector( + '.name-input .invalid-feedback', + ); + const sourceFeedbackInvalid = row.querySelector( + '.source-input .invalid-feedback', + ); + + const wikidataId = document + .getElementById('instrumentWikidataIdInModal') + .textContent.trim(); + + if (!isValidLanguage(languageInput)) { + languageInput.classList.add('is-invalid'); + languageFeedbackInvalid.textContent = + 'Please select a valid language from the list.'; + allValid = false; + continue; + } + + try { + const result = await checkNameInWikidata( + wikidataId, + languageCode, + languageLabel, + ); + if (result.exists) { + languageInput.classList.add('is-invalid'); + languageInput.classList.remove('is-valid'); + languageFeedbackInvalid.textContent = `This instrument already has a name in ${languageLabel} (${languageCode}): ${result.name}`; + allValid = false; + } else { + languageInput.classList.add('is-valid'); + languageInput.classList.remove('is-invalid'); + languageFeedbackValid.textContent = `This instrument does not have a name in ${languageLabel} (${languageCode}) yet. You can add a new name.`; + + // check if name is empty + if (nameInput.value.trim() === '') { + nameInput.classList.add('is-invalid'); + nameInput.classList.remove('is-valid'); + nameFeedbackInvalid.textContent = + 'Please enter a name for this instrument in the selected language.'; + allValid = false; + } else { + nameInput.classList.add('is-valid'); + nameInput.classList.remove('is-invalid'); + } + + // check if source is empty + if (sourceInput.value.trim() === '') { + sourceInput.classList.add('is-invalid'); + sourceInput.classList.remove('is-valid'); + sourceFeedbackInvalid.textContent = + 'Please enter the source of this name.'; + allValid = false; + } else { + sourceInput.classList.add('is-valid'); + sourceInput.classList.remove('is-invalid'); + } + + // Add the result to the confirmation message + publishResults += `
${languageLabel} (${languageCode}): ${nameInput.value}; Source: ${sourceInput.value}; Description: ${descriptionInput.value}; Alias: ${aliasInput.value}`; + } + } catch (error) { + displayMessage( + 'There was an error checking Wikidata. Please try again later.', + 'danger', + ); + return; // Stop further processing + } + } + + // If all rows are valid, show the confirmation modal + if (allValid) { + document.getElementById('publishResults').innerHTML = + `You will publish the following:
${publishResults}`; + const confirmationModal = new bootstrap.Modal( + document.getElementById('confirmationModal'), + ); + confirmationModal.show(); + } + }); + +// Function to reset the modal and ensure only one row is present +function resetModal() { + const nameRows = document.getElementById('nameRows'); + nameRows.innerHTML = ''; // Clear all rows + nameRows.appendChild(createRow(1)); // Add initial row + updateRemoveButtons(); // Ensure remove buttons are updated on reset + rowIndex = 1; // Reset row index +} + +// Fetch languages when the modal is loaded +document.addEventListener('DOMContentLoaded', async () => { + resetModal(); +}); + +// Add a new row when the 'Add another row' button is clicked +document.getElementById('addRowBtn').addEventListener('click', function () { + rowIndex++; + const nameRows = document.getElementById('nameRows'); + nameRows.appendChild(createRow(rowIndex)); + updateRemoveButtons(); // Update remove buttons after adding a new row +}); + +// Reset the modal when hidden +document + .getElementById('addNameModal') + .addEventListener('hide.bs.modal', resetModal); + +// Function to handle confirm publish action +document + .getElementById('confirmPublishBtn') + .addEventListener('click', function () { + const wikidataId = document + .getElementById('instrumentWikidataIdInModal') + .textContent.trim(); + const entries = []; + + // Collect the data to publish + const nameRows = document.querySelectorAll('.name-row'); + nameRows.forEach((row) => { + const languageInput = row.querySelector('input[list]'); + const nameInput = row.querySelector('.name-input input[type="text"]'); + const sourceInput = row.querySelector('.source-input input[type="text"]'); + const descriptionInput = row.querySelector( + '.description-input input[type="text"]', + ); + const aliasInput = row.querySelector('.alias-input input[type="text"]'); + + const languageCode = languageInput.value; + const nameValue = nameInput.value; + const sourceValue = sourceInput.value; + const descriptionValue = descriptionInput.value || ''; + const aliasValue = aliasInput.value || ''; + + console.log('languageCode: ', languageCode); + console.log('nameValue: ', nameValue); + console.log('sourceValue: ', sourceValue); + console.log('descriptionValue: ', descriptionValue); + console.log('aliasValue: ', aliasValue); + + entries.push({ + language: languageCode, + name: nameValue, + source: sourceValue, + description: descriptionValue, + alias: aliasValue, + }); + }); + + // Check if the user wants to publish to Wikidata + const publishToWikidata = document.getElementById( + 'publishToWikidataCheckbox', + ).checked; + + // Publish data to our database and then to Wikidata + fetch('/publish_name/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]') + .value, + }, + body: JSON.stringify({ + wikidata_id: wikidataId, + entries: entries, + publish_to_wikidata: publishToWikidata, + }), + }) + .then((response) => response.json()) + .then((data) => { + if (data.status === 'success') { + alert('Data published successfully!'); + // Close both modals + const addNameModal = bootstrap.Modal.getInstance( + document.getElementById('addNameModal'), + ); + const confirmationModal = bootstrap.Modal.getInstance( + document.getElementById('confirmationModal'), + ); + + if (addNameModal) { + addNameModal.hide(); // Close the 'Add Name' modal + } + + if (confirmationModal) { + confirmationModal.hide(); // Close the 'Confirmation' modal + } + } else { + alert('Error: ' + data.message); + } + }) + .catch((error) => { + alert('An error occurred while publishing the data: ' + error.message); + }); + }); diff --git a/web-app/django/VIM/apps/instruments/static/instruments/js/ImageUpload.js b/web-app/django/VIM/apps/instruments/static/instruments/js/ImageUpload.js new file mode 100644 index 0000000..6c9e8cb --- /dev/null +++ b/web-app/django/VIM/apps/instruments/static/instruments/js/ImageUpload.js @@ -0,0 +1,69 @@ +function displaySelectedImage(event, elementId) { + const selectedImage = document.getElementById(elementId); + const fileInput = event.target; + + if (fileInput.files && fileInput.files[0]) { + const reader = new FileReader(); + + reader.onload = function (e) { + selectedImage.src = e.target.result; + }; + + reader.readAsDataURL(fileInput.files[0]); + } +} + +// Get the modal element +var uploadImagesModal = document.getElementById('uploadImagesModal'); + +uploadImagesModal.addEventListener('show.bs.modal', function (event) { + var button = event.relatedTarget; + var instrumentName = button.getAttribute('data-instrument-name'); + var instrumentWikidataId = button.getAttribute('data-instrument-wikidata-id'); + var instrumentNameInModal = uploadImagesModal.querySelector( + '#instrumentNameInModal' + ); + instrumentNameInModal.textContent = instrumentName; + + var instrumentWikidataIdInModal = uploadImagesModal.querySelector( + '#instrumentWikidataIdInModal' + ); + instrumentWikidataIdInModal.textContent = instrumentWikidataId; +}); + +document + .getElementById('imageFiles') + .addEventListener('change', function (event) { + var previewContainer = document.getElementById('previewImages'); + previewContainer.innerHTML = ''; // Clear existing previews + + var files = event.target.files; + + for (var i = 0; i < files.length; i++) { + var file = files[i]; + + // Ensure that the file is an image + if (file.type.startsWith('image/')) { + var reader = new FileReader(); + + reader.onload = (function (file) { + return function (e) { + var colDiv = document.createElement('div'); + colDiv.className = 'col-3'; + + var img = document.createElement('img'); + img.src = e.target.result; + img.className = 'img-thumbnail'; + img.alt = file.name; + img.style.maxHeight = '150px'; + img.style.objectFit = 'cover'; + + colDiv.appendChild(img); + previewContainer.appendChild(colDiv); + }; + })(file); + + reader.readAsDataURL(file); + } + } + }); diff --git a/web-app/django/VIM/apps/instruments/static/instruments/js/InstrumentDetail.js b/web-app/django/VIM/apps/instruments/static/instruments/js/InstrumentDetail.js deleted file mode 100644 index c868191..0000000 --- a/web-app/django/VIM/apps/instruments/static/instruments/js/InstrumentDetail.js +++ /dev/null @@ -1,41 +0,0 @@ -document.addEventListener('DOMContentLoaded', function () { - const editButtons = document.querySelectorAll('.btn.edit'); - const cancelButtons = document.querySelectorAll('.btn.cancel'); - const publishButtons = document.querySelectorAll('.btn.publish'); - - editButtons.forEach((button) => { - button.addEventListener('click', function () { - const parentTd = this.closest('td'); - parentTd.querySelector('.view-field').style.display = 'none'; - parentTd.querySelector('.edit-field').style.display = 'inline-block'; - parentTd.querySelector('.btn.cancel').style.display = 'inline-block'; - parentTd.querySelector('.btn.publish').style.display = 'inline-block'; - this.style.display = 'none'; - }); - }); - - cancelButtons.forEach((button) => { - button.addEventListener('click', function () { - const parentTd = this.closest('td'); - parentTd.querySelector('.view-field').style.display = 'inline'; - parentTd.querySelector('.edit-field').style.display = 'none'; - parentTd.querySelector('.btn.edit').style.display = 'inline-block'; - parentTd.querySelector('.btn.publish').style.display = 'none'; - this.style.display = 'none'; - }); - }); - - publishButtons.forEach((button) => { - button.addEventListener('click', function () { - const parentTd = this.closest('td'); - const newValue = parentTd.querySelector('.edit-field').value; - // TODO: request to update the value on the server - parentTd.querySelector('.view-field').textContent = newValue; - parentTd.querySelector('.view-field').style.display = 'inline'; - parentTd.querySelector('.edit-field').style.display = 'none'; - parentTd.querySelector('.btn.edit').style.display = 'inline-block'; - this.style.display = 'none'; - parentTd.querySelector('.btn.cancel').style.display = 'none'; - }); - }); -}); diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/detail.html b/web-app/django/VIM/apps/instruments/templates/instruments/detail.html deleted file mode 100644 index 2fe1dc7..0000000 --- a/web-app/django/VIM/apps/instruments/templates/instruments/detail.html +++ /dev/null @@ -1,157 +0,0 @@ -{% extends "base.html" %} - -{% load static %} - -{% block title %} - Instrument Detail -{% endblock title %} - -{% block css_files %} - - -{% endblock css_files %} - -{% block content %} -
-
-

- {% for instrumentname in instrument_names %} - {% if instrumentname.language.en_label == active_language.en_label %} - {{ instrumentname.name|title }} - {% endif %} - {% endfor %} -

-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - -
Wikidata ID - -
Hornbostel-Sachs Classification -
-
- {{ instrument.hornbostel_sachs_class }} -
-
-
MIMO Classification - -
Instrument Names in Different Languages - - - - - - - - - - {% for instrumentname in instrument_names %} - - - - - - {% endfor %} - -
- Language - - Name - - Source -
-
-
- {{ instrumentname.language.en_label }} - -
-
- - - -
-
-
-
-
- {{ instrumentname.name }} - -
-
- - - -
-
-
-
-
- {{ instrumentname.source_name }} - -
-
- - - -
-
-
-
Image -
- -
-
-
-
-
-{% endblock content %} - -{% block scripts %} - -{% endblock scripts %} diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/includes/addNameModal.html b/web-app/django/VIM/apps/instruments/templates/instruments/includes/addNameModal.html new file mode 100644 index 0000000..f266d3a --- /dev/null +++ b/web-app/django/VIM/apps/instruments/templates/instruments/includes/addNameModal.html @@ -0,0 +1,105 @@ +{% load static %} + + + + + + diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/includes/instrumentContainer.html b/web-app/django/VIM/apps/instruments/templates/instruments/includes/instrumentContainer.html new file mode 100644 index 0000000..4909f6d --- /dev/null +++ b/web-app/django/VIM/apps/instruments/templates/instruments/includes/instrumentContainer.html @@ -0,0 +1,28 @@ +{% load static %} + +
+ instrument thumbnail +
+ {% if user.is_authenticated %} + + + {% endif %} + View on Wikidata +
+
diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/includes/masonryView.html b/web-app/django/VIM/apps/instruments/templates/instruments/includes/masonryView.html index d920518..f0e93c6 100644 --- a/web-app/django/VIM/apps/instruments/templates/instruments/includes/masonryView.html +++ b/web-app/django/VIM/apps/instruments/templates/instruments/includes/masonryView.html @@ -4,20 +4,17 @@ id="masonry-view"> {% for instrument in instruments %}
- +
- instrument thumbnail + {% include "instruments/includes/instrumentContainer.html" %} +

- {% for instrumentname in instrument.instrumentname_set.all %} - {{ instrumentname.name|title }} - {% endfor %} + {{ instrument.instrumentname_set.all.first.name|title }}

-
+
{% endfor %} diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/includes/stdView.html b/web-app/django/VIM/apps/instruments/templates/instruments/includes/stdView.html index a645148..c9c4825 100644 --- a/web-app/django/VIM/apps/instruments/templates/instruments/includes/stdView.html +++ b/web-app/django/VIM/apps/instruments/templates/instruments/includes/stdView.html @@ -5,22 +5,19 @@ style="display:none"> {% for instrument in instruments %}
- +
- instrument thumbnail + {% include "instruments/includes/instrumentContainer.html" %} +

- {% for instrumentname in instrument.instrumentname_set.all %} - {{ instrumentname.name|title }} - {% endfor %} + {{ instrument.instrumentname_set.all.first.name|title }}

-
+
{% endfor %} diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/includes/uploadImgModal.html b/web-app/django/VIM/apps/instruments/templates/instruments/includes/uploadImgModal.html new file mode 100644 index 0000000..ecac235 --- /dev/null +++ b/web-app/django/VIM/apps/instruments/templates/instruments/includes/uploadImgModal.html @@ -0,0 +1,44 @@ +{% load static %} + + + diff --git a/web-app/django/VIM/apps/instruments/templates/instruments/index.html b/web-app/django/VIM/apps/instruments/templates/instruments/index.html index 42bc80a..159af10 100644 --- a/web-app/django/VIM/apps/instruments/templates/instruments/index.html +++ b/web-app/django/VIM/apps/instruments/templates/instruments/index.html @@ -17,7 +17,7 @@
+ {% include "instruments/includes/addNameModal.html" %} + {% include "instruments/includes/uploadImgModal.html" %} +
diff --git a/web-app/django/VIM/urls.py b/web-app/django/VIM/urls.py index e225526..88d36f7 100644 --- a/web-app/django/VIM/urls.py +++ b/web-app/django/VIM/urls.py @@ -18,15 +18,15 @@ from django.contrib import admin from django.urls import path, include from django.conf import settings -from VIM.apps.instruments.views.instrument_list import InstrumentList -from VIM.apps.instruments.views.instrument_detail import InstrumentDetail from django.conf.urls.i18n import i18n_patterns +from VIM.apps.instruments.views.instrument_list import InstrumentList +from VIM.apps.instruments.views.publish_name import publish_name urlpatterns = i18n_patterns( path("admin/", admin.site.urls), path("", include("VIM.apps.main.urls", namespace="main")), path("instruments/", InstrumentList.as_view(), name="instrument-list"), - path("instrument//", InstrumentDetail.as_view(), name="instrument-detail"), + path("publish_name/", publish_name, name="publish_name"), prefix_default_language=False, ) diff --git a/web-app/django/startup_data/all_instruments_16aug_2024.csv b/web-app/django/startup_data/all_instruments_11oct_2024.csv similarity index 97% rename from web-app/django/startup_data/all_instruments_16aug_2024.csv rename to web-app/django/startup_data/all_instruments_11oct_2024.csv index 0483fc3..fccf488 100644 --- a/web-app/django/startup_data/all_instruments_16aug_2024.csv +++ b/web-app/django/startup_data/all_instruments_11oct_2024.csv @@ -858,3 +858,25 @@ http://www.wikidata.org/entity/Q18449477,clog fiddle,http://commons.wikimedia.or http://www.wikidata.org/entity/Q60689,banhu,http://commons.wikimedia.org/wiki/Special:FilePath/Banhu.jpg http://www.wikidata.org/entity/Q98647213,octocontrabass clarinet,http://commons.wikimedia.org/wiki/Special:FilePath/Clarinette%20octo-contrebasse%20Leblanc.jpg http://www.wikidata.org/entity/Q19650003,sets of free reeds,http://commons.wikimedia.org/wiki/Special:FilePath/Hohner%20Multimonica%20MIM.jpg +http://www.wikidata.org/wiki/Q4837771,babendil,http://commons.wikimedia.org/wiki/Special:FilePath/Babendil_03.jpg +http://www.wikidata.org/wiki/Q11859952,caxirola,http://commons.wikimedia.org/wiki/Special:FilePath/Carlinhos_Brown_com_Caxirolas.jpg +http://www.wikidata.org/wiki/Q1095097,clapstick,http://commons.wikimedia.org/wiki/Special:FilePath/Clapsticks.JPG +http://www.wikidata.org/wiki/Q1207707,dhol,http://commons.wikimedia.org/wiki/Special:FilePath/Armenian_Dhol.jpg +http://www.wikidata.org/wiki/Q3773253,gong bass drum,http://commons.wikimedia.org/wiki/Special:FilePath/Gong_Drum_(from_Emil_Richards_Collection).jpg +http://www.wikidata.org/wiki/Q1249186,idakka,http://commons.wikimedia.org/wiki/Special:FilePath/Idaykka.jpg +http://www.wikidata.org/wiki/Q1062015,janggu,http://commons.wikimedia.org/wiki/Special:FilePath/Janggu.jpg +http://www.wikidata.org/wiki/Q7997355,kanjira,http://commons.wikimedia.org/wiki/Special:FilePath/Kanjira.JPG +http://www.wikidata.org/wiki/Q3194590,Kebero,http://commons.wikimedia.org/wiki/Special:FilePath/Äthiopien_Kirchentrommel_Linden-Museum_21084.jpg +http://www.wikidata.org/wiki/Q1371129,krakebs,http://commons.wikimedia.org/wiki/Special:FilePath/Qaraqib.jpg +http://www.wikidata.org/wiki/Q6754723,maram,http://commons.wikimedia.org/wiki/Special:FilePath/Maram.jpg +http://www.wikidata.org/wiki/Q744831,mridangam,http://commons.wikimedia.org/wiki/Special:FilePath/Wiki-mridangam.jpg +http://www.wikidata.org/wiki/Q1898734,naqareh,http://commons.wikimedia.org/wiki/Special:FilePath/COLLECTIE_TROPENMUSEUM_Keteltrom_van_koper_TMnr_3492-7a.jpg +http://www.wikidata.org/wiki/Q892936,octaban,http://commons.wikimedia.org/wiki/Special:FilePath/Home_made_octoban.JPG +http://www.wikidata.org/wiki/Q2046658,pakhavaj,http://commons.wikimedia.org/wiki/Special:FilePath/Gundecha_Brothers_02A.jpg +http://www.wikidata.org/wiki/Q3196570,qilaut,"http://commons.wikimedia.org/wiki/Special:FilePath/Aniurunna,_Inuit_man,_singing_and_playing_drum.jpg" +http://www.wikidata.org/wiki/Q747604,sabar,http://commons.wikimedia.org/wiki/Special:FilePath/M'bung_M'bung's.jpg +http://www.wikidata.org/wiki/Q7410116,samphor,http://commons.wikimedia.org/wiki/Special:FilePath/Samphor.jpg +http://www.wikidata.org/wiki/Q7681062,tamborita calentana,http://commons.wikimedia.org/wiki/Special:FilePath/Tambrita.jpg +http://www.wikidata.org/wiki/Q1562149,taphon,http://commons.wikimedia.org/wiki/Special:FilePath/Thai_taphon.jpg +http://www.wikidata.org/wiki/Q7690562,tbilat,"http://commons.wikimedia.org/wiki/Special:FilePath/Tbilat,Marokko2.jpg" +http://www.wikidata.org/wiki/Q1355394,thavil,"http://commons.wikimedia.org/wiki/Special:FilePath/Tavil,_%E0%AE%A4%E0%AE%B5%E0%AE%BF%E0%AE%B2%E0%AF%8D,_%E0%B4%A4%E0%B4%B5%E0%B4%BF%E0%B5%BD.jpg" \ No newline at end of file