Implement functionality to assigne Licenses to Packages and show existing Licenses (#17)

Co-authored-by: Tim Lorsbach <tim@lorsba.ch>
Reviewed-on: enviPath/enviPy#17
This commit is contained in:
2025-07-03 06:28:49 +12:00
parent 9950112311
commit 4e58a1fad7
5 changed files with 231 additions and 10 deletions

View File

@ -204,9 +204,14 @@ class ScenarioMixin(models.Model):
class Meta: class Meta:
abstract = True abstract = True
class License(models.Model):
link = models.URLField(blank=False, null=False, verbose_name='link')
image_link = models.URLField(blank=False, null=False, verbose_name='Image link')
class Package(EnviPathModel): class Package(EnviPathModel):
reviewed = models.BooleanField(verbose_name='Reviewstatus', default=False) reviewed = models.BooleanField(verbose_name='Reviewstatus', default=False)
license = models.ForeignKey('epdb.License', on_delete=models.SET_NULL, blank=True, null=True, verbose_name='License')
def __str__(self): def __str__(self):
return f"{self.name} (pk={self.pk})" return f"{self.name} (pk={self.pk})"

View File

@ -1,5 +1,6 @@
import json import json
import logging import logging
from typing import List, Dict, Any
from django.conf import settings as s from django.conf import settings as s
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -11,7 +12,7 @@ from utilities.chem import FormatConverter, IndigoUtils
from .logic import GroupManager, PackageManager, UserManager, SettingManager from .logic import GroupManager, PackageManager, UserManager, SettingManager
from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \ from .models import Package, GroupPackagePermission, Group, CompoundStructure, Compound, Reaction, Rule, Pathway, Node, \
EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \ EPModel, EnviFormer, MLRelativeReasoning, RuleBaseRelativeReasoning, Scenario, SimpleAmbitRule, APIToken, \
UserPackagePermission, Permission UserPackagePermission, Permission, License, User
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -20,7 +21,7 @@ def log_post_params(request):
logger.debug(f"{k}\t{v}") logger.debug(f"{k}\t{v}")
def get_base_context(request): def get_base_context(request) -> Dict[str, Any]:
current_user = _anonymous_or_real(request) current_user = _anonymous_or_real(request)
ctx = { ctx = {
@ -47,7 +48,7 @@ def _anonymous_or_real(request):
return get_user_model().objects.get(username='anonymous') return get_user_model().objects.get(username='anonymous')
def breadcrumbs(first_level_object=None, second_level_namespace=None, second_level_object=None): def breadcrumbs(first_level_object=None, second_level_namespace=None, second_level_object=None) -> List[Dict[str, str]]:
bread = [ bread = [
{'Home': s.SERVER_URL}, {'Home': s.SERVER_URL},
{'Package': s.SERVER_URL + '/package'}, {'Package': s.SERVER_URL + '/package'},
@ -130,6 +131,8 @@ def compounds(request):
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_compound_qs |= Compound.objects.filter(package=p) reviewed_compound_qs |= Compound.objects.filter(package=p)
reviewed_compound_qs = reviewed_compound_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
"objects": [ "objects": [
@ -138,7 +141,7 @@ def compounds(request):
] ]
}) })
context['reviewed_objects'] = reviewed_compound_qs.order_by('name') context['reviewed_objects'] = reviewed_compound_qs
return render(request, 'collections/objects_list.html', context) return render(request, 'collections/objects_list.html', context)
elif request.method == 'POST': elif request.method == 'POST':
@ -162,7 +165,9 @@ def rules(request):
reviewed_rule_qs = Rule.objects.none() reviewed_rule_qs = Rule.objects.none()
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_rule_qs |= Rule.objects.filter(package=p).order_by('name') reviewed_rule_qs |= Rule.objects.filter(package=p)
reviewed_rule_qs = reviewed_rule_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
@ -198,6 +203,8 @@ def reactions(request):
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_reaction_qs |= Reaction.objects.filter(package=p).order_by('name') reviewed_reaction_qs |= Reaction.objects.filter(package=p).order_by('name')
reviewed_reaction_qs = reviewed_reaction_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
"objects": [ "objects": [
@ -233,6 +240,8 @@ def pathways(request):
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_pathway_qs |= Pathway.objects.filter(package=p).order_by('name') reviewed_pathway_qs |= Pathway.objects.filter(package=p).order_by('name')
reviewed_pathway_qs = reviewed_pathway_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
"objects": [ "objects": [
@ -268,6 +277,8 @@ def scenarios(request):
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_scenario_qs |= Scenario.objects.filter(package=p).order_by('name') reviewed_scenario_qs |= Scenario.objects.filter(package=p).order_by('name')
reviewed_scenario_qs = reviewed_scenario_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
"objects": [ "objects": [
@ -311,6 +322,8 @@ def models(request):
for p in PackageManager.get_reviewed_packages(): for p in PackageManager.get_reviewed_packages():
reviewed_model_qs |= EPModel.objects.filter(package=p).order_by('name') reviewed_model_qs |= EPModel.objects.filter(package=p).order_by('name')
reviewed_model_qs = reviewed_model_qs.order_by('name')
if request.GET.get('all'): if request.GET.get('all'):
return JsonResponse({ return JsonResponse({
"objects": [ "objects": [
@ -535,11 +548,11 @@ def package(request, package_uuid):
if s.DEBUG: if s.DEBUG:
for k, v in request.POST.items(): for k, v in request.POST.items():
print(k, v) logger.debug(f"{k}\t{v}")
if hidden := request.POST.get('hidden', None): if hidden := request.POST.get('hidden', None):
if hidden == 'delete-package': if hidden == 'delete-package':
print(current_package.delete()) logger.debug(current_package.delete())
return redirect(s.SERVER_URL + '/package') return redirect(s.SERVER_URL + '/package')
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
@ -552,6 +565,9 @@ def package(request, package_uuid):
write = request.POST.get('write') == 'on' write = request.POST.get('write') == 'on'
owner = request.POST.get('owner') == 'on' owner = request.POST.get('owner') == 'on'
license = request.POST.get('license')
license_link = request.POST.get('license-link')
license_image_link = request.POST.get('license-image-link')
if new_package_name: if new_package_name:
current_package.name = new_package_name current_package.name = new_package_name
@ -579,9 +595,32 @@ def package(request, package_uuid):
PackageManager.update_permissions(current_user, current_package, grantee, max_perm) PackageManager.update_permissions(current_user, current_package, grantee, max_perm)
return redirect(current_package.url) return redirect(current_package.url)
elif license is not None:
if license == 'no-license':
if current_package.license is not None:
current_package.license.delete()
current_package.license = None
current_package.save()
return redirect(current_package.url)
else:
if current_package.license is not None:
current_package.license.delete()
l = License()
l.link = license_link
l.image_link = license_image_link
l.save()
current_package.license = l
current_package.save()
return redirect(current_package.url)
else: else:
return HttpResponseBadRequest() return HttpResponseBadRequest()
else:
return HttpResponseBadRequest()
# https://envipath.org/package/<id>/compound # https://envipath.org/package/<id>/compound
def package_compounds(request, package_uuid): def package_compounds(request, package_uuid):
@ -960,9 +999,9 @@ def package_pathways(request, package_uuid):
# https://envipath.org/package/<id>/pathway/<id> # https://envipath.org/package/<id>/pathway/<id>
def package_pathway(request, package_uuid, pathway_uuid): def package_pathway(request, package_uuid, pathway_uuid):
current_user = _anonymous_or_real(request) current_user: User = _anonymous_or_real(request)
current_package = PackageManager.get_package_by_id(current_user, package_uuid) current_package: Package = PackageManager.get_package_by_id(current_user, package_uuid)
current_pathway = Pathway.objects.get(package=current_package, uuid=pathway_uuid) current_pathway: Pathway = Pathway.objects.get(package=current_package, uuid=pathway_uuid)
if request.method == 'GET': if request.method == 'GET':

View File

@ -6,6 +6,10 @@
<a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal"> <a role="button" data-toggle="modal" data-target="#edit_package_permissions_modal">
<i class="glyphicon glyphicon-user"></i> Edit Permissions</a> <i class="glyphicon glyphicon-user"></i> Edit Permissions</a>
</li> </li>
<li>
<a role="button" data-toggle="modal" data-target="#set_license_modal">
<i class="glyphicon glyphicon-duplicate"></i> License</a>
</li>
<li> <li>
<a class="button" data-toggle="modal" data-target="#delete_package_modal"> <a class="button" data-toggle="modal" data-target="#delete_package_modal">
<i class="glyphicon glyphicon-trash"></i> Delete Package</a> <i class="glyphicon glyphicon-trash"></i> Delete Package</a>

View File

@ -0,0 +1,153 @@
<div class="modal fade"
tabindex="-1"
id="set_license_modal"
role="dialog"
aria-labelledby="set_license_modal"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button"
class="close"
data-dismiss="modal">
<span aria-hidden="true">&times;</span>
<span class="sr-only">Close</span>
</button>
<h3 class="modal-title">Set License</h3>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-9">
<p>
Set the license for all data in this package:
<br> <br>
(For more information please visit our <a target="#"
href="https://wiki.envipath.org/index.php/License">wiki</a>.)
</p>
</div>
<div class="col-md-3">
<div id="ccfig"></div>
</div>
</div>
<form id="set_license_form" accept-charset="UTF-8" action="" data-remote="true" method="post">
{% csrf_token %}
<div class="input-group">
<div class="radio">
<label><input type="radio" name="optlicense" onclick="cc()" id="ccradio">Creative commons license</label>
</div>
<div>
<div class="checkbox">
<label><input type="checkbox" value="" onclick="cc()" id="ccremix" disabled>Allow adaptations of your work to be shared</label>
</div>
<div class="checkbox">
<label><input type="checkbox" value="" onclick="cc()" id="ccnocom" disabled>Prohibit commercial use</label>
</div>
<div class="checkbox">
<label><input type="checkbox" value="" onclick="cc()" id="ccalike" disabled>Share only if others share alike</label>
</div>
</div>
<div class="radio">
<label><input type="radio" name="optlicense" onclick="cc()" id="noccradio">No creative commons license, contact package owner for license information</label>
</div>
</div>
<input type="hidden" id="license" name="license">
<input type="hidden" id="license-link" name="license-link">
<input type="hidden" id="license-image-link" name="license-image-link">
</form>
</div>
<div class="modal-footer">
<button id="set_license_form_submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
<script>
function ccstring(ccremix, ccnocom, ccalike){
var ccstring = "by";
if(ccnocom){
ccstring +="-nc";
}
if(!ccremix){
ccstring +="-nd";
} else{
if(ccalike){
ccstring +="-sa";
}
}
return ccstring;
}
function cc() {
var nocc = $('#noccradio').prop('checked')
var iscc = $('#ccradio').prop('checked');
if(nocc) {
$('#ccradio').prop('checked', false);
$('#ccremix').prop('checked', false);
$('#ccnocom').prop('checked', false);
$('#ccalike').prop('checked', false);
$('#ccremix').prop('disabled', true);
$('#ccnocom').prop('disabled', true);
$('#ccalike').prop('disabled', true);
} else if (iscc) {
$('#ccremix').prop('disabled', false);
$('#ccnocom').prop('disabled', false);
if ($('#ccremix').prop('checked')) {
$('#ccalike').prop('disabled', false);
} else {
$('#ccalike').prop('disabled', true);
}
}
var remix = $('#ccremix').prop('checked');
var nocom = $('#ccnocom').prop('checked');
var alike = $('#ccalike').prop('checked');
if(nocc) {
$('#set_license_form_submit').prop('disabled', false);
$('#ccfig').empty();
$('#license').val('no-license')
} else if(iscc) {
$('#set_license_form_submit').prop('disabled', false);
$('#ccfig').empty();
var ccstr = ccstring(remix, nocom, alike)
var link = `https://creativecommons.org/licenses/${ccstr}/4.0/`;
var imageLink = `https://licensebuttons.net/l/${ccstr}/4.0/88x31.png`;
var img_tpl = `<a href='${link}' target="_blank">
<img src='${imageLink}'>
</a>`;
$('#ccfig').append(img_tpl);
$('#license').val(ccstr);
$('#license-link').val(link);
$('#license-image-link').val(imageLink);
} else {
$('#ccfig').empty();
$('#set_license_form_submit').prop('disabled', true);
$('#license').val('no-license')
}
}
$(function() {
// Disable by default as nothing is selected
cc();
$('#set_license_form_submit').prop('disabled', true);
$('#set_license_form_submit').on('click', function (e) {
e.preventDefault();
$('#set_license_form').submit();
});
});
</script>

View File

@ -5,6 +5,7 @@
{% block action_modals %} {% block action_modals %}
{% include "modals/objects/edit_package_modal.html" %} {% include "modals/objects/edit_package_modal.html" %}
{% include "modals/objects/edit_package_permissions_modal.html" %} {% include "modals/objects/edit_package_permissions_modal.html" %}
{% include "modals/objects/set_license_modal.html" %}
{% include "modals/objects/delete_package_modal.html" %} {% include "modals/objects/delete_package_modal.html" %}
{% endblock action_modals %} {% endblock action_modals %}
@ -50,5 +51,24 @@
</ul> </ul>
</div> </div>
{% if package.license %}
<p></p>
<div class="panel-group" id="license_accordion">
<div class="panel panel-default list-group-item" style="background-color:#f5f5f5">
<div class="panel-title">
<a data-toggle="collapse" data-parent="#licence_accordion" href="#license">License</a>
</div>
</div>
<div id="license" class="panel-collapse collapse in">
<div class="panel-body list-group-item">
<a target="_blank" href="{{ package.license.link }}">
<img src="{{ package.license.image_link }}">
</a>
</div>
</div>
</div>
{% endif %}
</div> </div>
{% endblock content %} {% endblock content %}