Repository: ddionrails/ddionrails

Issue:
Update models in publications app
- [x] add help_text to all fields
- [x] add type hints
- [x] change order of fields and methods in all models
- [ ] change type of Publication.url to URLField => validation
- [ ] change type of Publication.year to IntegerField?
- [ ] change type of Publication.doi to CharField => validation
- [ ] change type of Attachment.url to URLField => validation
- [ ] change type of Attachment.url_text to CharField => length



Discussion:

Resolving patch:
diff --git a/ddionrails/concepts/models.py b/ddionrails/concepts/models.py
--- a/ddionrails/concepts/models.py
+++ b/ddionrails/concepts/models.py
@@ -112,6 +112,7 @@ class Concept(models.Model, ModelMixin, ElasticMixin):
         Topic, related_name="concepts", help_text="ManyToMany relation to concepts.Topic"
     )
 
+    # Used by ElasticMixin when indexed into Elasticsearch
     DOC_TYPE = "concept"
 
     class DOR(ModelMixin.DOR):
diff --git a/ddionrails/publications/migrations/0002_auto_20190527_0934.py b/ddionrails/publications/migrations/0002_auto_20190527_0934.py
new file mode 100644
--- /dev/null
+++ b/ddionrails/publications/migrations/0002_auto_20190527_0934.py
@@ -0,0 +1,179 @@
+# Generated by Django 2.2.1 on 2019-05-27 09:34
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [("publications", "0001_initial")]
+
+    operations = [
+        migrations.AlterField(
+            model_name="attachment",
+            name="context_study",
+            field=models.ForeignKey(
+                help_text="Foreign key to studies.Study",
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="related_attachments",
+                to="studies.Study",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="dataset",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to data.Dataset",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="attachments",
+                to="data.Dataset",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="instrument",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to instruments.Instrument",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="attachments",
+                to="instruments.Instrument",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="question",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to instruments.Question",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="attachments",
+                to="instruments.Question",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="study",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to studies.Study",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="attachments",
+                to="studies.Study",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="url",
+            field=models.TextField(
+                blank=True, help_text="Link (URL) to the attachment", verbose_name="URL"
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="url_text",
+            field=models.TextField(
+                blank=True,
+                help_text="Text to be displayed for the link",
+                verbose_name="URL text",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="attachment",
+            name="variable",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to data.Variable",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="attachments",
+                to="data.Variable",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="abstract",
+            field=models.TextField(blank=True, help_text="Abstract of the publication"),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="author",
+            field=models.TextField(blank=True, help_text="Name(s) of the author(s)"),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="cite",
+            field=models.TextField(
+                blank=True, help_text="Suggested citation of the publication"
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="doi",
+            field=models.TextField(
+                blank=True,
+                help_text="DOI of the publication (DOI only, not the URL to the DOI)",
+                verbose_name="DOI",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="name",
+            field=models.CharField(
+                db_index=True, help_text="Name of the publication", max_length=255
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="studies",
+            field=models.TextField(
+                blank=True,
+                help_text="Description of studies/data sources used in the publication",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="study",
+            field=models.ForeignKey(
+                blank=True,
+                help_text="Foreign key to studies.Study",
+                null=True,
+                on_delete=django.db.models.deletion.CASCADE,
+                related_name="publications",
+                to="studies.Study",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="sub_type",
+            field=models.CharField(
+                blank=True,
+                help_text="Type of the publication (e.g., journal article or dissertation)",
+                max_length=255,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="title",
+            field=models.TextField(blank=True, help_text="Title of the publication"),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="url",
+            field=models.TextField(
+                blank=True,
+                help_text="URL referencing the publication",
+                verbose_name="URL",
+            ),
+        ),
+        migrations.AlterField(
+            model_name="publication",
+            name="year",
+            field=models.TextField(blank=True, help_text="Year of publication"),
+        ),
+    ]
diff --git a/ddionrails/publications/models.py b/ddionrails/publications/models.py
--- a/ddionrails/publications/models.py
+++ b/ddionrails/publications/models.py
@@ -1,3 +1,8 @@
+# -*- coding: utf-8 -*-
+""" Model definitions for ddionrails.publications app """
+
+from typing import Dict
+
 from django.db import models
 from django.urls import reverse
 
@@ -11,43 +16,78 @@
 
 class Publication(ElasticMixin, DorMixin, models.Model):
     """
-    Publication model
+    Stores a single publication, related to :model:`studies.Study`.
     """
 
-    name = models.CharField(max_length=255, db_index=True)
-    sub_type = models.CharField(max_length=255, blank=True)
-    title = models.TextField(blank=True)
-    author = models.TextField(blank=True)
-    year = models.TextField(blank=True)
-    abstract = models.TextField(blank=True)
-    cite = models.TextField(blank=True)
-    url = models.TextField(blank=True)
-    doi = models.TextField(blank=True)
-    studies = models.TextField(blank=True)
+    # attributes
+    name = models.CharField(
+        max_length=255, db_index=True, help_text="Name of the publication"
+    )
+    sub_type = models.CharField(
+        max_length=255,
+        blank=True,
+        help_text="Type of the publication (e.g., journal article or dissertation)",
+    )
+    title = models.TextField(blank=True, help_text="Title of the publication")
+    author = models.TextField(blank=True, help_text="Name(s) of the author(s)")
+    year = models.TextField(blank=True, help_text="Year of publication")
+    abstract = models.TextField(blank=True, help_text="Abstract of the publication")
+    cite = models.TextField(blank=True, help_text="Suggested citation of the publication")
+    url = models.TextField(
+        blank=True, verbose_name="URL", help_text="URL referencing the publication"
+    )
+    doi = models.TextField(
+        blank=True,
+        verbose_name="DOI",
+        help_text="DOI of the publication (DOI only, not the URL to the DOI)",
+    )
+    studies = models.TextField(
+        blank=True,
+        help_text="Description of studies/data sources used in the publication",
+    )
 
+    # relations
     study = models.ForeignKey(
         Study,
         blank=True,
         null=True,
         related_name="publications",
         on_delete=models.CASCADE,
+        help_text="Foreign key to studies.Study",
     )
 
+    # Used by ElasticMixin when indexed into Elasticsearch
     DOC_TYPE = "publication"
 
     class Meta:
+        """ Django's metadata options """
+
         unique_together = ("study", "name")
 
-    def html_abstract(self):
+    def __str__(self) -> str:
+        """ Returns a string representation using the "study" and "name" fields """
+        return f"{self.study}/publ/{self.name}"
+
+    def get_absolute_url(self) -> str:
+        """ Returns a canonical URL for the model using the "study" and "name" fields """
+        return reverse(
+            "publ:publication",
+            kwargs={"study_name": self.study.name, "publication_name": self.name},
+        )
+
+    def html_abstract(self) -> str:
+        """ Returns the "abstract" field as a string containing HTML markup """
         return render_markdown(self.abstract)
 
-    def html_cite(self):
+    def html_cite(self) -> str:
+        """ Returns the "cite" field as a string containing HTML markup """
         return render_markdown(self.cite)
 
-    def to_elastic_dict(self):
+    def to_elastic_dict(self) -> Dict[str, str]:
+        """ Returns a dictionary to be indexed by Elasticsearch """
         try:
             study_name = self.study.name
-        except:
+        except AttributeError:
             study_name = ""
         return dict(
             name=self.name,
@@ -63,23 +103,40 @@ def to_elastic_dict(self):
             study=study_name,
         )
 
-    def __str__(self):
-        return "%s/publ/%s" % (self.study, self.name)
 
-    def get_absolute_url(self):
-        return reverse(
-            "publ:publication",
-            kwargs={"study_name": self.study.name, "publication_name": self.name},
-        )
+class Attachment(models.Model):
+    """
+    Stores a single attachment, related to:
+    :model:`studies.Study`,
+    :model:`data.Dataset`,
+    :model:`data.Variable`,
+    :model:`instruments.Instrument` and
+    :model:`instruments.Question`
 
+    """
 
-class Attachment(models.Model):
+    # attributes
+    url = models.TextField(
+        blank=True, verbose_name="URL", help_text="Link (URL) to the attachment"
+    )
+    url_text = models.TextField(
+        blank=True, verbose_name="URL text", help_text="Text to be displayed for the link"
+    )
 
+    # relations
     context_study = models.ForeignKey(
-        Study, related_name="related_attachments", on_delete=models.CASCADE
+        Study,
+        related_name="related_attachments",
+        on_delete=models.CASCADE,
+        help_text="Foreign key to studies.Study",
     )
     study = models.ForeignKey(
-        Study, blank=True, null=True, related_name="attachments", on_delete=models.CASCADE
+        Study,
+        blank=True,
+        null=True,
+        related_name="attachments",
+        on_delete=models.CASCADE,
+        help_text="Foreign key to studies.Study",
     )
     dataset = models.ForeignKey(
         Dataset,
@@ -87,6 +144,7 @@ class Attachment(models.Model):
         null=True,
         related_name="attachments",
         on_delete=models.CASCADE,
+        help_text="Foreign key to data.Dataset",
     )
     variable = models.ForeignKey(
         Variable,
@@ -94,6 +152,7 @@ class Attachment(models.Model):
         null=True,
         related_name="attachments",
         on_delete=models.CASCADE,
+        help_text="Foreign key to data.Variable",
     )
     instrument = models.ForeignKey(
         Instrument,
@@ -101,6 +160,7 @@ class Attachment(models.Model):
         null=True,
         related_name="attachments",
         on_delete=models.CASCADE,
+        help_text="Foreign key to instruments.Instrument",
     )
     question = models.ForeignKey(
         Question,
@@ -108,6 +168,5 @@ class Attachment(models.Model):
         null=True,
         related_name="attachments",
         on_delete=models.CASCADE,
+        help_text="Foreign key to instruments.Question",
     )
-    url = models.TextField(blank=True)
-    url_text = models.TextField(blank=True)


Test patch: