diff --git a/giza/admin.py b/giza/admin.py index 1b1e40d..af3d405 100644 --- a/giza/admin.py +++ b/giza/admin.py @@ -4,7 +4,7 @@ from django.contrib.auth.admin import UserAdmin from .forms import CustomUserCreationForm, CustomUserChangeForm -from .models import CustomUser, Lesson, Topic, Collection +from .models import CustomUser, Lesson, Topic, Collection, ElasticsearchItem class CustomUserAdmin(UserAdmin): add_form = CustomUserCreationForm @@ -15,9 +15,17 @@ class CustomUserAdmin(UserAdmin): (None, {'fields': ('full_name', 'bio', 'tagline', 'picture')}), ) + +class ElasticsearchItemInline(admin.TabularInline): + model = ElasticsearchItem + class CollectionAdmin(admin.ModelAdmin): readonly_fields=('slug',) + inlines = [ + ElasticsearchItemInline, + ] + class LessonAdmin(admin.ModelAdmin): readonly_fields=('slug',) diff --git a/giza/forms.py b/giza/forms.py index 284f25a..cb28acf 100644 --- a/giza/forms.py +++ b/giza/forms.py @@ -2,7 +2,7 @@ from django.contrib.auth.forms import UserCreationForm, UserChangeForm from tinymce.widgets import TinyMCE -from .models import CustomUser +from .models import CustomUser, Collection class CustomUserCreationForm(UserCreationForm): class Meta: @@ -13,3 +13,9 @@ class CustomUserChangeForm(UserChangeForm): class Meta: model = CustomUser fields = ('full_name', 'username', 'email',) + +class CollectionForm(forms.ModelForm): + title = forms.CharField(required=False, label="Name your new collection") + class Meta: + model = Collection + fields = ('title', ) diff --git a/giza/migrations/0005_auto_20210314_0551.py b/giza/migrations/0005_auto_20210314_0551.py new file mode 100644 index 0000000..6f32866 --- /dev/null +++ b/giza/migrations/0005_auto_20210314_0551.py @@ -0,0 +1,27 @@ +# Generated by Django 2.2.12 on 2021-03-14 05:51 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('giza', '0004_lesson_summary'), + ] + + operations = [ + migrations.RemoveField( + model_name='collection', + name='items', + ), + migrations.CreateModel( + name='ElasticsearchItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(max_length=20)), + ('es_id', models.IntegerField()), + ('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='giza.Collection')), + ], + ), + ] diff --git a/giza/migrations/0006_collection_public.py b/giza/migrations/0006_collection_public.py new file mode 100644 index 0000000..65cfb0b --- /dev/null +++ b/giza/migrations/0006_collection_public.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.12 on 2021-04-15 13:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('giza', '0005_auto_20210314_0551'), + ] + + operations = [ + migrations.AddField( + model_name='collection', + name='public', + field=models.BooleanField(blank=True, default=False), + preserve_default=False, + ), + ] diff --git a/giza/models.py b/giza/models.py index 5935bb6..e4e09aa 100644 --- a/giza/models.py +++ b/giza/models.py @@ -1,6 +1,5 @@ from django.contrib.auth.models import AbstractUser from django.db import models -from django.contrib.postgres.fields import JSONField from django.utils.text import slugify from tinymce.models import HTMLField @@ -13,12 +12,6 @@ (PRIVATE, 'Private'), ] -EDITION = 'EDITION' -TRANSLATION = 'TRANSLATION' -CONTENT_TYPE_CHOICES = [ - (EDITION, 'Edition'), - (TRANSLATION, 'Translation'), -] class CustomUser(AbstractUser): @@ -79,18 +72,17 @@ def save(self, *args, **kwargs): self.slug = self._get_unique_slug() super().save(*args, **kwargs) + class Collection(models.Model): title = models.CharField(max_length=256) slug = models.SlugField(blank=True) + public = models.BooleanField(blank=True, default=False) owners = models.ManyToManyField( 'CustomUser', related_name='owners', blank=True) topics = models.ManyToManyField('Topic', related_name='collections_topics', blank=True) picture = models.ImageField( upload_to='images', blank=True) - # possibly consider json field for this in the future - items = models.TextField(blank=True) - def __str__(self): return self.title @@ -105,3 +97,12 @@ def _get_unique_slug(self): def save(self, *args, **kwargs): self.slug = self._get_unique_slug() super().save(*args, **kwargs) + + +class ElasticsearchItem(models.Model): + collection = models.ForeignKey(Collection, related_name='items', on_delete=models.CASCADE) + type = models.CharField(max_length=20) + es_id = models.IntegerField() + + def __str__(self): + return "{}-{}".format(self.es_id, self.type) diff --git a/giza/urls.py b/giza/urls.py index 2eea868..2050546 100644 --- a/giza/urls.py +++ b/giza/urls.py @@ -15,6 +15,7 @@ """ from django.conf.urls import url from django.urls import path +from django.conf.urls import include from django.contrib import admin from tms import views as tms_views from search import views as search_views @@ -34,7 +35,7 @@ url(r'^collections/user$', views.collections_user, name="collections_user"), url(r'^collections/create$', views.collections_create, name="collections_create"), url(r'^collections/(?P[\w-]+)$', views.collection, name="collection"), - url(r'^collections/(?P[\w-]+)/edit$', views.collection, name="collection_edit"), + url(r'^collections/(?P[\w-]+)/edit$', views.collections_edit, name="collection_edit"), url(r'^collections/$', views.collections, name="collections"), url(r'^mygiza/$', views.mygiza, name="mygiza"), @@ -47,6 +48,7 @@ url(r'^search-results/$', search_views.results, name='results'), # auth + # TODO for password change/reset, implement django accounts auth # path('accounts/', include('django.contrib.auth.urls')), url('sign-up/', views.sign_up, name='sign_up'), url('login/', views.user_login, name='login'), diff --git a/giza/views.py b/giza/views.py index 07b471e..bdaa2e4 100644 --- a/giza/views.py +++ b/giza/views.py @@ -17,8 +17,8 @@ from utils.elastic_backend import es, ES_INDEX from utils.views_utils import CATEGORIES, FACETS_PER_CATEGORY, FIELDS_PER_CATEGORY -from .forms import CustomUserCreationForm -from .models import Collection, Lesson +from .forms import CustomUserCreationForm, CollectionForm +from .models import Collection, Lesson, ElasticsearchItem RESULTS_SIZE = 20 @@ -245,7 +245,7 @@ def mygiza(request): return render(request, 'pages/mygiza-landing.html') def collections(request): - collections = Collection.objects.all() + collections = Collection.objects.filter(public=True) return render(request, 'pages/mygiza-allcollections.html', { 'collections': collections, @@ -266,7 +266,40 @@ def collection(request, slug): hits = [] search_term = request.GET.get('q', '') - item_ids = collection.items.split(",") + query = {} + + if collection.items.all(): + query = { + 'bool': { + "should": [], + } + } + for elasticsearch_item in collection.items.all(): + query['bool']['should'].append({ + 'bool': { + 'must': [ + { + 'term': { + "_type": elasticsearch_item.type, + } + }, + { + 'term': { + "_id": elasticsearch_item.es_id, + } + }, + ] + } + }) + else: + # pass a query that will get no values returned + query = { + 'ids': { + 'type': '_doc', + 'values': [] + } + } + categorystring = "" current_category = request.GET.get('category', '') current_subfacets = {} @@ -291,11 +324,7 @@ def collection(request, slug): body_query = { "from": results_from, "size": RESULTS_SIZE, - "query": { - "ids": { - "values": item_ids, - } - }, + "query": query, "aggregations": { "aggregation": { "terms": { @@ -323,12 +352,8 @@ def collection(request, slug): search_results = es.search(index=ES_INDEX, body={ "from": results_from, - "size": RESULTS_SIZE, - "query": { - "ids": { - "values": item_ids, - } - }, + "size": 10000, + "query": query, "aggregations": { "aggregation": { "terms": { @@ -399,11 +424,78 @@ def collection(request, slug): def collections_create(request): - return render(request, 'pages/mygiza-collection-edit.html') + # create a collection + if request.method == 'POST': + collection_form = CollectionForm(data=request.POST) + + # save user + if collection_form.is_valid(): + # create user + collection = collection_form.save() + collection.owners.add(request.user) + collection.save() + + return redirect('/collections/{}'.format(collection.slug)) + + else: + messages.error(request, "Error creating collection.") + + # show collection form + else: + collection_form = CollectionForm() -def collections_edit(request): + return render(request, 'pages/mygiza-collection-edit.html', { + 'collection_form': collection_form, + }) + +def collections_edit(request, slug): + + # create a collection + if request.method == 'POST': + collection_form = CollectionForm(data=request.POST) - return render(request, 'pages/mygiza-collection-edit.html') + # save user + if collection_form.is_valid(): + # create user + collection = collection_form.save() + collection.save() + + return redirect('/collections/{}'.format(collection.slug)) + + else: + messages.error(request, "Error creating collection.") + + # show collection form + else: + collection = get_object_or_404(Collection, slug=slug) + collection_form = CollectionForm(collection) + + # user does not own this collection, redirect to collections page + if not request.user in collection.owners.all(): + return redirect('/collections/') + + # handle adding new item id and type to collection + if request.GET.get('add_item_id') and request.GET.get('add_item_type'): + elasticsearch_item = ElasticsearchItem( + es_id=request.GET.get('add_item_id'), + type=request.GET.get('add_item_type'), + collection=collection + ) + elasticsearch_item.save() + return redirect('/collections/{}'.format(collection.slug)) + + elif request.GET.get('remove_item_id') and request.GET.get('remove_item_type'): + elasticsearch_item = ElasticsearchItem.objects.filter( + es_id=request.GET.get('remove_item_id'), + type=request.GET.get('remove_item_type'), + collection=collection + ) + elasticsearch_item.delete() + return redirect('/collections/{}'.format(collection.slug)) + + return render(request, 'pages/mygiza-collection-edit.html', { + 'collection_form': collection_form, + }) def lessons(request): lessons = Lesson.objects.all() diff --git a/templates/pages/full.html b/templates/pages/full.html index d6d1cf8..0ca3202 100644 --- a/templates/pages/full.html +++ b/templates/pages/full.html @@ -29,6 +29,18 @@

{{ object.displaytext }}

+
MyGiza:
+

+ {% if user.is_authenticated %} + + Add this to a collection + + {% else %} + + Add this to a collection + + {% endif %} +

Jump to:
@@ -829,6 +841,12 @@

+ + {% include 'partials/modals.html' %} + {% endblock %} {% block extra_js %} diff --git a/templates/pages/mygiza-allcollections.html b/templates/pages/mygiza-allcollections.html index c362687..ca5126d 100644 --- a/templates/pages/mygiza-allcollections.html +++ b/templates/pages/mygiza-allcollections.html @@ -23,7 +23,7 @@

{% if user.is_superuser %} - add + add {% elif user.is_authenticated %} add {% else %} @@ -36,16 +36,20 @@ @@ -90,7 +94,7 @@
{{collection.title}} - {{collection.items|length}} items + {{collection.items.all|length}} items
@@ -100,7 +104,7 @@
diff --git a/templates/pages/mygiza-collection-edit.html b/templates/pages/mygiza-collection-edit.html index 582272e..2c7daad 100644 --- a/templates/pages/mygiza-collection-edit.html +++ b/templates/pages/mygiza-collection-edit.html @@ -26,16 +26,11 @@

Create a new collection

Creating and editing collections is restricted to admin users currently. We hope to add this functionality in soon.

-
- -
+ {{collection_form.as_p}}

- +
diff --git a/templates/pages/mygiza-collection.html b/templates/pages/mygiza-collection.html index e3efca9..53ab04c 100644 --- a/templates/pages/mygiza-collection.html +++ b/templates/pages/mygiza-collection.html @@ -47,7 +47,7 @@ {% if user.is_superuser %} - add + add {% elif user.is_authenticated %} add {% else %} @@ -60,16 +60,20 @@ @@ -190,68 +194,6 @@
Search Options

Start a new search

- {% if search_params %} -
- {% for param in search_params %} -
{{ param.1 }}:
-
    -
  • - {{ param.2 }} -
  • -
- {% endfor %} -
- {% endif %} - - {% if total > 0 %} -
-
Category:
- -
- {% endif %} - - - {% for cat_name, cat_facets in sub_facets.items %} - {% for facet_type in cat_facets %} - {% for facet_title, facet_values in facet_type.items %} - {% if facet_values %} -
-
- {{facet_title}}: -
    - {% for f in facet_values %} - {% if f.display_text %} - {% with sf_combined=cat_name|add:'_'|add:facet_title|add:'_'|add:f.display_text %} - {% if sf_combined in subfacet_strings %} -
  • - -
  • - {% else %} -
  • - -
  • - {% endif %} - {% endwith %} - {% endif %} - {% endfor %} -
-
-
- {% endif %} - {% endfor %} - {% endfor %} - {% endfor %}
@@ -263,7 +205,11 @@

{{collection.title}}

Created by {% for owner in collection.owners.all %} - {{owner.full_name}} + {% if owner.full_name %} + {{owner.full_name}} + {% else %} + {{owner.username}} + {% endif %} {% endfor %} @@ -287,6 +233,7 @@

{{collection.title}}

{% for result in hits %}
  • + + {% if user in collection.owners.all %} + + Remove + + {% endif %} +
  • {% empty %} @@ -312,11 +266,7 @@

    {{collection.title}}

    There are no items in this collection yet.
    - {% if user.is_superuser %} - Add one. - {% elif user.is_authenticated %} - Add one. - {% endif %} + Add one.

    {% endfor %} diff --git a/templates/partials/modals.html b/templates/partials/modals.html index eb313ef..402699e 100644 --- a/templates/partials/modals.html +++ b/templates/partials/modals.html @@ -155,26 +155,33 @@

    Name of this image