Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4e40d66
GH-88: Initial SystemSpecs (see TODO.md)
wesleyboar Aug 4, 2021
e5fa6c4
GH-88: SystemSpecs: Integrate DataList
wesleyboar Aug 6, 2021
45f07cf
GH-88: SystemSpecs: Enable Link (Has Admin Bug)
wesleyboar Aug 6, 2021
eba88c0
GH-88: SystemSpecs: Fix Dup. Internal Link Opt
wesleyboar Aug 6, 2021
491c385
GH-88: Update submod (merge main)
wesleyboar Aug 13, 2021
3fb8a30
GH-88: SystemSpecs: Support Sysmon Image DataList
wesleyboar Aug 12, 2021
e5748a1
GH-88: SystemSpecs: Fix Bug using Generic Image
wesleyboar Aug 12, 2021
2c28f4a
GH-88: Set plugin font size and weight
wesleyboar Aug 13, 2021
5f5eb56
GH-88: Document support for app stylesheets
wesleyboar Aug 13, 2021
732b99c
GH-88: Fix incorrect var name (font-size→weight)
wesleyboar Aug 13, 2021
c2616c0
GH-88: Rename child plugin Sysmon to SystemMonitor
wesleyboar Aug 16, 2021
52b408b
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Aug 16, 2021
3b179f7
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Aug 20, 2021
a64592d
GH-88: Add missing taccsite helper and constant
wesleyboar Aug 20, 2021
0bfb57f
GH-88: Noop: Cleanup
wesleyboar Aug 23, 2021
fd5ed61
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Aug 23, 2021
b57e072
GH-88: Do not allow Bootstrap Picture
wesleyboar Aug 25, 2021
625957d
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Aug 31, 2021
3eebe59
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Sep 1, 2021
17cc231
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Sep 2, 2021
9ee9229
Merge branch 'main' into task/GH-88-system-specs
wesleyboar Sep 29, 2021
755fbbd
GH-88: Reduce verbosity of helpers.py clean_for_abstract_link
wesleyboar Sep 29, 2021
f32ac38
GH-88: Set desc limits
wesleyboar Sep 29, 2021
e8b2c74
Merge branch 'task/GH-88-system-specs' of https://github.com/TACC/Cor…
wesleyboar Sep 29, 2021
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: 10 additions & 0 deletions taccsite_cms/contrib/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
TEXT_FOR_NESTED_PLUGIN_CONTENT_ADD = '\
<dl>\
<dt>To add {element},</dt>\
<dd>nest "{plugin_name}" plugin inside this plugin.</dd>\
<dt>To edit {element},</dt>\
<dd>edit existing nested "{plugin_name}" plugin.*</dd>\
</dl>\
<br />\
* If the existing content is from a plugin not nested within this one, then you should nest it inside this one instead.'

TEXT_FOR_NESTED_PLUGIN_CONTENT_SWAP = '\
<dl>\
<dt>To add {element},</dt>\
Expand Down
36 changes: 36 additions & 0 deletions taccsite_cms/contrib/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,39 @@ def concat_classnames(classes):


# GH-93, GH-142, GH-133: Upcoming functions here (ease merge conflict, maybe)



# SEE: https://github.com/django-cms/djangocms-link/blob/3.0.0/djangocms_link/models.py#L48
def clean_for_abstract_link(model, self):
"""
Intercept and manipulate validation on `AbstractLink` so that it suits TACC's minimal subclassing of it. (To catch only parent validation errors, not custom ones, run this before any custom validation.)
Usage:
```
from taccsite_cms.contrib.helpers import clean_for_abstract_link
# Validate
def clean(self):
clean_for_abstract_link(__class__, self)
...
```
"""

# Bypass irrelevant parent validation
# SEE: ./_docs/how-to-override-validation-error-from-parent-model.md
try:
super(model, self).clean()
except ValidationError as err:
# Intercept multi-field errors
if hasattr(err, 'error_dict'):
for field, errors in err.message_dict.items():
# Reduce verbosity of original error message
# FAQ: Original error message assumes more fields exist
indices = get_indices_that_start_with(
'Only one of ', errors
)
for i in indices:
err.error_dict[field] = ValidationError(
_('Only one of External link or Internal link may be given.'), code='invalid')

if len(err.messages):
raise err
Empty file.
121 changes: 121 additions & 0 deletions taccsite_cms/contrib/taccsite_system_specs/cms_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from cms.plugin_pool import plugin_pool
from django.utils.translation import gettext_lazy as _

from djangocms_link.cms_plugins import LinkPlugin

from taccsite_cms.contrib.constants import TEXT_FOR_NESTED_PLUGIN_CONTENT_ADD
from taccsite_cms.contrib.helpers import concat_classnames

from .constants import DEFAULT_OTHER_TITLE

from .models import TaccsiteSystemSpecs

@plugin_pool.register_plugin
class TaccsiteSystemSpecsPlugin(LinkPlugin):
"""
Components > "System Specs" Plugin
"""
module = 'TACC Site'
model = TaccsiteSystemSpecs
name = _('System Specs')
render_template = 'system_specs.html'
def get_render_template(self, context, instance, placeholder):
return self.render_template

cache = True
text_enabled = False
allow_children = True
child_classes = [
'TaccsiteSystemMonitorPlugin',
'PicturePlugin',
'TaccsiteDataListPlugin',
]

fieldsets = [
(_('Specifications'), {
'fields': (
'system_desc',
'system_processor_count',
'system_processor_type',
'system_node_ram',
'system_network',
'system_performance',
'system_memory',
)
}),
(_('Footer link'), {
'classes': ('collapse',),
'description': 'The "See More Detailed Specs" link at the bottom of the list. "Display name" is for alternate link text.',
'fields': (
('external_link', 'internal_link'),
('anchor', 'target'),
'name',
)
}),
(_('Subsystems and/or resources - Introduction'), {
'fields': (
'other_title',
'other_desc',
)
}),
(_('Subsystems and/or resources - Data'), {
'classes': ('collapse',),
'description': TEXT_FOR_NESTED_PLUGIN_CONTENT_ADD.format(
element='data',
plugin_name='Data List'
),
'fields': ()
}),
(_('Image'), {
'classes': ('collapse',),
'description': TEXT_FOR_NESTED_PLUGIN_CONTENT_ADD.format(
element='an image',
plugin_name='(…) Image'
),
'fields': ()
}),
(_('System monitor'), {
'classes': ('collapse',),
'description': TEXT_FOR_NESTED_PLUGIN_CONTENT_ADD.format(
element='a system monitor',
plugin_name='(…) System Monitor'
),
'fields': ()
}),
(_('Advanced settings'), {
'classes': ('collapse',),
'fields': (
'attributes',
)
}),
]

# Render

def render(self, context, instance, placeholder):
context = super().render(context, instance, placeholder)
request = context['request']

classes = concat_classnames([
's-system-specs',
instance.attributes.get('class'),
])
instance.attributes['class'] = classes

# To identify child plugins
for plugin_instance in instance.child_plugin_instances:
if (type(plugin_instance).__name__ == 'TaccsiteSystemMonitor'):
context.update({ 'sysmon_plugin': plugin_instance })
if (type(plugin_instance).__name__ == 'Picture'):
context.update({ 'image_plugin': plugin_instance })
if (type(plugin_instance).__name__ == 'TaccsiteDataList'):
context.update({ 'data_plugin': plugin_instance })

context.update({
'default_other_title': DEFAULT_OTHER_TITLE,
'has_other': instance.other_title or instance.other_desc,
'link_url': instance.get_link(),
'link_text': instance.name,
'link_target': instance.target
})
return context
1 change: 1 addition & 0 deletions taccsite_cms/contrib/taccsite_system_specs/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DEFAULT_OTHER_TITLE = 'Subsystems and Associated Resources'
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 2.2.16 on 2021-08-06 04:17

from django.db import migrations, models
import django.db.models.deletion
import djangocms_attributes_field.fields
import djangocms_link.validators
import filer.fields.file


class Migration(migrations.Migration):

initial = True

dependencies = [
('filer', '0012_file_mime_type'),
('cms', '0022_auto_20180620_1551'),
]

operations = [
migrations.CreateModel(
name='TaccsiteSystemSpecs',
fields=[
('template', models.CharField(choices=[('default', 'Default')], default='default', max_length=255, verbose_name='Template')),
('name', models.CharField(blank=True, max_length=255, verbose_name='Display name')),
('external_link', models.CharField(blank=True, help_text='Provide a link to an external source.', max_length=2040, validators=[djangocms_link.validators.IntranetURLValidator(intranet_host_re=None)], verbose_name='External link')),
('anchor', models.CharField(blank=True, help_text='Appends the value only after the internal or external link. Do <em>not</em> include a preceding "#" symbol.', max_length=255, verbose_name='Anchor')),
('mailto', models.EmailField(blank=True, max_length=255, verbose_name='Email address')),
('phone', models.CharField(blank=True, max_length=255, verbose_name='Phone')),
('target', models.CharField(blank=True, choices=[('_blank', 'Open in new window'), ('_self', 'Open in same window'), ('_parent', 'Delegate to parent'), ('_top', 'Delegate to top')], max_length=255, verbose_name='Target')),
('attributes', djangocms_attributes_field.fields.AttributesField(blank=True, default=dict, verbose_name='Attributes')),
('cmsplugin_ptr', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='taccsite_system_specs_taccsitesystemspecs', serialize=False, to='cms.CMSPlugin')),
('system_desc', models.TextField(default='', help_text='Description of the system machine and mission.', verbose_name='System Description')),
('system_processor_count', models.IntegerField(blank=True, help_text='The number of processors in the system', null=True, verbose_name='Processors')),
('system_processor_type', models.CharField(blank=True, help_text='The number of processors in the system', max_length=50, verbose_name='Processor Type')),
('system_node_ram', models.CharField(blank=True, help_text='The amount of RAM in each node of the system, including the unit. (Reminder: Type "GB" for Gigabyte, not "Gb".)', max_length=50, verbose_name='RAM per Node')),
('system_network', models.CharField(blank=True, help_text='The network hardware in the system. (Reminder: Type "Gb" for Gigabit, not GB.)', max_length=50, verbose_name='Network')),
('system_performance', models.CharField(blank=True, help_text='The peak performance of the system, including the unit. (Reminder: Type number, then space, then unit; example: "38.8 PetaFLOPS".)', max_length=50, verbose_name='Peak Performance')),
('system_memory', models.CharField(blank=True, help_text='The amount of memory for the system, including the unit. (Reminder: Type "PB" for Petabyte, not "Pb".)', max_length=50, verbose_name='Memory')),
('other_title', models.CharField(blank=True, help_text='An alternate title to replace "Subsystems and Associated Resources".', max_length=40, verbose_name='Alternate Resources Title')),
('other_desc', models.TextField(blank=True, default='', help_text='Description of "Subsystems and Associated Resources".', verbose_name='Resources Description')),
('file_link', filer.fields.file.FilerFileField(blank=True, help_text='If provided links a file from the filer app.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='filer.File', verbose_name='File link')),
('internal_link', models.ForeignKey(blank=True, help_text='If provided, overrides the external link.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='cms.Page', verbose_name='Internal link')),
],
options={
'abstract': False,
},
bases=('cms.cmsplugin',),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.2.16 on 2021-09-29 17:51

from django.db import migrations, models


class Migration(migrations.Migration):

replaces = [('taccsite_system_specs', '0002_auto_20210929_1238'), ('taccsite_system_specs', '0003_auto_20210929_1247')]

dependencies = [
('taccsite_system_specs', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='taccsitesystemspecs',
name='system_desc',
field=models.TextField(default='', help_text='Description of the system machine and mission.', max_length=500, verbose_name='System Description'),
),
migrations.AlterField(
model_name='taccsitesystemspecs',
name='other_desc',
field=models.TextField(blank=True, default='', help_text='Description of "Subsystems and Associated Resources".', max_length=500, verbose_name='Resources Description'),
),
]
Empty file.
97 changes: 97 additions & 0 deletions taccsite_cms/contrib/taccsite_system_specs/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

from django.db import models

from djangocms_link.models import AbstractLink

from taccsite_cms.contrib.helpers import clean_for_abstract_link

from .constants import DEFAULT_OTHER_TITLE

class TaccsiteSystemSpecs(AbstractLink):
"""
Components > "System Specs" Model
"""
system_desc = models.TextField(
verbose_name=_('System Description'),
help_text=_('Description of the system machine and mission.'),
blank=False,
max_length=500,
default=''
)
system_processor_count = models.IntegerField(
verbose_name=_('Processors'),
help_text=_('The number of processors in the system'),
blank=True,
null=True,
# HELP: Is it worth it to create a min. value zero to avoid neg. values?
# SEE: https://stackoverflow.com/a/849426/11817077
# min_value=0,
)
system_processor_type = models.CharField(
verbose_name=_('Processor Type'),
help_text=_('The number of processors in the system'),
blank=True,
max_length=50,
)
system_node_ram = models.CharField(
verbose_name=_('RAM per Node'),
help_text=_('The amount of RAM in each node of the system, including the unit. (Reminder: Type "GB" for Gigabyte, not "Gb".)'),
blank=True,
max_length=50,
)
system_network = models.CharField(
verbose_name=_('Network'),
help_text=_('The network hardware in the system. (Reminder: Type "Gb" for Gigabit, not GB.)'),
blank=True,
max_length=50,
)
system_performance = models.CharField(
verbose_name=_('Peak Performance'),
help_text=_('The peak performance of the system, including the unit. (Reminder: Type number, then space, then unit; example: "38.8 PetaFLOPS".)'),
blank=True,
max_length=50,
)
system_memory = models.CharField(
verbose_name=_('Memory'),
help_text=_('The amount of memory for the system, including the unit. (Reminder: Type "PB" for Petabyte, not "Pb".)'),
blank=True,
max_length=50,
)

other_title = models.CharField(
verbose_name=_('Alternate Resources Title'),
help_text=_('An alternate title to replace "%(default_value)s".') % { 'default_value': DEFAULT_OTHER_TITLE },
blank=True,
max_length=40, # Based on approx. space available in design
)
other_desc = models.TextField(
verbose_name=_('Resources Description'),
help_text=_('Description of "%(default_value)s".') % { 'default_value': DEFAULT_OTHER_TITLE },
blank=True,
max_length=500,
default=''
)

def get_short_description(self):
return self.system_desc



# Parent

link_is_optional = True

class Meta:
abstract = False

# Validate
def clean(self):
clean_for_abstract_link(__class__, self)

# If user provided link text, then require link
if self.name and not self.get_link():
raise ValidationError(
_('Please provide a footer link or delete its display name.'), code='invalid'
)
Loading