diff --git a/clipping/__init__.py b/clipping/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/clipping/admin.py b/clipping/admin.py new file mode 100644 index 00000000..fafc9362 --- /dev/null +++ b/clipping/admin.py @@ -0,0 +1,56 @@ +from django import forms +from django.conf import settings +from django.contrib import admin +from django.contrib.contenttypes.models import ContentType +from django.forms.models import ModelChoiceField + +from .models import Clipping, ClippingRelation + + +class ClippingRelationAdminForm(forms.ModelForm): + contents = [] + for app_name in settings.CONTENTS: + for model_name in settings.CONTENTS[app_name]: + contents.append(model_name) + + content_type = ModelChoiceField( + ContentType.objects.filter(app_label__in=settings.CONTENTS, model__in=contents), + empty_label="--------", + label="Content", + ) + object_id = forms.CharField(widget=forms.Select(choices=[("", "---------")]), label="Element") + + class Meta: + model = ClippingRelation + fields = ["content_type", "object_id", "clipping"] + + +@admin.register(ClippingRelation) +class ClippingRelationAdmin(admin.ModelAdmin): + form = ClippingRelationAdminForm + list_display = ("get_clipping_relation", "clipping", "content_type") + + def get_clipping_relation(self, obj): + return obj.content_object.name + + get_clipping_relation.short_description = "Relation" + + +class ClippingAdminForm(forms.ModelForm): + category = forms.CharField(widget=forms.Select(choices=settings.CATEGORY_CHOICES), label="Category") + + class Meta: + model = Clipping + exclude = ["added_by"] + + +@admin.register(Clipping) +class ClippingAdmin(admin.ModelAdmin): + form = ClippingAdminForm + list_display = ("date", "title", "author", "vehicle", "category", "url", "added_by", "published") + + # Sets the current user as the adder + def save_model(self, request, obj, form, change): + if getattr(obj, "added_by", None) is None: + obj.added_by = request.user + obj.save() diff --git a/clipping/apps.py b/clipping/apps.py new file mode 100644 index 00000000..67270305 --- /dev/null +++ b/clipping/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ClippingConfig(AppConfig): + name = "clipping" diff --git a/clipping/forms.py b/clipping/forms.py new file mode 100644 index 00000000..f87ea691 --- /dev/null +++ b/clipping/forms.py @@ -0,0 +1,17 @@ +from django import forms +from django.conf import settings +from django.forms import ModelForm + +from .models import Clipping + + +class ClippingForm(ModelForm): + category = forms.CharField(widget=forms.Select(choices=settings.CATEGORY_CHOICES), label="Categoria") + date = forms.CharField(widget=forms.TextInput(attrs={"class": "datepicker"}), label="Data") + author = forms.CharField(label="Autor") + title = forms.CharField(label="Título") + + class Meta: + model = Clipping + fields = "__all__" + exclude = ["added_by", "published"] diff --git a/clipping/management/commands/import_clippings.py b/clipping/management/commands/import_clippings.py new file mode 100644 index 00000000..b651f63a --- /dev/null +++ b/clipping/management/commands/import_clippings.py @@ -0,0 +1,38 @@ +import csv +from pathlib import Path + +from django.contrib.auth import get_user_model +from django.core.management.base import BaseCommand + +from clipping.models import Clipping + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument("csv", type=Path, help="CSV file to update the clippings JSON field") + + def clean_args(self, **kwargs): + csv_file = kwargs["csv"] + if csv_file and not csv_file.exists(): + raise Exception(f"The CSV {csv_file} does not exists") + return csv_file + + def handle(self, *args, **kwargs): + csv_file = self.clean_args(**kwargs) + username = "turicas" + user = get_user_model().objects.get(username=username) + with open(csv_file, encoding="utf-8") as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + p = Clipping( + date=row["data"], + vehicle=row["veiculo"], + author=row["autor"], + title=row["titulo"], + category=row["categoria"], + url=row["link"], + published=True, + added_by=user, + ) + p.save() + self.stdout.write(self.style.SUCCESS("Successfully imported")) diff --git a/clipping/migrations/0001_initial.py b/clipping/migrations/0001_initial.py new file mode 100644 index 00000000..e9faac43 --- /dev/null +++ b/clipping/migrations/0001_initial.py @@ -0,0 +1,45 @@ +# Generated by Django 3.1.6 on 2021-04-09 13:04 + +import datetime +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Clipping', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField(default=datetime.date.today)), + ('vehicle', models.CharField(blank=True, max_length=100, null=True)), + ('author', models.CharField(blank=True, max_length=100, null=True)), + ('title', models.CharField(blank=True, max_length=200, null=True)), + ('category', models.CharField(max_length=100, null=True)), + ('url', models.URLField(unique=True)), + ('published', models.BooleanField(blank=True, default=False)), + ('added_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='ClippingRelation', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField(db_index=True)), + ('clipping', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='clipping.clipping')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ], + options={ + 'unique_together': {('content_type', 'object_id', 'clipping')}, + }, + ), + ] diff --git a/clipping/migrations/__init__.py b/clipping/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/clipping/models.py b/clipping/models.py new file mode 100644 index 00000000..f248f9b3 --- /dev/null +++ b/clipping/models.py @@ -0,0 +1,35 @@ +import datetime + +from django.contrib.auth import get_user_model +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.db import models + + +class Clipping(models.Model): + date = models.DateField(null=False, blank=False, default=datetime.date.today) + vehicle = models.CharField(max_length=100, null=True, blank=True) + author = models.CharField(max_length=100, null=True, blank=True) + title = models.CharField(max_length=200, null=True, blank=True) + category = models.CharField(max_length=100, null=True) + url = models.URLField(null=False, blank=False, unique=True) + published = models.BooleanField(default=False, blank=True) + + added_by = models.ForeignKey(get_user_model(), null=True, blank=True, on_delete=models.SET_NULL) + + def __str__(self): + return self.title + + +class ClippingRelation(models.Model): + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField(db_index=True) + content_object = GenericForeignKey() + + clipping = models.ForeignKey(Clipping, on_delete=models.CASCADE) + + def __str__(self): + return "Content: {} | Clipping: {}".format(self.content_object.name, self.clipping.title) + + class Meta: + unique_together = ["content_type", "object_id", "clipping"] diff --git a/clipping/templates/admin/clipping/change_form.html b/clipping/templates/admin/clipping/change_form.html new file mode 100644 index 00000000..6861fd20 --- /dev/null +++ b/clipping/templates/admin/clipping/change_form.html @@ -0,0 +1,43 @@ +{% extends "admin/change_form.html" %} {% block extrahead %} {{ block.super }} + + +{% endblock %} diff --git a/clipping/tests.py b/clipping/tests.py new file mode 100644 index 00000000..a39b155a --- /dev/null +++ b/clipping/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/clipping/urls.py b/clipping/urls.py new file mode 100644 index 00000000..19e5dc82 --- /dev/null +++ b/clipping/urls.py @@ -0,0 +1,8 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path("get/contentype_instances/", views.get_contenttype_instances), + path("get/selected_instance/", views.get_current_selected_instance), +] diff --git a/clipping/views.py b/clipping/views.py new file mode 100644 index 00000000..d7f1ddaa --- /dev/null +++ b/clipping/views.py @@ -0,0 +1,18 @@ +import json + +from django.contrib.contenttypes.models import ContentType +from django.http import HttpResponse + +from .models import ClippingRelation + + +def get_contenttype_instances(request): + pk = request.GET.get("id") + result = list(ContentType.objects.get(id=pk).model_class().objects.filter().values("id", "name")) + return HttpResponse(json.dumps(result), content_type="application/json") + + +def get_current_selected_instance(request): + pk = request.GET.get("id") + result = ClippingRelation.objects.get(id=pk).object_id + return HttpResponse(json.dumps(result), content_type="application/json") diff --git a/core/migrations/0030_table_short_description.py b/core/migrations/0030_table_short_description.py new file mode 100644 index 00000000..2f953c17 --- /dev/null +++ b/core/migrations/0030_table_short_description.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.8 on 2021-09-15 19:02 + +from django.db import migrations +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0029_auto_20201206_2132'), + ] + + operations = [ + migrations.AddField( + model_name='table', + name='short_description', + field=markdownx.models.MarkdownxField(blank=True, null=True), + ), + ] diff --git a/core/migrations/0031_auto_20210915_1605.py b/core/migrations/0031_auto_20210915_1605.py new file mode 100644 index 00000000..e3bd5955 --- /dev/null +++ b/core/migrations/0031_auto_20210915_1605.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.8 on 2021-09-15 19:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0030_table_short_description'), + ] + + operations = [ + migrations.RenameField( + model_name='dataset', + old_name='description', + new_name='short_description', + ), + ] diff --git a/core/migrations/0032_dataset_description.py b/core/migrations/0032_dataset_description.py new file mode 100644 index 00000000..0b339241 --- /dev/null +++ b/core/migrations/0032_dataset_description.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.8 on 2021-09-15 20:26 + +from django.db import migrations +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0031_auto_20210915_1605'), + ] + + operations = [ + migrations.AddField( + model_name='dataset', + name='description', + field=markdownx.models.MarkdownxField(blank=True, null=True), + ), + ] diff --git a/core/migrations/0033_auto_20210915_1731.py b/core/migrations/0033_auto_20210915_1731.py new file mode 100644 index 00000000..7740f83e --- /dev/null +++ b/core/migrations/0033_auto_20210915_1731.py @@ -0,0 +1,19 @@ +# Generated by Django 3.1.8 on 2021-09-15 20:31 + +from django.db import migrations +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0032_dataset_description'), + ] + + operations = [ + migrations.AlterField( + model_name='dataset', + name='short_description', + field=markdownx.models.MarkdownxField(blank=True, null=True), + ), + ] diff --git a/core/models.py b/core/models.py index d3419ffd..a4cf4e0b 100644 --- a/core/models.py +++ b/core/models.py @@ -153,7 +153,8 @@ class Dataset(models.Model): author_name = models.CharField(max_length=255, null=False, blank=False) author_url = models.URLField(max_length=2000, null=True, blank=True) code_url = models.URLField(max_length=2000, null=False, blank=False) - description = models.TextField(null=False, blank=False) + short_description = MarkdownxField(null=True, blank=True) + description = MarkdownxField(null=True, blank=True) icon = models.CharField(max_length=31, null=False, blank=False) license_name = models.CharField(max_length=255, null=False, blank=False) license_url = models.URLField(max_length=2000, null=False, blank=False) @@ -311,6 +312,7 @@ class Table(models.Model): version = models.ForeignKey(Version, on_delete=models.CASCADE, null=False, blank=False) import_date = models.DateTimeField(null=True, blank=True) description = MarkdownxField(null=True, blank=True) + short_description = MarkdownxField(null=True, blank=True) hidden = models.BooleanField(default=False) api_enabled = models.BooleanField(default=True) diff --git a/core/static/core/css/dataset-detail.css b/core/static/core/css/dataset-detail.css new file mode 100644 index 00000000..a2f863e3 --- /dev/null +++ b/core/static/core/css/dataset-detail.css @@ -0,0 +1,138 @@ +.breadcrumb { + letter-spacing: 3px; + margin-left: 21px; + padding-bottom: 12px; +} + +.breadcrumb .dropdown-trigger { + padding-right: 80px; +} + + +.breadcrumb a i.material-icons { + position: fixed; + line-height: 1; +} + +h1 { + font-size: 25px; + margin-bottom: 25px; + margin-left: 7px; +} + +.nav-content a:hover { + text-decoration: underline; +} + +.main-card { + height: 291px; + overflow-y: auto; +} + +.main-card-title { + font-size: x-large; + margin: 0 0 12px 0; +} + +.main-card-util { + margin: 12px 0 9px 0; + font-size: medium; +} + +#dataset-icon { + padding: 0 0 14px; +} + +.side-card-title { + font-size: large; + font-weight: 500; + margin: 14px 0 10px 0; +} + +.side-card-data { + font-weight: 500; + padding: 14px 0 2px 0; +} + +.side-card-data a { + font-weight: 500; +} + +.side-card-data-text { + font-weight: 400; +} + +.side-card p { + max-width: 450px; +} + +.main-card p { + max-width: 650px; +} + +.secondary-card-title { + font-size: medium; + font-weight: 500; + margin: 4px 0 12px 0; +} + +.secondary-card-content { + max-width: 900px; + margin: auto; +} + +.metadata { + margin: 4px 0; +} + +.metadata li { + padding: 5px 0; + font-weight: 600; +} + +.collection-item { + font-weight: 500; + letter-spacing: 1px; + color:#007db6 !important; +} + +.card-flex { + display: flex; +} + +.card-flex-item { + flex-grow: 1; +} + +/* Container style of BRiO */ +.container { + margin: 0 auto; + width: 90%; + max-width: 100%; +} + +/* Table detail */ + +.tab a.active { + background-color: rgba(203, 203, 255, 0.2) !important; +} + +.tabs .tab a { + background-color: rgba(225, 225, 255, 0.1) +} + +.tabs .indicator { + background-color: blue !important; +} + +@media only screen and (min-width: 301px) { + .table-detail p { + max-width: 600px; + } +} + +@media only screen and (min-width: 993px) { + .table-detail p { + max-width: 900px; + } +} \ No newline at end of file diff --git a/core/templates/core/dataset-card.html b/core/templates/core/dataset-card.html index 99537fa5..6bdf2bf7 100644 --- a/core/templates/core/dataset-card.html +++ b/core/templates/core/dataset-card.html @@ -7,7 +7,7 @@ {{ dataset.icon }}

{{ dataset.name }}

-

{{ dataset.description }}

+

{{ dataset.short_description }}

keyboard_arrow_up @@ -17,7 +17,7 @@

{{ dataset.name }}

{{ dataset.icon }} Tabelasclose - {{ dataset.name }} + {{ dataset.name }}