Compare commits

...

7 Commits

Author SHA1 Message Date
3f6facf45b Merge branch 'Kijaru/add_gtin_identifier' into develop
Some checks failed
CI / lint (ubuntu-latest, 3.9) (push) Failing after 33s
CI / build-and-test (macos-13, 3.9) (push) Has been cancelled
CI / build-and-test (macos-14, 3.9) (push) Has been cancelled
CI / build-and-test (ubuntu-22.04, 3.9) (push) Has been cancelled
CI / build-and-test (windows-latest, 3.9) (push) Has been cancelled
2025-06-08 13:24:26 -07:00
97c2242474 Fix typo in gtinvalidator 2025-05-28 08:23:16 +02:00
47df09c8c7 Add GTIN validation 2025-05-27 00:50:27 +02:00
66d1c5761e Removed GTIN from ComicRack 2025-05-27 00:50:01 +02:00
94f325a088 Fix error when parsing metadata from the CLI 2025-05-24 11:49:56 -07:00
ebd7fae059 Fix setting the issue to "1" when not searching online 2025-05-24 11:49:38 -07:00
38e7789c7b Add support for GTIN identifier 2025-05-17 11:54:59 +02:00
10 changed files with 102 additions and 6 deletions

View File

@ -203,6 +203,8 @@ class GenericMetadata:
story_arcs: list[str] = dataclasses.field(default_factory=list)
series_groups: list[str] = dataclasses.field(default_factory=list)
gtin: str | None = None # ISBN/EAN identifier
publisher: str | None = None
imprint: str | None = None
day: int | None = None
@ -340,6 +342,8 @@ class GenericMetadata:
self.story_arcs = assign_list(self.story_arcs, new_md.story_arcs)
self.series_groups = assign_list(self.series_groups, new_md.series_groups)
self.gtin = assign(self.gtin, new_md.gtin)
self.publisher = assign(self.publisher, new_md.publisher)
self.imprint = assign(self.imprint, new_md.imprint)
self.day = assign(self.day, new_md.day)
@ -509,6 +513,7 @@ class GenericMetadata:
add_string("web_links", [str(x) for x in self.web_links])
add_string("format", self.format)
add_string("manga", self.manga)
add_string("gtin", self.gtin)
add_string("price", self.price)
add_string("is_version_of", self.is_version_of)
@ -596,6 +601,7 @@ md_test: GenericMetadata = GenericMetadata(
alternate_series="Tales",
alternate_number="2",
alternate_count=7,
gtin=None,
imprint="craphound.com",
notes="Tagged with ComicTagger 1.3.2a5 using info from Comic Vine on 2022-04-16 15:52:26. [Issue ID 140529]",
web_links=[

View File

@ -29,6 +29,7 @@ class Tag:
"alternate_series",
"alternate_number",
"alternate_count",
"gtin",
"story_arcs",
"series_groups",
"publisher",

View File

@ -590,9 +590,6 @@ class CLI:
self.output(f"Processing {utils.path_to_short_str(ca.path)}...")
md, tags_read = self.create_local_metadata(ca, self.config.Runtime_Options__tags_read)
if md.issue is None or md.issue == "":
if self.config.Auto_Tag__assume_issue_one:
md.issue = "1"
matches: list[IssueResult] = []
# now, search online
@ -629,11 +626,15 @@ class CLI:
return res, match_results
else:
qt_md = self.try_quick_tag(ca, md)
query_md = md.copy()
qt_md = self.try_quick_tag(ca, query_md)
if query_md.issue is None or query_md.issue == "":
if self.config.Auto_Tag__assume_issue_one:
query_md.issue = "1"
if qt_md is None or qt_md.is_empty:
if qt_md is not None:
self.output("Failed to find match via quick tag")
ct_md, matches, res, match_results = self.normal_tag(ca, tags_read, md, match_results) # type: ignore[assignment]
ct_md, matches, res, match_results = self.normal_tag(ca, tags_read, query_md, match_results) # type: ignore[assignment]
if res is not None:
return res, match_results
else:

View File

@ -194,7 +194,7 @@ def parse_metadata_from_string(mdstr: str) -> GenericMetadata:
else:
value = t(value)
except (ValueError, TypeError):
raise argparse.ArgumentTypeError(f"Invalid syntax for tag '{key}': {value}")
raise argparse.ArgumentTypeError(f"Invalid syntax for tag {key!r}: {value!r}")
return value
md = GenericMetadata()
@ -240,6 +240,8 @@ def parse_metadata_from_string(mdstr: str) -> GenericMetadata:
else:
raise argparse.ArgumentTypeError(f"'{key}' is not a valid tag name")
md.is_empty = empty
except argparse.ArgumentTypeError as e:
raise e
except Exception as e:
logger.exception("Unable to read metadata from the commandline '%s'", mdstr)
raise Exception("Unable to read metadata from the commandline") from e

View File

@ -0,0 +1,51 @@
"""Functions for validating the GTIN field"""
#
# Copyright 2012-2014 ComicTagger Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
def is_valid_gtin(gtin: str) -> bool:
"""Check if the GTIN is valid.
Args:
gtin (str): The GTIN to validate.
Returns:
bool: True if the GTIN is valid, False otherwise.
"""
gtin = gtin.strip().replace("-", "").replace(" ", "")
if not gtin or len(gtin) not in (8, 12, 13, 14):
return False
try:
digits = [int(d) for d in gtin]
except ValueError:
return False
# Using the same algorithm since all GTIN
# codes are validated the same way.
# Also just checking mathematically and not against a GTIN database
control_number = 0
for i, digit in enumerate(digits[:-1]):
if i % 2 == 0:
control_number += digit * 1
else:
control_number += digit * 3
control_number = (10 - (control_number % 10)) % 10
return control_number == digits[-1]

View File

@ -91,6 +91,7 @@ Accepts the following variables:
{alternate_series} (string)
{alternate_number} (string)
{alternate_count} (integer)
{gtin} (string)
{imprint} (string)
{notes} (string)
{web_link} (string)

View File

@ -53,6 +53,7 @@ from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.exportwindow import ExportConflictOpts, ExportWindow
from comictaggerlib.fileselectionlist import FileSelectionList
from comictaggerlib.graphics import graphics_path
from comictaggerlib.gtinvalidator import is_valid_gtin
from comictaggerlib.issueidentifier import IssueIdentifier
from comictaggerlib.logwindow import LogWindow
from comictaggerlib.md import prepare_metadata
@ -113,6 +114,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
"alternate_series": self.leAltSeries,
"alternate_number": self.leAltIssueNum,
"alternate_count": self.leAltIssueCount,
"gtin": self.leGtin,
"imprint": self.leImprint,
"notes": self.teNotes,
"web_links": (self.leWebLink, self.btnOpenWebLink, self.btnAddWebLink, self.btnRemoveWebLink),
@ -289,6 +291,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.page_list_editor.firstFrontCoverChanged.connect(self.front_cover_changed)
self.page_list_editor.listOrderChanged.connect(self.page_list_order_changed)
self.tabWidget.currentChanged.connect(self.tab_changed)
self.leGtin.textChanged.connect(self.gtin_changed)
self.page_list_editor.set_blur(self.config[0].General__blur)
@ -850,6 +853,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
assign_text(self.teTeams, "\n".join(md.teams))
assign_text(self.teLocations, "\n".join(md.locations))
assign_text(self.leGtin, md.gtin)
self.dsbCriticalRating.setValue(md.critical_rating or 0.0)
if md.format is not None and md.format != "":
@ -985,6 +990,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
md.scan_info = utils.xlate(self.leScanInfo.text())
md.series_groups = utils.split(self.leSeriesGroup.text(), ",")
md.alternate_series = self.leAltSeries.text()
md.gtin = utils.xlate(self.leGtin.text())
md.web_links = [utils.parse_url(self.leWebLink.item(i).text()) for i in range(self.leWebLink.count())]
md.characters = set(utils.split(self.teCharacters.toPlainText(), "\n"))
md.teams = set(utils.split(self.teTeams.toPlainText(), "\n"))
@ -2240,6 +2246,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
if idx == 0:
self.splitter_moved_event(0, 0)
def gtin_changed(self) -> None:
# GTIN changed, so we check if it's valid
gtin = self.leGtin.text().strip()
is_valid = is_valid_gtin(gtin) if gtin else True
if is_valid:
self.leGtin.setStyleSheet("")
else:
self.leGtin.setStyleSheet("background-color: salmon;")
def check_latest_version_online(self) -> None:
version_checker = VersionChecker()
self.version_check_complete(version_checker.get_latest_version())

View File

@ -83,6 +83,7 @@ tr:nth-child(even) {
<tr><td>{alternate_series}</td><td>string</td></tr>
<tr><td>{alternate_number}</td><td>string</td></tr>
<tr><td>{alternate_count}</td><td>integer</td></tr>
<tr><td>{gtin}</td><td>string</td></tr>
<tr><td>{imprint}</td><td>string</td></tr>
<tr><td>{notes}</td><td>string</td></tr>
<tr><td>{web_link}</td><td>string</td></tr>

View File

@ -888,6 +888,23 @@ Source</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_34">
<property name="text">
<string>GTIN</string>
</property>
<property name="buddy">
<cstring>leGtin</cstring>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QLineEdit" name="leGtin">
<property name="acceptDrops">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -213,6 +213,7 @@ cv_md = comicapi.genericmetadata.GenericMetadata(
alternate_series=None,
alternate_number=None,
alternate_count=None,
gtin=None,
imprint=None,
notes=None,
web_links=[comicapi.genericmetadata.parse_url(cv_issue_result["results"]["site_detail_url"])],