Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT: Assign updates to existing published frozen archives to integrate errata and related work #116

Draft
wants to merge 1 commit into
base: edge
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion mig/assets/css/V3/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --- BEGIN_HEADER ---
#
# style - Core UI V3 specific but skin-independent styling
# Copyright (C) 2003-2019 The MiG Project lead by Brian Vinter
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
#
# This file is part of MiG.
#
Expand Down Expand Up @@ -808,3 +808,11 @@ var, sampl, code {
.table-responsive .table td, .table-responsive .table th {
padding: 0.5rem 1.2rem;
}

/* Highligt replacement and supplement information for published Archives */
.archive-update-header {
background-color: #ffcc00; /* Yellow for attention */
padding: 10px;
border: 1px solid #ff9900;
margin-bottom: 20px;
}
1 change: 1 addition & 0 deletions mig/shared/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@
public_archive_index = 'published-archive.html'
public_archive_files = 'published-files.json'
public_archive_doi = 'published-doi.json'
public_archive_updates = 'published-updates.json'
public_doi_index = 'archive-doi-index.html'

edit_lock_suffix = '.editor_lock__'
Expand Down
122 changes: 112 additions & 10 deletions mig/shared/freezefunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@
brief_list, pretty_format_user, get_site_base_url
from mig.shared.defaults import freeze_meta_filename, freeze_lock_filename, \
wwwpublic_alias, public_archive_dir, public_archive_index, \
public_archive_files, public_archive_doi, freeze_flavors, keyword_final, \
keyword_pending, keyword_updating, keyword_auto, keyword_any, \
keyword_all, max_freeze_files, archives_cache_filename, \
freeze_on_tape_filename, archive_marks_dir, csrf_field
public_archive_files, public_archive_doi, public_archive_updates, \
freeze_flavors, keyword_final, keyword_pending, keyword_updating, \
keyword_auto, keyword_any, keyword_all, max_freeze_files, \
archives_cache_filename, freeze_on_tape_filename, archive_marks_dir, \
csrf_field
from mig.shared.fileio import checksum_file, write_file, copy_file, copy_rec, \
move_file, move_rec, remove_rec, delete_file, delete_symlink, \
makedirs_rec, make_symlink, make_temp_dir, acquire_file_lock, \
Expand All @@ -67,7 +68,7 @@
('DESCRIPTION', 'Description')]
__meta_archive_internals = [freeze_meta_filename, freeze_lock_filename]
__public_archive_internals = [public_archive_index, public_archive_files,
public_archive_doi]
public_archive_doi, public_archive_updates]


def brief_freeze(freeze_dict):
Expand Down Expand Up @@ -158,10 +159,12 @@ def build_freezeitem_object(configuration, freeze_dict, summary=False,
if freeze_state not in (keyword_updating, keyword_final):
show_finalize = True
register_doi = False
assign_updates = False
if freeze_state == keyword_final and flavor != 'backup' and \
configuration.site_freeze_doi_url and \
freeze_dict.get('PUBLISH_URL', ''):
register_doi = True
assign_updates = True

if summary:
freeze_files = len(freeze_dict.get('FILES', []))
Expand Down Expand Up @@ -291,6 +294,18 @@ def build_freezeitem_object(configuration, freeze_dict, summary=False,
'text': 'Request archive DOI',
}
freeze_obj['registerdoi_link'] = registerdoi_link
if assign_updates:
assignupdates_link = {
'object_type': 'link',
'destination':
"javascript: confirmDialog(%s, '%s');" %
('assignfreezeupdates', 'Really assign archive updates for %s?' % freeze_id),
'class': 'assignarchiveupdateslink iconspace genericbutton',
'title': 'Assign an update for %s archive %s' % (flavor, freeze_id),
'text': 'Assign archive replacement or supplementary archive material',
}
# TODO: implement backend to handle assigment to published-updates.json
freeze_obj['assignupdates_link'] = assignupdates_link

return freeze_obj

Expand All @@ -311,9 +326,9 @@ def parse_time_delta(str_value):
elif unit == 'h':
multiplier = 60
elif unit == 'd':
multiplier = 24*60
multiplier = 24 * 60
elif unit == 'w':
multiplier = 7*24*60
multiplier = 7 * 24 * 60
minutes = multiplier * count
return datetime.timedelta(minutes=minutes)

Expand Down Expand Up @@ -974,6 +989,8 @@ def write_landing_page(freeze_dict, arch_dir, frozen_files, cached,
arch_url = published_url(freeze_dict, configuration)
files_url = published_url(freeze_dict, configuration, public_archive_files)
doi_url = published_url(freeze_dict, configuration, public_archive_doi)
updates_url = published_url(
freeze_dict, configuration, public_archive_updates)
freeze_dict['PUBLISH_URL'] = arch_url
_logger.debug("create landing page for %s on %s" % (freeze_id, arch_url))
publish_preamble = ""
Expand Down Expand Up @@ -1134,11 +1151,93 @@ def write_landing_page(freeze_dict, arch_dir, frozen_files, cached,
}
});
}
""" % (sorted_hash_algos, files_url, doi_url)
function ajax_showupdates() {
var url = lookup_url('%s');
console.debug('loading archive updates data from '+url+' ...');
$('#updatescontents').html('Loading archive updates data ...');
$('#updatescontents').addClass('spinner iconleftpad');
var updates_req = $.ajax({
url: url,
type: 'GET',
dataType: 'json',
success: function(jsonRes, textStatus) {
console.debug('got response from updates lookup: '+textStatus);
console.debug(jsonRes);
var updates_data = '';
var updates_url = jsonRes.id;
/* Users can assign archive updates to a published archive.
If so the result here is a dict with the following contents:
* replaces_id: ID of another archive that this replaces
* replaces_url: URL of another archive that this replaces
* replaced_by_id: ID of another archive replacing this
* replaced_by_url: URL of another archive replacing this

Planned but still pending additional fields for references are
* supplements_id: ID of another archive that this supplements
* supplements_url: URL of another archive that this supplements
* supplemented_by_id: ID of another archive supplementing this
* supplemented_by_url: URL of another archive supplementing this
We could make it supplements lists but it quickly gets hairy.
*/
var updates = jsonRes.updates;
if (updates !== undefined) {
updates_data += '<div class="archive-update-header">';
var replaced_by_id = updates.replaced_by_id;
var replaced_by_url = updates.replaced_by_url;
if (replaced_by_id !== undefined && replaced_by_url !== undefined) {
updates_data += '<h4>Replacement Available!</h4>';
updates_data += '<p class="archive-replaced-info"">';
updates_data += 'The author(s) of this archive has created the new ';
updates_data += 'public <a class=\"iconleftpad replacingarchivelink\" href=\"';
updates_data += replaced_by_url + '\">'+replaced_by_id+'</a> archive ';
updates_data += 'as a replacement for this archive. Please refer to ';
updates_data += 'that linked new archive for additional information ';
updates_data + ='about the update and changes.</p><br/>';
} else {
console.debug('no archive replaced by data');
}
var replaces_id = updates.replaces_id;
var replaces_url = updates.replaces_url;
if (replaces_id !== undefined && replaces_url !== undefined) {
updates_data += '<h4>Replacement Archive</h4>';
updates_data += '<p class="archive-replaced-by-info"">';
updates_data += 'The author(s) of the previously published ';
updates_data += '<a class=\"iconleftpad replacedarchivelink\" href=\"';
updates_data += replaces_url + '\">'+replaces_id+'</a> archive ';
updates_data += 'created this archive to replace it. Please see ';
updates_data += 'that linked old archive for additional details ';
updates_data + ='about the original publication.</p><br/>';
} else {
console.debug('no archive replaces data');
}

/* TODO: Add handling of supplement archives here */

updates_data += '</div>';
$('#updatescontents').html(updates_data);
} else {
updates_data = 'No archive updates data found';
$('#updatescontents').html(updates_data);
}
$('#updatescontents').removeClass('spinner iconleftpad');
$('#updatestoggle').show();
},
error: function(jqXHR, textStatus, errorThrown) {
console.info('No archive updates data found')
console.debug('Archive updates request said: '+ \
textStatus+' : '+errorThrown);
updates_data = 'No archive updates data found';
$('#updatescontents').html(updates_data);
$('#updatescontents').removeClass('spinner iconleftpad');
}
});
}
""" % (sorted_hash_algos, files_url, doi_url, updates_url)
add_ready += """
ajax_showdoi('%s');
ajax_showupdates('%s');
%s;
""" % (freeze_id, refresh_call)
""" % (freeze_id, freeze_id, refresh_call)
# Fake manager themed style setup for tablesorter layout with site style
style_entry = themed_styles(configuration, user_settings={})
base_style = style_entry.get("base", "")
Expand Down Expand Up @@ -1175,6 +1274,9 @@ def write_landing_page(freeze_dict, arch_dir, frozen_files, cached,
contents += """
%s
<div class='archive-header'>
<div class='archive-updatesdata'>
<div id='updatescontents'><!-- filled by AJAX call--></div>
</div>
<p class='archive-autometa archive-box'>%s
</p>
</div>
Expand Down Expand Up @@ -1638,7 +1740,7 @@ def delete_frozen_archive(freeze_dict, client_id, configuration):
_logger.error(web_res)
return (False, web_res)

if not delete_file(arch_dir+CACHE_EXT, _logger, allow_missing=True) \
if not delete_file(arch_dir + CACHE_EXT, _logger, allow_missing=True) \
or not remove_rec(arch_dir, configuration):
_logger.error("could not remove archive dir for %s" %
brief_freeze(freeze_dict))
Expand Down
32 changes: 23 additions & 9 deletions mig/shared/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# --- BEGIN_HEADER ---
#
# output - general formatting of backend output objects
# Copyright (C) 2003-2023 The MiG Project lead by Brian Vinter
# Copyright (C) 2003-2024 The MiG Project lead by Brian Vinter
#
# This file is part of MiG.
#
Expand All @@ -28,6 +28,7 @@
"""Helper functions to generate output in format specified by the client"""

from __future__ import absolute_import
from past.builtins import basestring

import os
import sys
Expand Down Expand Up @@ -363,7 +364,7 @@ def txt_format(configuration, ret_val, ret_msg, out_obj):
header = [['ID', 'Path']]
optional_cols = [('access', 'Access'), ('created', 'Created'),
('active', 'Active'), ('owner', 'Owner'),
('invites', 'Invites'), ('expire', 'Expire'),
('invites', 'Invites'), ('expire', 'Expire'),
('single_file', 'Single file'),
]
content_keys = ['share_id', 'path']
Expand Down Expand Up @@ -1730,8 +1731,9 @@ def html_format(configuration, ret_val, ret_msg, out_obj):
lines.append(""" </div>
</div>""")

show_updating, show_edit, show_register = 3 * ['hidden']
edit_link, finalize_link, register_link = 3 * \
show_updating, show_edit, show_register, show_updates = 4 * \
['hidden']
edit_link, finalize_link, register_link, updates_link = 4 * \
['<!-- filled by AJAX-->']
if i.get('state', '') == keyword_updating:
show_updating = ''
Expand All @@ -1745,6 +1747,9 @@ def html_format(configuration, ret_val, ret_msg, out_obj):
if i.get('registerdoi_link', ''):
register_link = html_link(i['registerdoi_link'])
show_register = ''
if i.get('assignupdates_link', ''):
updates_link = html_link(i['assignupdates_link'])
show_updates = ''

lines.append("""
<div class='updatearchive %s'>
Expand All @@ -1770,10 +1775,19 @@ def html_format(configuration, ret_val, ret_msg, out_obj):
%s
<p id='registerdoibutton'>%s</p>
</div>
<div class='assignupdates %s'>
<p>
You can assign updates to already finalized published archives. This may be
useful in case you fouund errors in the existing archive or want to reference
the already published contents in another archive.
</p>
<p id='assignupdatesbutton'>%s</p>
</div>
<div class='vertical-spacer'></div>
</div>
""" % (show_updating, show_edit, edit_link, finalize_link, show_register,
configuration.site_freeze_doi_text, register_link))
configuration.site_freeze_doi_text, register_link,
show_updates, updates_link))

elif i['object_type'] == 'freezestatus':
# We only use this element for scripted archive creation
Expand Down Expand Up @@ -1894,7 +1908,7 @@ def html_format(configuration, ret_val, ret_msg, out_obj):
skip_list = i.get('skip_list', [])
optional_cols = [('access', 'Access'), ('created', 'Created'),
('active', 'Active'), ('owner', 'Owner'),
('invites', 'Invites'), ('expire', 'Expire'),
('invites', 'Invites'), ('expire', 'Expire'),
('single_file', 'Single file'),
]
# IMPORTANT: AdBlock Plus hides elements with class sharelink(s)
Expand Down Expand Up @@ -2849,10 +2863,10 @@ def format_output(
def format_timedelta(timedelta):
"""Formats timedelta as '[Years,] [days,] HH:MM:SS'"""
years = timedelta.days // 365
days = timedelta.days - (years*365)
days = timedelta.days - (years * 365)
hours = timedelta.seconds // 3600
minutes = (timedelta.seconds-(hours*3600)) // 60
seconds = timedelta.seconds - (hours*3600) - (minutes*60)
minutes = (timedelta.seconds - (hours * 3600)) // 60
seconds = timedelta.seconds - (hours * 3600) - (minutes * 60)

hours_str = "%s" % hours
if hours < 10:
Expand Down
Loading