aboutsummaryrefslogtreecommitdiffstats
path: root/src/tmview/cli.py
blob: cebb95dfa37b6fd6455389af8d80525b8dd70c66 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import argparse
import sys
from rich.console import Console

from tmview import version
from tmview import client as tm_client
from tmview import download as tm_download
from tmview import output as tm_output

console = Console(stderr=True)

# WO is already in EU_OFFICES; extras are non-EU/non-WO offices only
ALL_OFFICES_EXTRA = [
    "US", "GB", "JP", "CN", "KR", "CH", "NO", "CA", "AU", "MX",
    "IN", "BR", "RU", "TR", "UA", "MA", "TN", "IL", "ZA",
]


def main():
    parser = argparse.ArgumentParser(
        prog="tmview",
        description="Search EU trademark registries via TMview",
    )
    parser.add_argument("query", nargs="?", help="Trademark name to search for")
    parser.add_argument(
        "-o", "--offices",
        metavar="CODES",
        help="Comma-separated office codes (default: all EU + WIPO)",
    )
    parser.add_argument(
        "-l", "--limit",
        type=int,
        default=20,
        metavar="N",
        help="Max results to return (default: 20, max: 100)",
    )
    parser.add_argument(
        "-c", "--classes",
        metavar="NUMS",
        help="Nice class filter, comma-separated (e.g. 9,35)",
    )
    parser.add_argument(
        "-s", "--status",
        metavar="STATUS",
        help="Status filter: registered|pending|expired",
    )
    parser.add_argument(
        "-v", "--verbose",
        action="store_true",
        help="Show fetch timestamp column in table output",
    )
    parser.add_argument(
        "--similar-to",
        metavar="IMAGE",
        help="Find trademarks with visually similar logos to IMAGE",
    )
    parser.add_argument(
        "-d", "--download-logos",
        metavar="DIR",
        help="Download trademark logo images to DIR",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        dest="json_output",
        help="Output as JSON instead of table",
    )
    parser.add_argument(
        "--page",
        type=int,
        default=1,
        metavar="N",
        help="Page number (default: 1)",
    )
    parser.add_argument(
        "--all-offices",
        action="store_true",
        help="Include non-EU offices (US, JP, GB, etc.)",
    )
    parser.add_argument(
        "--version",
        action="version",
        version=f"tmview {version}",
    )

    args = parser.parse_args()

    if not args.query and not args.similar_to:
        parser.error("provide a search query or --similar-to IMAGE")

    limit = min(args.limit, 100)

    if args.offices:
        offices = [o.strip().upper() for o in args.offices.split(",") if o.strip()]
    elif args.all_offices:
        offices = tm_client.EU_OFFICES + ALL_OFFICES_EXTRA
    else:
        offices = tm_client.EU_OFFICES

    classes = None
    if args.classes:
        try:
            classes = [int(c.strip()) for c in args.classes.split(",") if c.strip()]
        except ValueError:
            console.print("[red]Error: --classes must be comma-separated integers[/red]")
            sys.exit(1)

    try:
        if args.similar_to:
            status_msg = f"[bold green]Searching TMview for logos similar to {args.similar_to}…[/bold green]"
            with console.status(status_msg):
                result = tm_client.search_by_image(
                    image_path=args.similar_to,
                    offices=offices,
                    limit=limit,
                    page=args.page,
                )
        else:
            with console.status(f'[bold green]Searching TMview for "{args.query}"…[/bold green]'):
                result = tm_client.search(
                    query=args.query,
                    offices=offices,
                    limit=limit,
                    page=args.page,
                    classes=classes,
                    status=args.status,
                )
    except RuntimeError as exc:
        console.print(f"[red]Error: {exc}[/red]")
        console.print("[dim]Suggestion: check your connection or retry in a few seconds.[/dim]")
        sys.exit(1)

    if result.get("from_cache"):
        console.print(f'[dim](cached — fetched {result["fetched_at"]})[/dim]')

    if args.json_output:
        tm_output.print_json(result)
    else:
        tm_output.print_table(result, args.query, verbose=args.verbose, image_path=args.similar_to)

    if args.download_logos:
        trademarks = result["trademarks"]
        if not trademarks:
            console.print("[dim]No results to download.[/dim]")
        else:
            ok, skipped = tm_download.download_logos(trademarks, args.download_logos, console)
            console.print(f"[green]Downloaded {ok} logo(s) to {args.download_logos}[/green]" +
                          (f" [yellow]({skipped} skipped)[/yellow]" if skipped else ""))