# For copyright and license terms, see COPYRIGHT.rst (top level of repository)
# Repository: https://github.com/C3S/collecting_society_web
import logging
import deform
from portal_web.models import Tdb, Country
from portal_web.views.forms import FormController
from ...services import _
from ...models import (
TariffCategory,
Artist,
Creation,
CreationDerivative,
CreationTariffCategory,
CreationRight,
Content,
Instrument,
CollectingSociety
)
from .add_creation import (
AddCreationSchema,
validate_content
)
log = logging.getLogger(__name__)
# --- Controller --------------------------------------------------------------
[docs]
class EditCreation(FormController):
"""
form controller for edit creation
"""
[docs]
def controller(self):
self.form = edit_creation_form(self.request)
if self.submitted():
if self.validate():
self.update_creation()
else:
self.edit_creation()
return self.response
# --- Stages --------------------------------------------------------------
# --- Conditions ----------------------------------------------------------
# --- Actions -------------------------------------------------------------
[docs]
def edit_creation(self):
creation = self.context.creation
# set appstruct
self.appstruct = {
'metadata': {
'title': creation.title or '',
'artist': creation.artist.id
},
'areas': {
},
'lyrics': {
'lyrics': creation.lyrics
}
}
# tariff categories
tcats = TariffCategory.search_all()
for tcat in tcats:
for creation_tcat in creation.tariff_categories:
if creation_tcat.category.oid == tcat.oid:
collecting_soc_oid = creation_tcat.collecting_society.oid
self.appstruct['areas'][
'tariff_category_'+tcat.code] = collecting_soc_oid
break
# rightsholders
if creation.rights:
a_rightsholders = []
for right in creation.rights:
subject_mode = "add"
subject_email = ""
artist = right.rightsholder
if Artist.is_foreign_rightsholder(
self.request, right, artist):
subject_mode = "edit"
subject_email = artist.party.email
a_rightsholders.append({
'mode': 'edit',
'subject': [{
'mode': subject_mode,
'oid': right.rightsholder.oid,
'name': right.rightsholder.name,
'code': right.rightsholder.code,
'description':
right.rightsholder.description or
'',
'email': subject_email,
}],
'rights': [{
'mode': 'edit',
'oid': right.oid,
'type_of_right': right.type_of_right,
'contribution': right.contribution,
'instruments':
[str(i.oid) for i in right.instruments],
'collecting_society': None
if not right.collecting_society
else right.collecting_society.oid
}]
})
# look for duplicate rightsholders
a_rightsholders.sort(key=lambda x: x['subject'][0]['code'])
i = 0
while i < len(a_rightsholders):
if (i < len(a_rightsholders)-1 and # entries with same artist?
a_rightsholders[i]['subject'][0]['code'] ==
a_rightsholders[i+1]['subject'][0]['code']):
if (a_rightsholders[i+1]['rights'][0] not in
a_rightsholders[i]['rights']): # same right?
# merge rights of the same artist
a_rightsholders[i]['rights'].append(
a_rightsholders[i+1]['rights'][0])
del a_rightsholders[i+1] # delete duplicate and stay here
else: # to check for another duplicate
i = i + 1 # move forward if another rightsholder is found
# TODO: Maybe check for duplicate instrument records an merge them
# finally add rightholders to appstruct
self.appstruct['rightsholders'] = {
'rightsholders': a_rightsholders
}
# original works, this creation is derived from
if creation.original_relations:
a_derivation = {'adaption': [], 'cover': [], 'remix': []}
for original_relation in creation.original_relations:
original_mode = "add"
if (
Creation.is_foreign_original(
self.request,
creation,
original_relation.original_creation
)
):
original_mode = "edit"
a_derivation[original_relation.allocation_type].append({
'mode': original_mode,
'code': original_relation.original_creation.code,
'oid': original_relation.original_creation.oid,
'titlefield': original_relation.original_creation.title,
'artist':
original_relation.original_creation.artist.name
})
self.appstruct['derivation'] = {
'adaption': a_derivation['adaption'],
'cover': a_derivation['cover'],
'remix': a_derivation['remix']
}
# content files that are assigned to the creation
if creation.content:
self.appstruct['content'] = {}
_contentfiles = {}
for contentfile in creation.content:
if contentfile.category not in _contentfiles:
_contentfiles[contentfile.category] = []
_contentfiles[contentfile.category].append(
{
'mode': 'add',
'oid': contentfile.oid,
'code': contentfile.code,
'name': contentfile.name
}
)
for contenttype in _contentfiles.keys():
self.appstruct['content'][contenttype] = \
_contentfiles[contenttype]
# render form with init data
self.render(self.appstruct)
[docs]
@Tdb.transaction(readonly=False)
def update_creation(self):
a = self.appstruct
web_user = self.request.web_user
party = self.request.party
creation = self.context.creation
# (working) title
if a['metadata']['title']:
creation.title = a['metadata']['title']
# artist
if a['metadata']['artist']:
creation.artist = a['metadata']['artist']
# lyrics
if a['lyrics']['lyrics']:
creation.lyrics = a['lyrics']['lyrics']
# rightsholders
rightsholders_current = {
cr.oid: cr for cr in CreationRight.search(
['rightsobject.id', '=', creation.id])}
rightsholders_delete = set(rightsholders_current.values())
# rightsholders: merge artists
# TODO: merge rightsholders_current with same rightsholder subject
# within appstruct
# rightsholders: merge instruments
# TODO: merge instruments of rights (contribution == instrument)
# within appstruct; use one right with oid, if present
# rightsholders: security check, pick objects
for a_rightsholder in a['rightsholders']['rightsholders']:
a_subject = a_rightsholder['subject'][0]
for a_right in a_rightsholder['rights']:
# add objects to create
if a_right['mode'] == "create" and a_right['oid'] == "IGNORED":
continue
# remove objects to delete
if a_right['oid'] in rightsholders_current:
rightsholders_delete.remove(
rightsholders_current[a_right['oid']])
continue
# unknown/unallowed oid
log.warning(
"user %s tried to edit an unknown/unallowed right oid "
"%s for creation %s" % (
web_user,
a_right['oid'],
creation['code']))
self.request.session.flash(
_("Creation edit failed: ${crct} (${crco})",
mapping={'crct': creation.title,
'crco': creation.code}),
'main-alert-danger'
)
self.redirect()
return
# prohibit duplicate rights subjects
a_rho_to_remove = []
for a_rightsholder in a['rightsholders']['rightsholders']:
for a_rho_to_match in a['rightsholders']['rightsholders']:
if (a_rightsholder != a_rho_to_match and
a_rightsholder not in a_rho_to_remove and
a_rho_to_match not in a_rho_to_remove):
if (a_rightsholder['subject'][0]['code'] ==
a_rho_to_match['subject'][0]['code']):
if a_rightsholder['mode'] == 'create':
a_rho_to_remove.append(a_rightsholder)
else: # must be the other one that was newly created
a_rho_to_remove.append(a_rho_to_match)
self.request.session.flash(
_("Warning: Only one rightsholder entry allowed "
"for '${name}'. Entry has been removed.",
mapping={
'name': a_rightsholder['subject'][0]['code']
}),
'main-alert-warning'
)
for a_rho in a_rho_to_remove:
del a_rho
# TODO: cover this also in a colander form validator
# rightsholders: create and edit
for a_rightsholder in a['rightsholders']['rightsholders']:
a_subject = a_rightsholder['subject'][0]
rightsholder_subject = None
# create rightsholder subject
if a_subject['mode'] == "create":
# TODO: foreign subjects
# rightsholder_subject = ...
pass
else:
rightsholder_subject = Artist.search_by_code(a_subject['code'])
# edit rightsholder subject
if a_subject['mode'] == "edit":
# TODO: foreign subjects
pass
# check for duplicate rights per rightsholder and merge instruments
# a_rights_to_remove = []
# for a_right in a_rightsholder['rights']:
# for a_match in a_rightsholder['rights']:
# if (a_right != a_match and
# a_right not in a_rho_to_remove and
# a_match not in a_rho_to_remove):
# if (a_right['subject'][0]['code'] ==
# a_match['subject'][0]['code']):
# if a_right['mode'] == 'create':
# a_rho_to_remove.append(a_right)
# else: # must be the other one newly created
# a_rho_to_remove.append(a_match)
# self.request.session.flash(
# _(u"Warning: Only one rightsholder entry "
# "allowed for '${name}'. Entry has been "
# "removed.",
# mapping={
# 'name': a_rightsholder[
# 'subject'][0]['code']
# }),
# 'main-alert-warning'
# )
# for a_rho in a_rho_to_remove:
# del a_rho
for a_right in a_rightsholder['rights']:
# create right
if a_right['mode'] == "create":
instrument_ids = []
if a_right["contribution"] == "instrument":
instruments = Instrument.search_by_oids(
a_right["instruments"])
instrument_ids = [i.id for i in instruments]
new_right = {
'rightsholder': rightsholder_subject.id,
'rightsobject': creation.id,
'type_of_right': a_right['type_of_right'],
'contribution': a_right['contribution'],
'instruments': [('add', instrument_ids)],
'country': Country.search_by_code('DE').id
}
if a_right['collecting_society']:
cs_representing = CollectingSociety.search_by_oid(
a_right['collecting_society'])
new_right['collecting_society'] = cs_representing.id
CreationRight.create([new_right])
continue
# edit right
rightsholder = rightsholders_current[a_right['oid']]
rightsholder.type_of_right = a_right['type_of_right']
rightsholder.rightsholder = rightsholder_subject
rightsholder.rightsobject = creation
rightsholder.contribution = a_right["contribution"]
rightsholder.instruments = []
if rightsholder.contribution == "instrument":
rightsholder.instruments = Instrument.search_by_oids(
a_right["instruments"])
if a_right['collecting_society']:
cs_representing = CollectingSociety.search_by_oid(
a_right['collecting_society'])
rightsholder.collecting_society = cs_representing.id
# rightsholder.country = Country.search_by_code('DE').id
rightsholder.save()
# rightsholders: delete
if rightsholders_delete:
CreationRight.delete(rightsholders_delete)
# look for removed originals
# originals = CreationDerivative.search_originals_of_creation_by_id(
# creation.id)
originals = creation.original_relations
oids_to_preserve = []
for derivation_type in ['adaption', 'cover', 'remix']:
for a_derivation in a['derivation'][derivation_type]:
# sanity: already in list? then must be a dupe: only add once
if a_derivation['oid'] not in oids_to_preserve:
oids_to_preserve.append(a_derivation['oid'])
if originals:
for original in originals: # loop through database
# original from db table no longer in appstruct?
if original.oid not in oids_to_preserve:
CreationDerivative.delete([original]) # remove it from db
# add new derivative-original relations or perform edits there
# relations can be of allocation type 'adaption', 'cover', or 'remix'
# (objects starting with a_ relate to form data provided by appstruct)
for derivation_type in ['adaption', 'cover', 'remix']:
for a_derivation in a['derivation'][derivation_type]:
# sanity checks
if a_derivation['code'] == creation.code: # original of self?
self.request.session.flash(
_("Warning: A Creation cannot be the original of it "
"self. If you do an adaption of a creation, you "
"need to create a new creation in order to be able "
"to refer to it as an original."),
'main-alert-warning'
)
else:
# create foreign creation
if a_derivation['mode'] == 'create':
original = Creation.create_foreign(
party,
a_derivation['artist'],
a_derivation['titlefield']
)
if not original:
continue
else: # add creation
original = Creation.search_by_oid(a_derivation['oid'])
if not original:
# TODO: Userfeedback
continue
a_original = {
'original_creation': original.id,
'derivative_creation': creation.id,
'allocation_type': derivation_type
}
CreationDerivative.create([a_original])
# areas of exploitation / tariff categories / collecting societies
ctcs_to_delete = [] # collect necessary operations in list
ctcs_to_add = [] # to minimize number of tryton calls
tcats = TariffCategory.search_all()
for tcat in tcats: # for each tariff category
collecting_soc_oid_new = a['areas']['tariff_category_'+tcat.code]
# find changes in previously stored collectingsocieties of creation
ctc_oid_old = None
for creation_tcat in creation.tariff_categories:
if creation_tcat.category.oid == tcat.oid:
ctc_oid_old = creation_tcat.oid
break
# collecting society association deleted for this tariff category?
if not collecting_soc_oid_new and ctc_oid_old:
ctc_to_delete = CreationTariffCategory.search_by_oid(
ctc_oid_old)
if ctc_to_delete:
ctcs_to_delete.append(ctc_to_delete)
# new collecting society association for this tariff category?
if collecting_soc_oid_new and not ctc_oid_old:
collecting_society = CollectingSociety.search_by_oid(
collecting_soc_oid_new)
if collecting_society:
ctcs_to_add.append({
'creation': creation.id,
'category': tcat.id,
'collecting_society': collecting_society.id
})
# collecting society association changed for this tariff category?
if (collecting_soc_oid_new and ctc_oid_old
and creation_tcat.collecting_society.oid !=
collecting_soc_oid_new):
ctc_to_change = CreationTariffCategory.search_by_oid(
ctc_oid_old)
collecting_society = CollectingSociety.search_by_oid(
collecting_soc_oid_new)
if ctc_to_change and collecting_society:
ctc_to_change.collecting_society = collecting_society
ctc_to_change.save()
if ctcs_to_delete:
CreationTariffCategory.delete(ctcs_to_delete)
if ctcs_to_add:
CreationTariffCategory.create(ctcs_to_add)
# content
contents_to_add = []
for contenttype in self.appstruct['content'].keys():
if self.appstruct['content'][contenttype]:
content_new = self.appstruct['content'][contenttype][0]
content_to_add = Content.search_by_oid(content_new['oid'])
if not content_to_add:
continue
if not content_to_add.permits(web_user, 'edit_content'):
continue
contents_to_add.append(content_to_add)
creation.content = contents_to_add
# update creation
creation.save()
# user feedback
log.info("creation edit successful for %s: %s" % (web_user, creation))
self.request.session.flash(
_("Creation edited: ${crct} (${crco})",
mapping={'crct': creation.title,
'crco': creation.code}),
'main-alert-success'
)
# redirect
self.redirect()
# --- Validators --------------------------------------------------------------
# --- Options -----------------------------------------------------------------
# --- Widgets -----------------------------------------------------------------
# --- Fields ------------------------------------------------------------------
# --- Schemas -----------------------------------------------------------------
# --- Forms -------------------------------------------------------------------