internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: davanstrien/flyswot

Issue:
catch no files found before running prediction function



Discussion:

Resolving patch:
diff --git a/src/flyswot/inference.py b/src/flyswot/inference.py
--- a/src/flyswot/inference.py
+++ b/src/flyswot/inference.py
@@ -107,6 +107,11 @@ def predict_directory(
     vocab = models.load_vocab(model_parts.vocab)
     onnxinference = OnnxInferenceSession(model, vocab)
     files = list(core.get_image_files_from_pattern(directory, pattern, image_format))
+    if not files:
+        typer.echo(
+            f"Didn't find any files maching {pattern} in {directory}, please check the inputs to flyswot"
+        )
+        raise typer.Exit(code=1)
     typer.echo(f"Found {len(files)} files matching {pattern} in {directory}")
     csv_fname = create_csv_fname(csv_save_dir)
     create_csv_header(csv_fname)


Test patch:
diff --git a/tests/test_inference.py b/tests/test_inference.py
--- a/tests/test_inference.py
+++ b/tests/test_inference.py
@@ -4,6 +4,7 @@
 from typing import Any
 
 import pytest
+import typer
 from hypothesis import given
 from hypothesis import strategies as st
 from hypothesis.core import given
@@ -84,3 +85,10 @@ def test_predict_directory(datafiles, tmp_path) -> None:
             assert row["predicted_label"]
             assert row["confidence"]
             assert type(float(row["confidence"])) == float
+
+
+def test_predict_empty_directory(tmp_path) -> None:
+    test_dir = tmp_path / "test"
+    test_dir.mkdir()
+    with pytest.raises(typer.Exit):
+        inference.predict_directory(test_dir)
Repository: davanstrien/flyswot

Issue:
Convert documentation to myst



Discussion:
https://docs.readthedocs.io/en/stable/guides/migrate-rest-myst.html

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,15 +1,12 @@
 """Sphinx configuration."""
-from datetime import datetime
-
-
 project = "flyswot"
 author = "Daniel van Strien"
-copyright = f"{datetime.now().year}, {author}"
+copyright = "2021, Daniel van Strien"
 extensions = [
     "sphinx.ext.autodoc",
     "sphinx.ext.napoleon",
     "sphinx_click",
-    "sphinx_rtd_theme",
+    "myst_parser",
 ]
 autodoc_typehints = "description"
-html_theme = "sphinx_rtd_theme"
+html_theme = "furo"
diff --git a/noxfile.py b/noxfile.py
--- a/noxfile.py
+++ b/noxfile.py
@@ -1,4 +1,5 @@
 """Nox sessions."""
+import os
 import shutil
 import sys
 from pathlib import Path
@@ -168,12 +169,17 @@ def xdoctest(session: Session) -> None:
     session.run("python", "-m", "xdoctest", package, *args)
 
 
-@session(name="docs-build", python="3.8")
+@session(name="docs-build", python=python_versions[1])
 def docs_build(session: Session) -> None:
     """Build the documentation."""
-    args = session.posargs or ["docs", "docs/_build"]
     session.install(".")
-    session.install("sphinx", "sphinx-click", "sphinx-rtd-theme")
+    session.install("cogapp")
+    args = ["-r", "README.md"]
+    session.run("cog", *args)
+    args = session.posargs or ["docs", "docs/_build"]
+    if not session.posargs and "FORCE_COLOR" in os.environ:
+        args.insert(0, "--color")
+    session.install("sphinx", "sphinx-click", "furo", "myst-parser")
 
     build_dir = Path("docs", "_build")
     if build_dir.exists():
@@ -182,12 +188,12 @@ def docs_build(session: Session) -> None:
     session.run("sphinx-build", *args)
 
 
-@session(python="3.8")
+@session(python=python_versions[0])
 def docs(session: Session) -> None:
     """Build and serve the documentation with live reloading on file changes."""
     args = session.posargs or ["--open-browser", "docs", "docs/_build"]
     session.install(".")
-    session.install("sphinx", "sphinx-autobuild", "sphinx-click", "sphinx-rtd-theme")
+    session.install("sphinx", "sphinx-autobuild", "sphinx-click", "furo", "myst-parser")
 
     build_dir = Path("docs", "_build")
     if build_dir.exists():


Test patch:
Repository: davanstrien/flyswot

Issue:
remove unused dependenices



Discussion:

Resolving patch:
diff --git a/src/flyswot/models.py b/src/flyswot/models.py
--- a/src/flyswot/models.py
+++ b/src/flyswot/models.py
@@ -9,7 +9,6 @@
 
 import requests
 import typer
-import validators  # type: ignore
 from huggingface_hub import hf_hub_url
 from huggingface_hub import snapshot_download
 from huggingface_hub.hf_api import ModelInfo
@@ -49,14 +48,14 @@ def _get_model_parts(self, hf_cache: Path):
                 self.model = file
 
 
-def _url_callback(url: str) -> Union[str, None]:
-    """Checks url is valid"""
-    if url == "latest":
-        return url
-    if validators.url(url):
-        return url
-    else:
-        raise typer.BadParameter(f"Please check {url} is a valid url")
+# def _url_callback(url: str) -> Union[str, None]:
+#     """Checks url is valid"""
+#     if url == "latest":
+#         return url
+#     if validators.url(url):
+#         return url
+#     else:
+#         raise typer.BadParameter(f"Please check {url} is a valid url")
 
 
 def ensure_model_dir(model_dir_path: Union[Path, None] = None) -> Path:
@@ -85,7 +84,7 @@ def show_model_dir() -> None:
 
 @app.command(name="download")
 def get_model(
-    revision: Optional[str] = typer.Argument(None, callback=_url_callback),
+    revision: Optional[str] = typer.Argument(None),
     model_dir: Path = typer.Argument(
         None,
         envvar="MODEL_DIR",


Test patch:
diff --git a/tests/test_models.py b/tests/test_models.py
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -31,16 +31,6 @@ def test_app_exists() -> None:
     assert models.app
 
 
-def test_url_callback() -> None:
-    url_latest = "latest"
-    assert models._url_callback(url_latest) == url_latest
-    valid_url = "https://google.com"
-    assert models._url_callback(valid_url) == valid_url
-    with pytest.raises(typer.BadParameter):
-        bad_url = "nope"
-        models._url_callback(bad_url)
-
-
 def test_ensure_model_dir_returns_path(tmp_path: Any) -> None:
     """It returns paths"""
     model_dir_path = tmp_path / "app"
Repository: DDMAL/CantusDB

Issue:
Hide items-count page
The items-count page (content statistics) should only be visible to logged-in contributors. The corresponding view should be given a `loginrequired` tag. 


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py
--- a/django/cantusdb_project/main_app/views/views.py
+++ b/django/cantusdb_project/main_app/views/views.py
@@ -12,8 +12,9 @@
 from django.core.mail import send_mail, get_connection
 from django.contrib.auth.views import LogoutView
 from django.contrib import messages
+from django.contrib.auth.decorators import login_required
 
-
+@login_required
 def items_count(request):
     """
     Function-based view for the ``items count`` page, accessed with ``content-statistics``


Test patch:
Repository: DDMAL/CantusDB

Issue:
the source create form has no required fields
fix this


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py
--- a/django/cantusdb_project/main_app/forms.py
+++ b/django/cantusdb_project/main_app/forms.py
@@ -187,22 +187,22 @@ class Meta:
     )
 
     full_source = forms.ChoiceField(
-        choices=TRUE_FALSE_CHOICES_SOURCE,
+        choices=TRUE_FALSE_CHOICES_SOURCE, required=False
     )
     full_source.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
     century = forms.ModelMultipleChoiceField(
-        queryset=Century.objects.all().order_by("name"), required=False
+        queryset=Century.objects.all().order_by("name")
     )
     century.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
     current_editors = forms.ModelMultipleChoiceField(
-        queryset=get_user_model().objects.all().order_by("last_name"), required=False
+        queryset=get_user_model().objects.all().order_by("last_name")
     )
     current_editors.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
     melodies_entered_by = forms.ModelMultipleChoiceField(
-        queryset=Indexer.objects.all().order_by("family_name"), required=False
+        queryset=Indexer.objects.all().order_by("family_name")
     )
     melodies_entered_by.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
@@ -212,7 +212,7 @@ class Meta:
     )
 
     complete_inventory = forms.ChoiceField(
-        choices=TRUE_FALSE_CHOICES_INVEN,
+        choices=TRUE_FALSE_CHOICES_INVEN, required=False
     )
     complete_inventory.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 


Test patch:
Repository: DDMAL/CantusDB

Issue:
Source create page layout could be improved
At `source-create/`, the form is not taking up all the horizontal space. This should be an easy fix by changing the column width in Bootstrap. 
It would be better if we keep the same layout as the old Cantus. For example, the first three fields should be on the same row. This will make the form look more compact. 
Plus, the width of the fields should be adjusted according to the expected content. For example, the `summary` field should be wider than "date". 
The look is not the most important thing, but in this case, a little bit more polishing can go a long way. 


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/models/source.py b/django/cantusdb_project/main_app/models/source.py
--- a/django/cantusdb_project/main_app/models/source.py
+++ b/django/cantusdb_project/main_app/models/source.py
@@ -28,7 +28,12 @@ class Source(BaseModel):
     )
     # the siglum field as implemented on the old Cantus is composed of both the RISM siglum and the shelfmark
     # it is a human-readable ID for a source
-    siglum = models.CharField(max_length=63, null=True, blank=True)
+    siglum = models.CharField(
+        max_length=63, 
+        null=True, 
+        blank=True,
+        help_text="RISM-style siglum + Shelf-mark (e.g. GB-Ob 202).",
+    )
     # the RISM siglum uniquely identifies a library or holding institution
     rism_siglum = models.ForeignKey(
         "RismSiglum", on_delete=models.PROTECT, null=True, blank=True,
@@ -61,6 +66,9 @@ class Source(BaseModel):
     )
     # TODO: Fill this field up with JSON info when I have access to the Users
     current_editors = models.ManyToManyField(get_user_model(), related_name="sources_edited")
+    # created_by = models.ForeignKey(
+    #     get_user_model(), related_name="sources_created", on_delete=models.PROTECT, blank=True, null=True
+    # )
     inventoried_by = models.ManyToManyField(
         "Indexer", related_name="sources_inventoried"
     )
@@ -81,7 +89,11 @@ class Source(BaseModel):
     liturgical_occasions = models.TextField(blank=True, null=True)
     description = models.TextField(blank=True, null=True)
     selected_bibliography = models.TextField(blank=True, null=True)
-    image_link = models.URLField(blank=True, null=True)
+    image_link = models.URLField(
+        blank=True, 
+        null=True,
+        help_text='HTTP link to the image gallery of the source.',
+    )
     indexing_notes = models.TextField(blank=True, null=True)
     indexing_date = models.TextField(blank=True, null=True)
     json_info = models.JSONField(blank=True, null=True)


Test patch:
Repository: DDMAL/CantusDB

Issue:
chant-detail page, list of chants on the right side of the page
Currently, for a particular folio, we first group chants by feast, then sort each group by sequence number.
The desired implementation (that in old CantusDB) would be to first sort the chants by sequence number, then group chants by feast.


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/views/chant.py b/django/cantusdb_project/main_app/views/chant.py
--- a/django/cantusdb_project/main_app/views/chant.py
+++ b/django/cantusdb_project/main_app/views/chant.py
@@ -103,27 +103,29 @@ def get_context_data(self, **kwargs):
         ).prefetch_related("feast")
 
         def get_chants_with_feasts(chants_in_folio):
-            feast_ids = []
+            # this will be a nested list of the following format:
+            # [
+            #   [feast_id_1, [chant, chant, ...]], 
+            #   [feast_id_2, [chant, chant, ...]], 
+            #   ...
+            # ]
+            feasts_chants = []
             for chant in chants_in_folio.order_by("sequence_number"):
-                if chant.feast:
-                    feast_ids.append(chant.feast.id)
-            # remove duplicate feast ids and preserve the order
-            feast_ids = list(dict.fromkeys(feast_ids))
-
-            feasts = []
-            for feast_id in feast_ids:
-                feasts.append(Feast.objects.get(id=feast_id))
-            # feasts = Feast.objects.filter(id__in=feast_ids) # this loses the order
-            chants_in_feast = []
-            for feast in feasts:
-                chants = chants_in_folio.filter(feast=feast).order_by("sequence_number")
-                chants_in_feast.append(chants)
-            feasts_zip = zip(feasts, chants_in_feast)
-            return feasts_zip
-
-        context["feasts_current_folio"] = list(
-            get_chants_with_feasts(chants_current_folio)
-        )
+                # if feasts_chants is empty, or if your current chant in the for loop 
+                # has a different feast.id than the last chant,
+                # append a new list with your current chant's feast.id
+                if chant.feast and (not feasts_chants or chant.feast.id != feasts_chants[-1][0]):
+                    feasts_chants.append([chant.feast.id, []])
+                # add the chant
+                feasts_chants[-1][1].append(chant)
+
+            # go through feasts_chants and replace feast_id with the corresponding Feast object
+            for feast_chants in feasts_chants:
+                feast_chants[0] = Feast.objects.get(id=feast_chants[0])
+
+            return feasts_chants
+
+        context["feasts_current_folio"] = get_chants_with_feasts(chants_current_folio)
 
         if context["previous_folio"]:
             chants_previous_folio = chants_in_source.filter(


Test patch:
Repository: DDMAL/CantusDB

Issue:
need to have fields of chant and sequence models synced 
or else, it'll lead to errors, such as not being able to retrieve chants from /chant-search
please fix


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/models/sequence.py b/django/cantusdb_project/main_app/models/sequence.py
--- a/django/cantusdb_project/main_app/models/sequence.py
+++ b/django/cantusdb_project/main_app/models/sequence.py
@@ -41,6 +41,7 @@ class Sequence(BaseModel):
     feast = models.ForeignKey("Feast", on_delete=models.PROTECT, null=True, blank=True)
     mode = models.CharField(max_length=63, null=True, blank=True)
     differentia = models.CharField(blank=True, null=True, max_length=63)
+    differentia_id = models.CharField(blank=True, null=True, max_length=12)
     finalis = models.CharField(blank=True, null=True, max_length=63)
     extra = models.CharField(blank=True, null=True, max_length=63)
     chant_range = models.CharField(


Test patch:
Repository: DDMAL/CantusDB

Issue:
Chant-edit page doesn't load for Admin user
The chant-edit page (e.g., http://127.0.0.1:3122/edit-volpiano/702611?pk=705019) takes forever to load for Admin user. 
I was logged in with my Admin account (i.e., superuser). Ideally, this should give me power to access and change anything. 

I also check with my project manager account and it loaded fine.


Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/users/managers.py b/django/cantusdb_project/users/managers.py
--- a/django/cantusdb_project/users/managers.py
+++ b/django/cantusdb_project/users/managers.py
@@ -2,7 +2,7 @@
 
 from django.contrib.auth.base_user import BaseUserManager
 from django.utils.translation import gettext_lazy as _
-
+from django.contrib.auth.models import Group
 
 class CustomUserManager(BaseUserManager):
     """
@@ -33,4 +33,7 @@ def create_superuser(self, email, password, **extra_fields):
             raise ValueError(_('Superuser must have is_staff=True.'))
         if extra_fields.get('is_superuser') is not True:
             raise ValueError(_('Superuser must have is_superuser=True.'))
-        return self.create_user(email, password, **extra_fields)
\ No newline at end of file
+        user = self.create_user(email, password, **extra_fields)
+        pm = Group.objects.get(name='project manager') 
+        pm.user_set.add(user)
+        return user
\ No newline at end of file


Test patch:
Repository: DDMAL/CantusDB

Issue:
Remove the "Users Online" section in footer.



Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/templatetags/helper_tags.py b/django/cantusdb_project/main_app/templatetags/helper_tags.py
--- a/django/cantusdb_project/main_app/templatetags/helper_tags.py
+++ b/django/cantusdb_project/main_app/templatetags/helper_tags.py
@@ -44,3 +44,7 @@ def source_links():
         options += option_str
 
     return mark_safe(options)
+
+@register.filter(name='has_group') 
+def has_group(user, group_name):
+    return user.groups.filter(name=group_name).exists() 


Test patch:
Repository: DDMAL/CantusDB

Issue:
search (incipit) box
On OldCantus, the search box in the corner seems to search incipits for a string, so one gets e.g. 
![image](https://user-images.githubusercontent.com/67451875/180861177-b399e975-a5e0-4eac-8d01-fabb46bfebeb.png)


On NewCantus we seems to be searching "contains" on the whole chant, which means it takes longer and probably the chant you wanted won't show up:
![image](https://user-images.githubusercontent.com/67451875/180861731-6b505429-13e6-40d3-8d80-a7e56318fb8a.png)



Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/views/views.py b/django/cantusdb_project/main_app/views/views.py
--- a/django/cantusdb_project/main_app/views/views.py
+++ b/django/cantusdb_project/main_app/views/views.py
@@ -429,16 +429,16 @@ def ajax_search_bar(request, search_term):
     # load only the first seven chants
     CHANT_CNT = 7
 
-    if search_term.isalpha():
-        # if the search term contains only alphabet letters, search incipit
-        chants = Chant.objects.filter(incipit__icontains=search_term).order_by("id")[
-            :CHANT_CNT
-        ]
-    else:
-        # if the search term contains digits, search Cantus ID
+    if any(char.isdigit() for char in search_term):
+        # if the search term contains at least one digit, assume user is searching by Cantus ID
         chants = Chant.objects.filter(cantus_id__istartswith=search_term).order_by(
             "id"
         )[:CHANT_CNT]
+    else:
+        # if the search term does not contain any digits, assume user is searching by incipit
+        chants = Chant.objects.filter(incipit__icontains=search_term).order_by("id")[
+            :CHANT_CNT
+        ]
     returned_values = chants.values(
         "incipit",
         "genre__name",


Test patch:
Repository: DDMAL/CantusDB

Issue:
Edit-volpiano: links should open in new tabs
in the sidebar of the edit-volpiano page on NewCantus, clicking on chants' cantusIDs takes the user to the corresponding CantusIndex page in a new tab, as it should. But clicking on the incipit of the chant loads the corresponding chant-detail page in the same tab. In OldCantus, the incipit is not a link at all.

This new link is useful, so I do think we should retain it. It should, though, open in a new tab to match the behavior of the links that are next to it, and to ensure that it doesn't take proofreaders away from the folio they're working on.




Discussion:

Resolving patch:
diff --git a/django/cantusdb_project/main_app/forms.py b/django/cantusdb_project/main_app/forms.py
--- a/django/cantusdb_project/main_app/forms.py
+++ b/django/cantusdb_project/main_app/forms.py
@@ -249,6 +249,7 @@ class Meta:
             "mode",
             "finalis",
             "differentia",
+            "differentia_id",
             "extra",
             "image_link",
             "indexing_notes"
@@ -260,14 +261,13 @@ class Meta:
             "marginalia": TextInputWidget(),
             "folio": TextInputWidget(),
             "sequence": TextInputWidget(),
-            "office": TextInputWidget(),
-            "genre": TextInputWidget(),
             "position": TextInputWidget(),
             "cantus_id": TextInputWidget(),
             "melody_id": TextInputWidget(),
             "mode": TextInputWidget(),
             "finalis": TextInputWidget(),
             "differentia": TextInputWidget(),
+            "differentia_id": TextInputWidget(),
             "extra": TextInputWidget(),
             "image_link": TextInputWidget(),
             "indexing_notes": TextAreaWidget()
@@ -277,6 +277,16 @@ class Meta:
     )
     feast.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
 
+    office = forms.ModelChoiceField(
+        queryset=Office.objects.all().order_by("name"), required=False
+    )
+    office.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
+
+    genre = forms.ModelChoiceField(
+        queryset=Genre.objects.all().order_by("name"), required=False
+    )
+    genre.widget.attrs.update({"class": "form-control custom-select custom-select-sm"})
+
 class ChantProofreadForm(forms.ModelForm):
     class Meta:
         model = Chant
diff --git a/django/cantusdb_project/main_app/views/chant.py b/django/cantusdb_project/main_app/views/chant.py
--- a/django/cantusdb_project/main_app/views/chant.py
+++ b/django/cantusdb_project/main_app/views/chant.py
@@ -1177,6 +1177,10 @@ def form_valid(self, form):
         else:
             return super().form_invalid(form)
 
+    def get_success_url(self):
+        # stay on the same page after save
+        return self.request.get_full_path()
+
 class ChantProofreadView(ChantEditVolpianoView):
     template_name = "chant_proofread.html"
     model = Chant


Test patch: