Skip to content

Commit

Permalink
Merge pull request #294 from AmpliconSuite/project_alias
Browse files Browse the repository at this point in the history
Feature to access projects via an alias name & redirect
  • Loading branch information
jluebeck authored Sep 18, 2024
2 parents bf9259a + 20dec0c commit c66cba6
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 18 deletions.
8 changes: 6 additions & 2 deletions caper/caper/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from crispy_forms.layout import Submit, Layout, Field


from django.utils.safestring import mark_safe


class RunForm(forms.ModelForm):
accept_license = forms.BooleanField(
label=format_html(
Expand All @@ -22,7 +25,7 @@ class RunForm(forms.ModelForm):

class Meta:
model = Run
fields = ('project_name','description','publication_link','private','project_members', 'accept_license')
fields = ('project_name','description','publication_link','private','project_members', 'accept_license', 'alias')
labels = {
'private': 'Visibility'
}
Expand All @@ -36,6 +39,7 @@ def __init__(self, *args, **kwargs):
self.fields['project_members'].widget.attrs.update({'placeholder': 'Optional: List of additional email addresses or AmpliconRepository usernames separated by spaces or commas'})

class UpdateForm(forms.ModelForm):

accept_license = forms.BooleanField(
label=format_html(
"I acknowledge that the uploaded files will be released under a <a href='https://raw.githubusercontent.com/AmpliconSuite/AmpliconRepository/main/licenses/CCv4-BY.txt'>Creative Commons v4 license</a>."),
Expand All @@ -47,7 +51,7 @@ class UpdateForm(forms.ModelForm):

class Meta:
model = Run
fields = ('project_name', 'description', 'publication_link', 'private', 'project_members', 'accept_license')
fields = ('project_name', 'description', 'publication_link', 'private', 'project_members', 'accept_license', 'alias')
labels = {
'private': 'Visibility'
}
Expand Down
1 change: 1 addition & 0 deletions caper/caper/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Run(models.Model):
BOOL_CHOICES = ((True, 'Private'), (False, 'Public'))
private = models.BooleanField(choices=BOOL_CHOICES,default=True)
project_members = models.CharField(max_length=1000, blank = True)
alias = models.CharField(max_length = 300, blank =True, null = True)


class FeaturedProjectUpdate(models.Model):
Expand Down
23 changes: 23 additions & 0 deletions caper/caper/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,22 @@ def sample_data_from_feature_list(features_list):
# print(f'********** TOOK {datetime.datetime.now() - now}')
return sample_data

def get_all_alias():
"""
Gets all alias names in the db
"""
return collection_handle.distinct('alias_name')



def get_one_project(project_name_or_uuid):
"""
Gets one project from name or UUID.
if name, then checks the DB for an "alias" field, then gets that project if it has one
"""

try:
project = collection_handle.find({'_id': ObjectId(project_name_or_uuid), 'delete': False})[0]
prepare_project_linkid(project)
Expand All @@ -209,6 +223,15 @@ def get_one_project(project_name_or_uuid):

# backstop using the name the old way
if project is None:
## first try finding the alias name
try:
project = collection_handle.find({'alias_name' : project_name_or_uuid, 'delete':False})[0]
prepare_project_linkid(project)
return project
except:
project = None

## then find project via project name
try:
project = collection_handle.find_one({'project_name': project_name_or_uuid, 'delete': False})
if project is not None:
Expand Down
51 changes: 38 additions & 13 deletions caper/caper/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from .forms import RunForm, UpdateForm, FeaturedProjectForm, DeletedProjectForm, SendEmailForm, UserPreferencesForm
from .utils import collection_handle, collection_handle_primary, db_handle, fs_handle, replace_space_to_underscore, \
preprocess_sample_data, get_one_sample, sample_data_from_feature_list, get_one_project, validate_project, \
prepare_project_linkid, replace_underscore_keys, get_projects_close_cursor
prepare_project_linkid, replace_underscore_keys, get_projects_close_cursor, get_all_alias
from django.forms.models import model_to_dict

import subprocess
Expand Down Expand Up @@ -166,6 +166,14 @@ def form_to_dict(form):
# print(form)
run = form.save(commit=False)
form_dict = model_to_dict(run)

if "alias" in form_dict:
try:
form_dict['alias'] = form_dict['alias'].replace(' ', '_')
print(f'alias for this project is: {form_dict["alias"]}')
except:
print('No alias provided, probably Null')
print(type(form_dict['alias']))
return form_dict


Expand Down Expand Up @@ -1260,7 +1268,7 @@ def edit_project_page(request, project_name):

form = UpdateForm(request.POST, request.FILES)
form_dict = form_to_dict(form)

form_dict['project_members'] = create_user_list(form_dict['project_members'], get_current_user(request))

# lets notify users (if their preferences request it) if project membership has changed
Expand Down Expand Up @@ -1298,10 +1306,11 @@ def edit_project_page(request, project_name):
return redirect('project_page', project_name=new_id.inserted_id)
else:
alert_message = "The input file was not a valid aggregation. Please see site documentation."
return render(request, 'pages/edit_project.html', {'project': project, 'run': form, 'alert_message': alert_message})



return render(request, 'pages/edit_project.html',
{'project': project,
'run': form,
'alert_message': alert_message,
'all_alias' :get_all_alias()})
# JTL 081823 Not sure what these next 4 lines are about? An earlier plan to change the project file?
# leaving them alone for now but they smell like dead code
if 'file' in form_dict:
Expand All @@ -1311,19 +1320,27 @@ def edit_project_page(request, project_name):

if check_project_exists(project_name):
new_project_name = form_dict['project_name']

logging.info(f"project name: {project_name} change to {new_project_name}")
current_runs = project['runs']
if runs != 0:
current_runs.update(runs)
query = {'_id': ObjectId(project_name)}
proj = {'project_name':new_project_name, 'runs' : current_runs, 'description': form_dict['description'], 'date': get_date(),
'private': form_dict['private'], 'project_members': form_dict['project_members'], 'publication_link': form_dict['publication_link'],
'Oncogenes': get_project_oncogenes(current_runs)}
proj = {'project_name':new_project_name,
'runs' : current_runs,
'description': form_dict['description'],
'date': get_date(),
'private': form_dict['private'],
'project_members': form_dict['project_members'],
'publication_link': form_dict['publication_link'],
'Oncogenes': get_project_oncogenes(current_runs),
'alias_name':form_dict['alias'],}
new_val = { "$set": proj }

proj['_id'] = query['_id']
edit_proj_privacy(proj, old_privacy, new_privacy)
if form.is_valid():
print('im here')
collection_handle.update_one(query, new_val)
return redirect('project_page', project_name=project_name)
else:
Expand Down Expand Up @@ -1352,8 +1369,11 @@ def edit_project_page(request, project_name):




return render(request, "pages/edit_project.html", {'project': project, 'run': form})
print(project['alias_name'])
return render(request, "pages/edit_project.html",
{'project': project,
'run': form,
'all_alias' :json.dumps(get_all_alias())})

def create_user_list(string, current_user):
# user_list = str.split(',')
Expand Down Expand Up @@ -1852,11 +1872,15 @@ def create_project(request):
return redirect('project_page', project_name=new_id.inserted_id)
else:
alert_message = "The input file was not a valid aggregation. Please see site documentation."
return render(request, 'pages/create_project.html', {'run': form, 'alert_message': alert_message})
return render(request, 'pages/create_project.html',
{'run': form,
'alert_message': alert_message,
'all_alias' : json.dumps(get_all_alias())})

else:
form = RunForm()
return render(request, 'pages/create_project.html', {'run' : form})
return render(request, 'pages/create_project.html', {'run' : form,
'all_alias' : json.dumps(get_all_alias())})


def _create_project(form, request, previous_versions = [], previous_views = [0, 0]):
Expand Down Expand Up @@ -2005,6 +2029,7 @@ def create_project_helper(form, user, request_file, save = True, tmp_id = uuid.u
project['FINISHED?'] = False
project['views'] = previous_views[0]
project['downloads'] = previous_views[1]
project['alias_name'] = form_dict['alias']
return project, tmp_id

class FileUploadView(APIView):
Expand Down
42 changes: 41 additions & 1 deletion caper/templates/pages/create_project.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,24 @@ <h1>Create New Project</h1>
<div id="alert-box"></div>
<form method="post" enctype="multipart/form-data" id="upload-form">
{% csrf_token %}
{{ run|crispy }}
{% for field in run %}
{% if field.name == 'alias' %}
<p>Enter an unique alias for this project. It can be used to access this project.</p> <p></p>
<div style='display:flex; gap:5px; align-items:center;'>
<span> ampliconrepository.org/project/</span>
<div style = 'width:200px; padding:2px; font-size:15px border:1px;'>{{ field }}</div>
<span>/</span>
</div>
<div id='result'></div>
<br>
<br>

{% else %}
{{field | as_crispy_field }}

{% endif %}
{% endfor %}


<input type="file" name="document" id="fileuploadfield">
<input type="button" onClick="clearFileInput();" value="Remove Upload" id="remove-upload-btn">
Expand Down Expand Up @@ -164,6 +181,29 @@ <h1>Create New Project</h1>
}
}

document.addEventListener('DOMContentLoaded', function() {
// Pass the alias names from Django context to JavaScript
var aliasNames = JSON.parse("{{ all_alias | escapejs}}");
console.log(aliasNames);

var inputField = document.getElementById('id_alias');
var result = document.getElementById('result');

inputField.addEventListener('input', function() {
var userInput = inputField.value;

if (aliasNames.includes(userInput) ) {
result.textContent = 'Alias already taken ...';
result.style.color = 'red';
} else {
result.textContent = 'This alias works!';
result.style.color = 'green';
}
});
});



</script>

{% endblock %}
48 changes: 47 additions & 1 deletion caper/templates/pages/edit_project.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,33 @@ <h1>Editing project: '{{ project.project_name }}'</h1>
<div id="alert-box"></div>
<form method="post" enctype="multipart/form-data" id="upload-form">
{% csrf_token %}
{{ run|crispy }}
{% for field in run %}

{% if field.name == 'alias' %}
<p>Enter an unique alias for this project. It can be used to access this project.</p>

{% if project.alias_name != None %}
<p> Current alias name is: <b>{{ project.alias_name }}</b>. The project can be accessed at <a href="">ampliconrepository.org/project/{{project.alias_name}}</a></p>
{% endif %}


<div style='display:flex; gap:5px; align-items:center;'>
<span> ampliconrepository.org/project/</span>
<div style = 'width:200px; padding:2px; font-size:15px border:1px;'>{{ field }}</div>
<span>/</span>
</div>
<div id='result'></div>
<br>
{% else %}
{{field | as_crispy_field }}

{% endif %}
{% endfor %}

<label for="document">
Replace AmpliconAggregator output file with: &nbsp;&nbsp;
</label>

<input type="file" name="document" id="fileuploadfield">
<input type="button" onClick="clearFileInput();" value="Remove Upload" id="remove-upload-btn">
<br> <br>
Expand Down Expand Up @@ -147,6 +170,29 @@ <h1>Please sign in to edit projects</h1>
}
}

document.addEventListener('DOMContentLoaded', function() {
// Pass the alias names from Django context to JavaScript
var aliasNames = JSON.parse("{{ all_alias | escapejs}}");
console.log(aliasNames);

var inputField = document.getElementById('id_alias');
var result = document.getElementById('result');

inputField.addEventListener('input', function() {
var userInput = inputField.value;

if (aliasNames.includes(userInput) ) {
result.textContent = 'Alias already taken ...';
result.style.color = 'red';
} else {
result.textContent = 'This alias works!';
result.style.color = 'green';
}
});
});



</script>

{% endblock %}
44 changes: 43 additions & 1 deletion caper/templates/pages/project.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% load static %}

{% block extra_js %}

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script>
$(document).ready( function () {
$('#myTable1').DataTable({
Expand Down Expand Up @@ -98,6 +98,41 @@
}


function copyToClipboard() {
// Get the anchor element with the URL
var copyText = document.getElementById("aliaslink");

// Create a range and select the text
var range = document.createRange();
range.selectNode(copyText);

// Get the selection object and add the range to it
var selection = window.getSelection();
selection.removeAllRanges(); // Clear any existing selections
selection.addRange(range);

// Copy the selected text to the clipboard
try {
var successful = document.execCommand('copy');
var message = successful ? 'Copied to clipboard!' : 'Unable to copy.';
document.getElementById("message").innerHTML = message;
// Remove the message after 5 seconds (5000 milliseconds)
setTimeout(function() {
document.getElementById("message").innerHTML = '';
}, 3000);


} catch (err) {
console.error('Failed to copy:', err);
}

// Clear the selection for better user experience
selection.removeAllRanges();
}




</script>

{% endblock %}
Expand Down Expand Up @@ -129,6 +164,13 @@ <h2>Project: <span style="font-weight: 200">{{ project.project_name }}</span>
<h5>Description: <span style="font-weight: 200">{{ project.description }}</span></h5>
<h5>Publication link: <span style="font-weight: 200">{{ project.publication_link|escape|replace_urls|safe }}</span></h5>
<h5>Reference build: <span style="font-weight: 200">{{ reference_genome }}</span></h5>

{% if project.alias_name %}
<h5>Project Link: <a id='aliaslink' href="">ampliconrepository.org/project/{{project.alias_name}} </a><i class="fas fa-link chain-icon" style = 'cursor:pointer;'onclick="copyToClipboard()"></i></h5>
<p id="message"></p>
{% endif %}


</div>

<div class="col-md-3">
Expand Down

0 comments on commit c66cba6

Please sign in to comment.