Following on from #696 we also implement the group origin universe pages in the same way. Code reuse and my styling should be much more in keeping first time this time :). Tested lots locally with normal group, origin with group and group with universe attached.
image.png (view on web)There was also an existing bug where active_generalisations was not working on groups, the relation id was using 6 like characters, but it needs to be 5 on groups. This probably means its not used much / we don't have many bottom level groups, but good to have it working consistently with characters in any case, so all the views now use active_generalisations too to support both ways to view. As an example if you goto https://www.comics.org/group/4/, fourth from the bottom there should be a link to the bottom level group for MC2 but its not displayed, and then with the fix locally:
image.png (view on web)There was also a couple heading texts I'd got wrong on the character implementation where 'with origin' appeared before the wrong %s, those are fixed too.
I noticed a random 'external links' in the middle of the group page, i think not intentional, so removed it, but can adjust if needed:
image.png (view on web)Finally, because all the code is now there, i added the 'Groups from this universe' link to the universe page and supporting table etc:
image.png (view on web) image.png (view on web)https://github.com/GrandComicsDatabase/gcd-django/pull/699
(9 files)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
Hello @adam-knights, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly enhances the platform's data browsing capabilities by introducing dedicated overview pages for 'Group Origin Universe', building upon similar functionality previously implemented for characters. It addresses a critical bug in group relation filtering, ensuring that group generalisations are correctly identified and displayed. The changes integrate universe-specific filtering into numerous existing views and tables, providing a more consistent and detailed user experience when exploring groups within the context of their originating universes. Additionally, minor UI improvements and navigation links have been added to improve overall usability and consistency.
active_generalisations for groups incorrectly used relation_type_id=6 instead of 5, ensuring consistent and accurate filtering of group relations.UniverseGroupTable specifically designed for presenting groups within a universe context, improving data presentation.active_specifications and active_generalisations to use relation_type_id=5 for groups.active_universe_appearances to handle group generalisations and added active_universe_appearances_with_origin for specific universe filtering.UniverseGroupTable for displaying groups in a universe context.render_issue_count in creator tables to support group origin universe URLs.universe_groups.group_issues_per_universe URL.UniverseGroupTable._resolve_group_universe helper for consistent group and universe resolution.checklist_by_id and creator_name_checklist to use the new group universe resolution logic.universe_groups view to list groups within a universe.character_issues_character, character_issues_feature, and character_issues_series.group_origin_universe view for the new overview page.group_features, group_issues, group_name_issues, group_issues_feature, group_issues_series, group_series, group_creators, group_sequences, and group_covers views to incorporate group origin universe filtering and dynamic heading generation.group_origin_universe page.active_generalisations for groups was not working correctly due to an incorrect relation ID.The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in pull request comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@gemini-code-assist[bot] commented on this pull request.
This pull request introduces group origin universe pages, mirroring existing functionality for characters. It also includes several important bug fixes and refactorings, such as correcting a relation ID for group generalizations, fixing incorrect headings, and removing stray UI text. The code is cleaner and more reusable in several places. I've identified a few critical issues related to incorrect super() calls that could lead to bugs, and some potential race conditions with .get() that should be addressed. There are also a couple of minor bugs and opportunities for performance improvement. Overall, a great set of changes that significantly enhance functionality.
In apps/gcd/models/feature.py:
> @@ -400,6 +402,7 @@ class Meta:
def __init__(self, *args, **kwargs):
self.group = kwargs.pop('group')
+ self.universe_id = kwargs.pop('universe_id', None)
self.resolve_name = 'group'
super(CharacterFeatureTable, self).__init__(*args, **kwargs)
This super() call is incorrect. GroupFeatureTable inherits from CharacterFeatureTable, so the super() call should refer to GroupFeatureTable to correctly call the parent's __init__ method. The current implementation will skip CharacterFeatureTable.__init__ and call the __init__ of its parent (FeatureSearchTable), which is likely not the intended behavior and can lead to bugs. The correct Python 3 syntax would be super().__init__(*args, **kwargs).
- super(CharacterFeatureTable, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs)
> @@ -623,6 +625,7 @@ def value_appearances_count(self, record):
class GroupSeriesTable(CharacterSeriesTable):
def __init__(self, *args, **kwargs):
self.group = kwargs.pop('group')
+ self.universe_id = kwargs.pop('universe_id', None)
self.resolve_name = 'group'
super(SeriesTable, self).__init__(*args, **kwargs)
This super() call is incorrect. GroupSeriesTable inherits from CharacterSeriesTable, but this call skips CharacterSeriesTable.__init__ and its parent's __init__, directly calling a method from SeriesTable's MRO. This is a bug and can cause incorrect initialization. The correct Python 3 syntax would be super().__init__(*args, **kwargs).
- super(SeriesTable, self).__init__(*args, **kwargs) + super(CharacterSeriesTable, self).__init__(*args, **kwargs)
In apps/gcd/models/character.py:
> @@ -442,13 +442,31 @@ def active_universe_origins(self):
return Universe.objects.filter(id__in=universes)
def active_universe_appearances(self):
+ from .story import Story
+ if self.universe and self.active_generalisations().exists():
+ group = self.active_generalisations().get().from_group
The use of .get() here is risky as active_generalisations() could potentially return more than one object, which would raise a MultipleObjectsReturned exception. To make the code more robust, consider using .first() and checking if an object was returned. This will prevent unexpected server errors if the data ever allows for multiple generalizations.
- group = self.active_generalisations().get().from_group + group = self.active_generalisations().first().from_group
In apps/gcd/models/character.py:
> appearances = Story.objects.filter(
appearing_groups__group_name__group=self,
- appearing_groups__deleted=False,
+ appearing_groups__universe_id=origin_universe,
deleted=False)
This query is missing a filter for appearing_groups__deleted=False. Other similar queries in this file include this filter to exclude deleted story-group relationships. Without it, you might be including data from deleted appearances.
appearances = Story.objects.filter( appearing_groups__group_name__group=self, appearing_groups__universe_id=origin_universe, appearing_groups__deleted=False, deleted=False)
> + filter_group = group.active_generalisations().get()\ + .from_group
The use of .get() here is risky as active_generalisations() could potentially return more than one object, which would raise a MultipleObjectsReturned exception. To make the code more robust, consider using .first(). This will prevent unexpected server errors if the data ever allows for multiple generalizations.
- filter_group = group.active_generalisations().get()\ - .from_group + filter_group = group.active_generalisations().first()\ + .from_group
> + name=group_name.name, + deleted=False) + except GroupNameDetail.MultipleObjectsReturned: + # This can happen due to diacritics, so we try to find the + # exact match. + found = False + for name in filter_group.group_names\ + .filter(name=group_name.name, + deleted=False): + if name.name == group_name.name: + filter_group_name = name + found = True + break + if not found: + # more a GroupNameDetail.DoesNotExist exception here ? + raise
Re-raising the MultipleObjectsReturned exception here will result in a 500 server error, which is not user-friendly. The developer comment suggests this might not be the best behavior. Consider handling this case more gracefully by rendering an error page to the user, similar to how other errors are handled in this view (e.g., the "Name does not exist at group generalization" error).
return render(request, 'indexer/error.html', {'error_text': 'Could not find a unique name match for ' 'the group generalization.'})
> if country:
country = get_object_or_404(Country, code=country)
- stories = stories.filter(issue__series__country=country)
+ query['issue__series__country'] = country
+
+ heading = _build_universe_filter_and_heading(
+ universe_id, link_universe_id, query,
+ 'appearing_groups__',
+ 'Sequences for Group %s with origin %s',
+ 'Sequences for Group %s',
+ (group,))
+
+ stories = Story.objects.filter(**query).distinct()\
+ .select_related('issue__series__publisher')
+
heading = 'Sequences for Group %s' % (group)
The heading variable is calculated with universe context using _build_universe_filter_and_heading earlier in the function, but it's being overwritten here. This causes the universe information to be lost from the page heading. This line should be removed to display the correct heading.
In apps/gcd/models/character.py:
> @@ -813,6 +831,38 @@ def render_issue_count(self, record):
record.issue_count))
+class UniverseGroupTable(CharacterSearchTable):
+ first_appearance = tables.Column(verbose_name='First Appearance')
+
+ class Meta:
+ model = Group
+ fields = ('name', 'year_first_published', 'first_appearance')
+
+ def __init__(self, *args, **kwargs):
+ self.universe = kwargs.pop('universe')
+ kwargs['group'] = True
+ super(UniverseGroupTable, self).__init__(*args, **kwargs)
This super() call uses Python 2 syntax. For consistency with the rest of the codebase and modern Python 3 practices, it's better to use the argument-less super().__init__(*args, **kwargs).
super().__init__(*args, **kwargs)
> +
+ query = {
+ 'story__credits__creator': creator,
+ 'story__appearing_groups__group_name__group': filter_group,
+ 'story__appearing_groups__deleted': False,
+ 'story__type__id__in': CORE_TYPES
+ }
+
+ heading_addon = _build_universe_filter_and_heading(
+ universe_id, link_universe_id, query,
+ 'story__appearing_groups__',
+ 'with group %s with origin %s',
+ 'with group %s',
+ (group,))
+
+ issues = issues.filter(**query)
This query might return duplicate Issue objects if a creator worked on multiple stories within the same issue that match the query. Consider adding .distinct() to the end of the filter to ensure each issue appears only once in the results, similar to how it's handled in other parts of the code (e.g., in checklist_by_id).
- issues = issues.filter(**query) + issues = issues.filter(**query).distinct()
> + if issues: + selected_issue = issues[randint(0, issues.count()-1)]
The issues.count() method is called twice here (once implicitly in the if issues: check, and once inside randint). For a minor performance improvement, you could store the count in a variable.
issues_count = issues.count() if issues_count: selected_issue = issues[randint(0, issues_count - 1)]
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
In apps/gcd/models/feature.py:
> @@ -400,6 +402,7 @@ class Meta:
def __init__(self, *args, **kwargs):
self.group = kwargs.pop('group')
+ self.universe_id = kwargs.pop('universe_id', None)
self.resolve_name = 'group'
super(CharacterFeatureTable, self).__init__(*args, **kwargs)
This is existing code and i believe intended
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
In apps/gcd/models/character.py:
> @@ -813,6 +831,38 @@ def render_issue_count(self, record):
record.issue_count))
+class UniverseGroupTable(CharacterSearchTable):
+ first_appearance = tables.Column(verbose_name='First Appearance')
+
+ class Meta:
+ model = Group
+ fields = ('name', 'year_first_published', 'first_appearance')
+
+ def __init__(self, *args, **kwargs):
+ self.universe = kwargs.pop('universe')
+ kwargs['group'] = True
+ super(UniverseGroupTable, self).__init__(*args, **kwargs)
This matches the existing UniverseCharacterTable for consistency.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> @@ -442,13 +442,31 @@ def active_universe_origins(self):
return Universe.objects.filter(id__in=universes)
def active_universe_appearances(self):
+ from .story import Story
+ if self.universe and self.active_generalisations().exists():
+ group = self.active_generalisations().get().from_group
This matches the character version that already exists.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
> @@ -623,6 +625,7 @@ def value_appearances_count(self, record):
class GroupSeriesTable(CharacterSeriesTable):
def __init__(self, *args, **kwargs):
self.group = kwargs.pop('group')
+ self.universe_id = kwargs.pop('universe_id', None)
self.resolve_name = 'group'
super(SeriesTable, self).__init__(*args, **kwargs)
This is existing code and i believe intended
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
> + filter_group = group.active_generalisations().get()\ + .from_group
This matches characters.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> + name=group_name.name, + deleted=False) + except GroupNameDetail.MultipleObjectsReturned: + # This can happen due to diacritics, so we try to find the + # exact match. + found = False + for name in filter_group.group_names\ + .filter(name=group_name.name, + deleted=False): + if name.name == group_name.name: + filter_group_name = name + found = True + break + if not found: + # more a GroupNameDetail.DoesNotExist exception here ? + raise
This code now matches the character equivalent, we would change together if we want to.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
In apps/gcd/models/character.py:
> appearances = Story.objects.filter(
appearing_groups__group_name__group=self,
- appearing_groups__deleted=False,
+ appearing_groups__universe_id=origin_universe,
deleted=False)
Fixed in 7497f30
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
> +
+ query = {
+ 'story__credits__creator': creator,
+ 'story__appearing_groups__group_name__group': filter_group,
+ 'story__appearing_groups__deleted': False,
+ 'story__type__id__in': CORE_TYPES
+ }
+
+ heading_addon = _build_universe_filter_and_heading(
+ universe_id, link_universe_id, query,
+ 'story__appearing_groups__',
+ 'with group %s with origin %s',
+ 'with group %s',
+ (group,))
+
+ issues = issues.filter(**query)
Fixed in 7497f30
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> + if issues: + selected_issue = issues[randint(0, issues.count()-1)]
Fixed in 7497f30 - also did the same to the origin character equivalent from last time
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
> if country:
country = get_object_or_404(Country, code=country)
- stories = stories.filter(issue__series__country=country)
+ query['issue__series__country'] = country
+
+ heading = _build_universe_filter_and_heading(
+ universe_id, link_universe_id, query,
+ 'appearing_groups__',
+ 'Sequences for Group %s with origin %s',
+ 'Sequences for Group %s',
+ (group,))
+
+ stories = Story.objects.filter(**query).distinct()\
+ .select_related('issue__series__publisher')
+
heading = 'Sequences for Group %s' % (group)
Fixed in 7497f30
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@jochengcd commented on this pull request.
> + if issues: + selected_issue = issues[randint(0, issues.count()-1)]
I think issues,count() is cached, so
if issues.count():
would be fine as well
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@jochengcd commented on this pull request.
In apps/gcd/models/character.py:
> @@ -442,13 +442,31 @@ def active_universe_origins(self):
return Universe.objects.filter(id__in=universes)
def active_universe_appearances(self):
+ from .story import Story
+ if self.universe and self.active_generalisations().exists():
+ group = self.active_generalisations().get().from_group
We want server errors if the data is wrong.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
> + if issues: + selected_issue = issues[randint(0, issues.count()-1)]
Shall i leave it now i've done it or would you prefer i changed this back?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
In apps/gcd/models/character.py:
> @@ -442,13 +442,31 @@ def active_universe_origins(self):
return Universe.objects.filter(id__in=universes)
def active_universe_appearances(self):
+ from .story import Story
+ if self.universe and self.active_generalisations().exists():
+ group = self.active_generalisations().get().from_group
Excellent, will leave as is.
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@jochengcd commented on this pull request.
In apps/gcd/models/character.py:
> appearing_groups__deleted=False,
deleted=False)
- universes = set(appearances.values_list('universe',
- flat=True).distinct())
+ universes = appearances.values_list('universe',
set should stay, while the distinct reduces the elements, it is not distinct in the universe due to ForeignKey and stuff.
to see, try
cnt = 0
for i in universes:
print(i, Universe.objects.get(id=i))
cnt += 1
print(cnt)
with/without distinct and set
This also needs to be redone in the earlier character PR
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@adam-knights commented on this pull request.
In apps/gcd/models/character.py:
> appearing_groups__deleted=False,
deleted=False)
- universes = set(appearances.values_list('universe',
- flat=True).distinct())
+ universes = appearances.values_list('universe',
Fixed in 1d51c2d
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()