mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
editor: Functional recording and work editor
This commit is contained in:
parent
f0135cd415
commit
d7401195b3
8 changed files with 487 additions and 138 deletions
|
|
@ -17,19 +17,11 @@ pub use tables::{Album, Instrument, Person, Role};
|
|||
pub struct Work {
|
||||
pub work_id: String,
|
||||
pub name: TranslatedString,
|
||||
pub parts: Vec<WorkPart>,
|
||||
pub parts: Vec<Work>,
|
||||
pub persons: Vec<Composer>,
|
||||
pub instruments: Vec<Instrument>,
|
||||
}
|
||||
|
||||
// TODO: Handle part composers.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct WorkPart {
|
||||
pub work_id: String,
|
||||
pub level: u8,
|
||||
pub name: TranslatedString,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Selectable, Clone, Debug)]
|
||||
pub struct Composer {
|
||||
#[diesel(embed)]
|
||||
|
|
@ -122,42 +114,17 @@ impl PartialEq for Composer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Eq for WorkPart {}
|
||||
impl PartialEq for WorkPart {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.work_id == other.work_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Work {
|
||||
pub fn from_table(data: tables::Work, connection: &mut SqliteConnection) -> Result<Self> {
|
||||
fn visit_children(
|
||||
work_id: &str,
|
||||
level: u8,
|
||||
connection: &mut SqliteConnection,
|
||||
) -> Result<Vec<WorkPart>> {
|
||||
let mut parts = Vec::new();
|
||||
|
||||
let children: Vec<tables::Work> = works::table
|
||||
.filter(works::parent_work_id.eq(work_id))
|
||||
.load(connection)?;
|
||||
|
||||
for child in children {
|
||||
let mut grand_children = visit_children(&child.work_id, level + 1, connection)?;
|
||||
|
||||
parts.push(WorkPart {
|
||||
work_id: child.work_id,
|
||||
level,
|
||||
name: child.name,
|
||||
});
|
||||
|
||||
parts.append(&mut grand_children);
|
||||
}
|
||||
|
||||
Ok(parts)
|
||||
}
|
||||
|
||||
let parts = visit_children(&data.work_id, 0, connection)?;
|
||||
// Note: Because this calls Work::from_table for each part, this recursively
|
||||
// adds all children. It does not check for circularity.
|
||||
let parts = works::table
|
||||
.order(works::sequence_number)
|
||||
.filter(works::parent_work_id.eq(&data.work_id))
|
||||
.load::<tables::Work>(connection)?
|
||||
.into_iter()
|
||||
.map(|w| Work::from_table(w, connection))
|
||||
.collect::<Result<Vec<Work>>>()?;
|
||||
|
||||
let persons: Vec<Composer> = persons::table
|
||||
.inner_join(work_persons::table.inner_join(roles::table))
|
||||
|
|
|
|||
|
|
@ -15,4 +15,5 @@ pub mod translation_editor;
|
|||
pub mod translation_entry;
|
||||
pub mod work_editor;
|
||||
pub mod work_editor_composer_row;
|
||||
pub mod work_editor_part_row;
|
||||
pub mod work_selector_popover;
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ mod imp {
|
|||
|
||||
let obj = self.obj().clone();
|
||||
work_selector_popover.connect_create(move |_| {
|
||||
let editor = MusicusWorkEditor::new(&obj.navigation(), &obj.library(), None);
|
||||
let editor = MusicusWorkEditor::new(&obj.navigation(), &obj.library(), None, false);
|
||||
|
||||
editor.connect_created(clone!(
|
||||
#[weak]
|
||||
|
|
@ -124,7 +124,7 @@ mod imp {
|
|||
|
||||
let obj = self.obj().clone();
|
||||
persons_popover.connect_person_selected(move |_, person| {
|
||||
obj.add_performer(person);
|
||||
obj.new_performer(person);
|
||||
});
|
||||
|
||||
let obj = self.obj().clone();
|
||||
|
|
@ -135,7 +135,7 @@ mod imp {
|
|||
#[weak]
|
||||
obj,
|
||||
move |_, person| {
|
||||
obj.add_performer(person);
|
||||
obj.new_performer(person);
|
||||
}
|
||||
));
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ mod imp {
|
|||
|
||||
let obj = self.obj().clone();
|
||||
ensembles_popover.connect_ensemble_selected(move |_, ensemble| {
|
||||
obj.add_ensemble(ensemble);
|
||||
obj.new_ensemble_performer(ensemble);
|
||||
});
|
||||
|
||||
let obj = self.obj().clone();
|
||||
|
|
@ -161,7 +161,7 @@ mod imp {
|
|||
#[weak]
|
||||
obj,
|
||||
move |_, ensemble| {
|
||||
obj.add_ensemble(ensemble);
|
||||
obj.new_ensemble_performer(ensemble);
|
||||
}
|
||||
));
|
||||
|
||||
|
|
@ -200,7 +200,20 @@ impl MusicusRecordingEditor {
|
|||
.recording_id
|
||||
.set(recording.recording_id.clone())
|
||||
.unwrap();
|
||||
// TODO: Initialize data.
|
||||
|
||||
obj.set_work(recording.work.clone());
|
||||
|
||||
if let Some(year) = recording.year {
|
||||
obj.imp().year_row.set_value(year as f64);
|
||||
}
|
||||
|
||||
for performer in recording.persons.clone() {
|
||||
obj.add_performer_row(performer);
|
||||
}
|
||||
|
||||
for ensemble_performer in recording.ensembles.clone() {
|
||||
obj.add_ensemble_row(ensemble_performer);
|
||||
}
|
||||
}
|
||||
|
||||
obj
|
||||
|
|
@ -227,14 +240,17 @@ impl MusicusRecordingEditor {
|
|||
self.imp().work.replace(Some(work));
|
||||
}
|
||||
|
||||
fn add_performer(&self, person: Person) {
|
||||
let role = self.library().performer_default_role().unwrap();
|
||||
fn new_performer(&self, person: Person) {
|
||||
let performer = Performer {
|
||||
person,
|
||||
role,
|
||||
role: self.library().performer_default_role().unwrap(),
|
||||
instrument: None,
|
||||
};
|
||||
|
||||
self.add_performer_row(performer);
|
||||
}
|
||||
|
||||
fn add_performer_row(&self, performer: Performer) {
|
||||
let row =
|
||||
MusicusRecordingEditorPerformerRow::new(&self.navigation(), &self.library(), performer);
|
||||
|
||||
|
|
@ -254,12 +270,21 @@ impl MusicusRecordingEditor {
|
|||
self.imp().performer_rows.borrow_mut().push(row);
|
||||
}
|
||||
|
||||
fn add_ensemble(&self, ensemble: Ensemble) {
|
||||
let role = self.library().performer_default_role().unwrap();
|
||||
let performer = EnsemblePerformer { ensemble, role };
|
||||
fn new_ensemble_performer(&self, ensemble: Ensemble) {
|
||||
let performer = EnsemblePerformer {
|
||||
ensemble,
|
||||
role: self.library().performer_default_role().unwrap(),
|
||||
};
|
||||
|
||||
let row =
|
||||
MusicusRecordingEditorEnsembleRow::new(&self.navigation(), &self.library(), performer);
|
||||
self.add_ensemble_row(performer);
|
||||
}
|
||||
|
||||
fn add_ensemble_row(&self, ensemble_performer: EnsemblePerformer) {
|
||||
let row = MusicusRecordingEditorEnsembleRow::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
ensemble_performer,
|
||||
);
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,18 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
use crate::{
|
||||
db::{
|
||||
self,
|
||||
models::{Composer, Instrument, Person, Work},
|
||||
},
|
||||
editor::{
|
||||
instrument_editor::MusicusInstrumentEditor,
|
||||
instrument_selector_popover::MusicusInstrumentSelectorPopover,
|
||||
person_editor::MusicusPersonEditor, person_selector_popover::MusicusPersonSelectorPopover,
|
||||
translation_editor::MusicusTranslationEditor,
|
||||
work_editor_composer_row::MusicusWorkEditorComposerRow,
|
||||
work_editor_part_row::MusicusWorkEditorPartRow,
|
||||
},
|
||||
library::MusicusLibrary,
|
||||
};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
|
|
@ -8,20 +22,7 @@ use gtk::glib::{
|
|||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
db::{
|
||||
self,
|
||||
models::{Composer, Instrument, Person, Work, WorkPart},
|
||||
},
|
||||
editor::{
|
||||
instrument_editor::MusicusInstrumentEditor,
|
||||
instrument_selector_popover::MusicusInstrumentSelectorPopover,
|
||||
person_editor::MusicusPersonEditor, person_selector_popover::MusicusPersonSelectorPopover,
|
||||
translation_editor::MusicusTranslationEditor,
|
||||
work_editor_composer_row::MusicusWorkEditorComposerRow,
|
||||
},
|
||||
library::MusicusLibrary,
|
||||
};
|
||||
use std::cell::{Cell, OnceCell, RefCell};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
@ -37,13 +38,14 @@ mod imp {
|
|||
pub library: OnceCell<MusicusLibrary>,
|
||||
|
||||
pub work_id: OnceCell<String>,
|
||||
pub is_part_editor: Cell<bool>,
|
||||
|
||||
// Holding a reference to each composer row is the simplest way to enumerate all
|
||||
// results when finishing the process of editing the work. The composer rows
|
||||
// handle all state related to the composer.
|
||||
pub composer_rows: RefCell<Vec<MusicusWorkEditorComposerRow>>,
|
||||
// TODO: These need to be PartRows!
|
||||
pub parts: RefCell<Vec<WorkPart>>,
|
||||
pub part_rows: RefCell<Vec<MusicusWorkEditorPartRow>>,
|
||||
|
||||
pub instruments: RefCell<Vec<Instrument>>,
|
||||
|
||||
pub persons_popover: OnceCell<MusicusPersonSelectorPopover>,
|
||||
|
|
@ -165,16 +167,36 @@ impl MusicusWorkEditor {
|
|||
navigation: &adw::NavigationView,
|
||||
library: &MusicusLibrary,
|
||||
work: Option<&Work>,
|
||||
is_part_editor: bool,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.build();
|
||||
|
||||
if is_part_editor {
|
||||
obj.set_title(&gettext("Work part"));
|
||||
obj.imp().save_button.set_label(&gettext("Add work part"));
|
||||
obj.imp().is_part_editor.set(true);
|
||||
}
|
||||
|
||||
if let Some(work) = work {
|
||||
obj.imp().save_button.set_label(&gettext("Save changes"));
|
||||
obj.imp().work_id.set(work.work_id.clone()).unwrap();
|
||||
// TODO: Initialize work data.
|
||||
|
||||
obj.imp().name_editor.set_translation(&work.name);
|
||||
|
||||
for part in &work.parts {
|
||||
obj.add_part_row(part.clone());
|
||||
}
|
||||
|
||||
for composer in &work.persons {
|
||||
obj.add_composer_row(composer.clone());
|
||||
}
|
||||
|
||||
for instrument in &work.instruments {
|
||||
obj.add_instrument_row(instrument.clone());
|
||||
}
|
||||
}
|
||||
|
||||
obj
|
||||
|
|
@ -196,41 +218,17 @@ impl MusicusWorkEditor {
|
|||
|
||||
#[template_callback]
|
||||
fn add_part(&self, _: &adw::ActionRow) {
|
||||
let part = WorkPart {
|
||||
work_id: db::generate_id(),
|
||||
..Default::default()
|
||||
};
|
||||
let editor = MusicusWorkEditor::new(&self.navigation(), &self.library(), None, true);
|
||||
|
||||
let row = adw::EntryRow::builder().title(gettext("Name")).build();
|
||||
|
||||
let remove_button = gtk::Button::builder()
|
||||
.icon_name("user-trash-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.css_classes(["flat"])
|
||||
.build();
|
||||
|
||||
remove_button.connect_clicked(clone!(
|
||||
editor.connect_created(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
#[weak]
|
||||
row,
|
||||
#[strong]
|
||||
part,
|
||||
move |_| {
|
||||
this.imp().part_list.remove(&row);
|
||||
this.imp().parts.borrow_mut().retain(|p| *p != part);
|
||||
move |_, part| {
|
||||
this.add_part_row(part);
|
||||
}
|
||||
));
|
||||
|
||||
row.add_suffix(&remove_button);
|
||||
|
||||
self.imp()
|
||||
.part_list
|
||||
.insert(&row, self.imp().parts.borrow().len() as i32);
|
||||
|
||||
row.grab_focus();
|
||||
|
||||
self.imp().parts.borrow_mut().push(part);
|
||||
self.navigation().push(&editor);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
|
|
@ -241,6 +239,29 @@ impl MusicusWorkEditor {
|
|||
fn add_composer(&self, person: Person) {
|
||||
let role = self.library().composer_default_role().unwrap();
|
||||
let composer = Composer { person, role };
|
||||
self.add_composer_row(composer);
|
||||
}
|
||||
|
||||
fn add_part_row(&self, part: Work) {
|
||||
let row = MusicusWorkEditorPartRow::new(&self.navigation(), &self.library(), part);
|
||||
|
||||
row.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |row| {
|
||||
this.imp().part_list.remove(row);
|
||||
this.imp().part_rows.borrow_mut().retain(|p| p != row);
|
||||
}
|
||||
));
|
||||
|
||||
self.imp()
|
||||
.part_list
|
||||
.insert(&row, self.imp().part_rows.borrow().len() as i32);
|
||||
|
||||
self.imp().part_rows.borrow_mut().push(row);
|
||||
}
|
||||
|
||||
fn add_composer_row(&self, composer: Composer) {
|
||||
let row = MusicusWorkEditorComposerRow::new(&self.navigation(), &self.library(), composer);
|
||||
|
||||
row.connect_remove(clone!(
|
||||
|
|
@ -300,7 +321,15 @@ impl MusicusWorkEditor {
|
|||
let library = self.imp().library.get().unwrap();
|
||||
|
||||
let name = self.imp().name_editor.translation();
|
||||
let parts = self.imp().parts.borrow().clone();
|
||||
|
||||
let parts = self
|
||||
.imp()
|
||||
.part_rows
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|p| p.part())
|
||||
.collect::<Vec<Work>>();
|
||||
|
||||
let composers = self
|
||||
.imp()
|
||||
.composer_rows
|
||||
|
|
@ -310,15 +339,34 @@ impl MusicusWorkEditor {
|
|||
.collect::<Vec<Composer>>();
|
||||
let instruments = self.imp().instruments.borrow().clone();
|
||||
|
||||
if let Some(work_id) = self.imp().work_id.get() {
|
||||
library
|
||||
.update_work(work_id, name, parts, composers, instruments)
|
||||
.unwrap();
|
||||
if self.imp().is_part_editor.get() {
|
||||
let work_id = self
|
||||
.imp()
|
||||
.work_id
|
||||
.get()
|
||||
.map(|w| w.to_string())
|
||||
.unwrap_or_else(db::generate_id);
|
||||
|
||||
let part = Work {
|
||||
work_id,
|
||||
name,
|
||||
parts,
|
||||
persons: composers,
|
||||
instruments,
|
||||
};
|
||||
|
||||
self.emit_by_name::<()>("created", &[&part]);
|
||||
} else {
|
||||
let work = library
|
||||
.create_work(name, parts, composers, instruments)
|
||||
.unwrap();
|
||||
self.emit_by_name::<()>("created", &[&work]);
|
||||
if let Some(work_id) = self.imp().work_id.get() {
|
||||
library
|
||||
.update_work(work_id, name, parts, composers, instruments)
|
||||
.unwrap();
|
||||
} else {
|
||||
let work = library
|
||||
.create_work(name, parts, composers, instruments)
|
||||
.unwrap();
|
||||
self.emit_by_name::<()>("created", &[&work]);
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().navigation.get().unwrap().pop();
|
||||
|
|
|
|||
129
src/editor/work_editor_part_row.rs
Normal file
129
src/editor/work_editor_part_row.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
use crate::{db::models::Work, editor::work_editor::MusicusWorkEditor, library::MusicusLibrary};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone, subclass::Signal, Properties};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
mod imp {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
#[properties(wrapper_type = super::MusicusWorkEditorPartRow)]
|
||||
#[template(file = "data/ui/work_editor_part_row.blp")]
|
||||
pub struct MusicusWorkEditorPartRow {
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub library: OnceCell<MusicusLibrary>,
|
||||
|
||||
pub part: RefCell<Option<Work>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for MusicusWorkEditorPartRow {
|
||||
const NAME: &'static str = "MusicusWorkEditorPartRow";
|
||||
type Type = super::MusicusWorkEditorPartRow;
|
||||
type ParentType = adw::ActionRow;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for MusicusWorkEditorPartRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> =
|
||||
Lazy::new(|| vec![Signal::builder("remove").build()]);
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for MusicusWorkEditorPartRow {}
|
||||
impl ListBoxRowImpl for MusicusWorkEditorPartRow {}
|
||||
impl PreferencesRowImpl for MusicusWorkEditorPartRow {}
|
||||
impl ActionRowImpl for MusicusWorkEditorPartRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct MusicusWorkEditorPartRow(ObjectSubclass<imp::MusicusWorkEditorPartRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl MusicusWorkEditorPartRow {
|
||||
pub fn new(navigation: &adw::NavigationView, library: &MusicusLibrary, part: Work) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.build();
|
||||
obj.set_part(part);
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn connect_remove<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("remove", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn part(&self) -> Work {
|
||||
self.imp().part.borrow().to_owned().unwrap()
|
||||
}
|
||||
|
||||
fn set_part(&self, part: Work) {
|
||||
self.set_title(&part.name.get());
|
||||
|
||||
if !part.parts.is_empty() {
|
||||
self.set_subtitle(
|
||||
&part
|
||||
.parts
|
||||
.iter()
|
||||
.map(|p| p.name.get())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\n"),
|
||||
);
|
||||
} else {
|
||||
self.set_subtitle("");
|
||||
}
|
||||
|
||||
self.imp().part.replace(Some(part));
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn edit(&self) {
|
||||
let editor = MusicusWorkEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
self.imp().part.borrow().as_ref(),
|
||||
true,
|
||||
);
|
||||
|
||||
editor.connect_created(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |_, part| {
|
||||
this.set_part(part);
|
||||
}
|
||||
));
|
||||
|
||||
self.navigation().push(&editor);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn remove(&self, _: >k::Button) {
|
||||
self.emit_by_name::<()>("remove", &[]);
|
||||
}
|
||||
}
|
||||
|
|
@ -173,6 +173,7 @@ impl MusicusHomePage {
|
|||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(work),
|
||||
false,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
207
src/library.rs
207
src/library.rs
|
|
@ -848,20 +848,33 @@ impl MusicusLibrary {
|
|||
pub fn create_work(
|
||||
&self,
|
||||
name: TranslatedString,
|
||||
parts: Vec<WorkPart>,
|
||||
parts: Vec<Work>,
|
||||
persons: Vec<Composer>,
|
||||
instruments: Vec<Instrument>,
|
||||
) -> Result<Work> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
self.create_work_priv(connection, name, parts, persons, instruments, None, None)
|
||||
}
|
||||
|
||||
fn create_work_priv(
|
||||
&self,
|
||||
connection: &mut SqliteConnection,
|
||||
name: TranslatedString,
|
||||
parts: Vec<Work>,
|
||||
persons: Vec<Composer>,
|
||||
instruments: Vec<Instrument>,
|
||||
parent_work_id: Option<&str>,
|
||||
sequence_number: Option<i32>,
|
||||
) -> Result<Work> {
|
||||
let work_id = db::generate_id();
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
let work_data = tables::Work {
|
||||
work_id: work_id.clone(),
|
||||
parent_work_id: None,
|
||||
sequence_number: None,
|
||||
parent_work_id: parent_work_id.map(|w| w.to_string()),
|
||||
sequence_number: sequence_number,
|
||||
name,
|
||||
created_at: now,
|
||||
edited_at: now,
|
||||
|
|
@ -874,20 +887,15 @@ impl MusicusLibrary {
|
|||
.execute(connection)?;
|
||||
|
||||
for (index, part) in parts.into_iter().enumerate() {
|
||||
let part_data = tables::Work {
|
||||
work_id: part.work_id,
|
||||
parent_work_id: Some(work_id.clone()),
|
||||
sequence_number: Some(index as i32),
|
||||
name: part.name,
|
||||
created_at: now,
|
||||
edited_at: now,
|
||||
last_used_at: now,
|
||||
last_played_at: None,
|
||||
};
|
||||
|
||||
diesel::insert_into(works::table)
|
||||
.values(&part_data)
|
||||
.execute(connection)?;
|
||||
self.create_work_priv(
|
||||
connection,
|
||||
part.name,
|
||||
part.parts,
|
||||
part.persons,
|
||||
part.instruments,
|
||||
Some(&work_id),
|
||||
Some(index as i32),
|
||||
)?;
|
||||
}
|
||||
|
||||
for (index, composer) in persons.into_iter().enumerate() {
|
||||
|
|
@ -922,20 +930,125 @@ impl MusicusLibrary {
|
|||
|
||||
pub fn update_work(
|
||||
&self,
|
||||
id: &str,
|
||||
work_id: &str,
|
||||
name: TranslatedString,
|
||||
parts: Vec<WorkPart>,
|
||||
parts: Vec<Work>,
|
||||
persons: Vec<Composer>,
|
||||
instruments: Vec<Instrument>,
|
||||
) -> Result<()> {
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
self.update_work_priv(
|
||||
connection,
|
||||
work_id,
|
||||
name,
|
||||
parts,
|
||||
persons,
|
||||
instruments,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn update_work_priv(
|
||||
&self,
|
||||
connection: &mut SqliteConnection,
|
||||
work_id: &str,
|
||||
name: TranslatedString,
|
||||
parts: Vec<Work>,
|
||||
persons: Vec<Composer>,
|
||||
instruments: Vec<Instrument>,
|
||||
parent_work_id: Option<&str>,
|
||||
sequence_number: Option<i32>,
|
||||
) -> Result<()> {
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
// TODO: Update work, check which work parts etc exist, update them,
|
||||
// create new work parts, delete and readd composers and instruments.
|
||||
todo!()
|
||||
diesel::update(works::table)
|
||||
.filter(works::work_id.eq(work_id))
|
||||
.set((
|
||||
works::parent_work_id.eq(parent_work_id),
|
||||
works::sequence_number.eq(sequence_number),
|
||||
works::name.eq(name),
|
||||
works::edited_at.eq(now),
|
||||
works::last_used_at.eq(now),
|
||||
))
|
||||
.execute(connection)?;
|
||||
|
||||
diesel::delete(works::table)
|
||||
.filter(
|
||||
works::parent_work_id
|
||||
.eq(work_id)
|
||||
.and(works::work_id.ne_all(parts.iter().map(|p| p.work_id.clone()))),
|
||||
)
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, part) in parts.into_iter().enumerate() {
|
||||
if works::table
|
||||
.filter(works::work_id.eq(&part.work_id))
|
||||
.first::<tables::Work>(connection)
|
||||
.optional()?
|
||||
.is_some()
|
||||
{
|
||||
self.update_work_priv(
|
||||
connection,
|
||||
&part.work_id,
|
||||
part.name,
|
||||
part.parts,
|
||||
part.persons,
|
||||
part.instruments,
|
||||
Some(work_id),
|
||||
Some(index as i32),
|
||||
)?;
|
||||
} else {
|
||||
// Note: The previously used ID is discarded. This should be OK, because
|
||||
// at this point, the part ID should not have been used anywhere.
|
||||
self.create_work_priv(
|
||||
connection,
|
||||
part.name,
|
||||
part.parts,
|
||||
part.persons,
|
||||
part.instruments,
|
||||
Some(work_id),
|
||||
Some(index as i32),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
diesel::delete(work_persons::table)
|
||||
.filter(work_persons::work_id.eq(work_id))
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, composer) in persons.into_iter().enumerate() {
|
||||
let composer_data = tables::WorkPerson {
|
||||
work_id: work_id.to_string(),
|
||||
person_id: composer.person.person_id,
|
||||
role_id: composer.role.role_id,
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(work_persons::table)
|
||||
.values(composer_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
diesel::delete(work_instruments::table)
|
||||
.filter(work_instruments::work_id.eq(work_id))
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, instrument) in instruments.into_iter().enumerate() {
|
||||
let instrument_data = tables::WorkInstrument {
|
||||
work_id: work_id.to_string(),
|
||||
instrument_id: instrument.instrument_id,
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(work_instruments::table)
|
||||
.values(instrument_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_ensemble(&self, name: TranslatedString) -> Result<Ensemble> {
|
||||
|
|
@ -1045,7 +1158,7 @@ impl MusicusLibrary {
|
|||
|
||||
pub fn update_recording(
|
||||
&self,
|
||||
id: &str,
|
||||
recording_id: &str,
|
||||
work: Work,
|
||||
year: Option<i32>,
|
||||
performers: Vec<Performer>,
|
||||
|
|
@ -1056,8 +1169,52 @@ impl MusicusLibrary {
|
|||
|
||||
let now = Local::now().naive_local();
|
||||
|
||||
// TODO: Update recording.
|
||||
todo!()
|
||||
diesel::update(recordings::table)
|
||||
.filter(recordings::recording_id.eq(recording_id))
|
||||
.set((
|
||||
recordings::work_id.eq(work.work_id),
|
||||
recordings::year.eq(year),
|
||||
recordings::edited_at.eq(now),
|
||||
recordings::last_used_at.eq(now),
|
||||
))
|
||||
.execute(connection)?;
|
||||
|
||||
diesel::delete(recording_persons::table)
|
||||
.filter(recording_persons::recording_id.eq(recording_id))
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, performer) in performers.into_iter().enumerate() {
|
||||
let recording_person_data = tables::RecordingPerson {
|
||||
recording_id: recording_id.to_string(),
|
||||
person_id: performer.person.person_id,
|
||||
role_id: performer.role.role_id,
|
||||
instrument_id: performer.instrument.map(|i| i.instrument_id),
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(recording_persons::table)
|
||||
.values(&recording_person_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
diesel::delete(recording_ensembles::table)
|
||||
.filter(recording_ensembles::recording_id.eq(recording_id))
|
||||
.execute(connection)?;
|
||||
|
||||
for (index, ensemble) in ensembles.into_iter().enumerate() {
|
||||
let recording_ensemble_data = tables::RecordingEnsemble {
|
||||
recording_id: recording_id.to_string(),
|
||||
ensemble_id: ensemble.ensemble.ensemble_id,
|
||||
role_id: ensemble.role.role_id,
|
||||
sequence_number: index as i32,
|
||||
};
|
||||
|
||||
diesel::insert_into(recording_ensembles::table)
|
||||
.values(&recording_ensemble_data)
|
||||
.execute(connection)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue